Skip to content

Add different default sampling rates for when RC fetch failed #5059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion firebase-perf/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased

* [fixed] Fixed app start trace creation where some measured time could be NULL (#4730).
* [changed] Adjusted default behavior when remote config fetch fails.


# 20.3.2
Expand Down Expand Up @@ -357,4 +358,3 @@ updates.

# 16.1.0
* [fixed] Fixed a `SecurityException` crash on certain devices that do not have Google Play Services on them.

Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ public double getTraceSamplingRate() {
// Order of precedence is:
// 1. If the value exists through Firebase Remote Config, cache and return this value.
// 2. If the value exists in device cache, return this value.
// 3. Otherwise, return default value.
// 3. If the Firebase Remote Config fetch failed, return the default "rc failure" value.
// 4. Otherwise, return default value.
TraceSamplingRate config = TraceSamplingRate.getInstance();

// 1. Reads value from Firebase Remote Config, saves this value in cache layer if valid.
Expand All @@ -306,7 +307,12 @@ public double getTraceSamplingRate() {
return deviceCacheValue.get();
}

// 3. Returns default value if there is no valid value from above approaches.
// 3. Check if RC fetch failed.
if (remoteConfigManager.isLastFetchFailed()) {
return config.getDefaultOnRcFetchFail();
}

// 4. Returns default value if there is no valid value from above approaches.
return config.getDefault();
}

Expand All @@ -315,7 +321,8 @@ public double getNetworkRequestSamplingRate() {
// Order of precedence is:
// 1. If the value exists through Firebase Remote Config, cache and return this value.
// 2. If the value exists in device cache, return this value.
// 3. Otherwise, return default value.
// 3. If the Firebase Remote Config fetch failed, return the default "rc failure" value.
// 4. Otherwise, return default value.
NetworkRequestSamplingRate config = NetworkRequestSamplingRate.getInstance();

// 1. Reads value from Firebase Remote Config, saves this value in cache layer if valid.
Expand All @@ -331,7 +338,12 @@ public double getNetworkRequestSamplingRate() {
return deviceCacheValue.get();
}

// 3. Returns default value if there is no valid value from above approaches.
// 3. Check if RC fetch failed.
if (remoteConfigManager.isLastFetchFailed()) {
return config.getDefaultOnRcFetchFail();
}

// 4. Returns default value if there is no valid value from above approaches.
return config.getDefault();
}

Expand All @@ -342,7 +354,8 @@ public double getSessionsSamplingRate() {
// and return this value.
// 2. If the value exists through Firebase Remote Config, cache and return this value.
// 3. If the value exists in device cache, return this value.
// 4. Otherwise, return default value.
// 4. If the Firebase Remote Config fetch failed, return the default "rc failure" value.
// 5. Otherwise, return default value.
SessionsSamplingRate config = SessionsSamplingRate.getInstance();

// 1. Reads value in Android Manifest (it is set by developers during build time).
Expand All @@ -368,7 +381,12 @@ public double getSessionsSamplingRate() {
return deviceCacheValue.get();
}

// 4. Returns default value if there is no valid value from above approaches.
// 4. Check if RC fetch failed.
if (remoteConfigManager.isLastFetchFailed()) {
return config.getDefaultOnRcFetchFail();
}

// 5. Returns default value if there is no valid value from above approaches.
return config.getDefault();
}

Expand All @@ -381,7 +399,8 @@ public long getSessionsCpuCaptureFrequencyForegroundMs() {
// 1. If the value exists in Android Manifest, return this value.
// 2. If the value exists through Firebase Remote Config, cache and return this value.
// 3. If the value exists in device cache, return this value.
// 4. Otherwise, return default value.
// 4. If the Firebase Remote Config fetch failed, return default failure value.
// 5. Otherwise, return default value.
SessionsCpuCaptureFrequencyForegroundMs config =
SessionsCpuCaptureFrequencyForegroundMs.getInstance();

Expand All @@ -404,7 +423,12 @@ public long getSessionsCpuCaptureFrequencyForegroundMs() {
return deviceCacheValue.get();
}

// 4. Returns default value if there is no valid value from above approaches.
// 4. Check if RC fetch failed.
if (remoteConfigManager.isLastFetchFailed()) {
return config.getDefaultOnRcFetchFail();
}

// 5. Returns default value if there is no valid value from above approaches.
return config.getDefault();
}

Expand Down Expand Up @@ -453,7 +477,8 @@ public long getSessionsMemoryCaptureFrequencyForegroundMs() {
// 1. If the value exists in Android Manifest, return this value.
// 2. If the value exists through Firebase Remote Config, cache and return this value.
// 3. If the value exists in device cache, return this value.
// 4. Otherwise, return default value.
// 4. If the Firebase Remote Config fetch failed, return default failure value.
// 5. Otherwise, return default value.
SessionsMemoryCaptureFrequencyForegroundMs config =
SessionsMemoryCaptureFrequencyForegroundMs.getInstance();

Expand All @@ -476,7 +501,12 @@ public long getSessionsMemoryCaptureFrequencyForegroundMs() {
return deviceCacheValue.get();
}

// 4. Returns default value if there is no valid value from above approaches.
// 4. Check if RC fetch failed.
if (remoteConfigManager.isLastFetchFailed()) {
return config.getDefaultOnRcFetchFail();
}

// 5. Returns default value if there is no valid value from above approaches.
return config.getDefault();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ protected Double getDefault() {
return 1.0;
}

@Override
protected Double getDefaultOnRcFetchFail() {
// Reduce the typical default by 2 orders of magnitude.
return getDefault() / 100;
}

@Override
protected String getRemoteConfigFlag() {
return "fpr_vc_trace_sampling_rate";
Expand Down Expand Up @@ -185,6 +191,12 @@ protected Double getDefault() {
return 1.0;
}

@Override
protected Double getDefaultOnRcFetchFail() {
// Reduce the typical default by 2 orders of magnitude.
return getDefault() / 100;
}

@Override
protected String getRemoteConfigFlag() {
return "fpr_vc_network_request_sampling_rate";
Expand Down Expand Up @@ -218,6 +230,11 @@ protected Long getDefault() {
return 100L;
}

@Override
protected Long getDefaultOnRcFetchFail() {
return getDefault() * 3;
}

@Override
protected String getRemoteConfigFlag() {
return "fpr_session_gauge_cpu_capture_frequency_fg_ms";
Expand Down Expand Up @@ -294,6 +311,11 @@ protected Long getDefault() {
return 100L;
}

@Override
protected Long getDefaultOnRcFetchFail() {
return getDefault() * 3;
}

@Override
protected String getRemoteConfigFlag() {
return "fpr_session_gauge_memory_capture_frequency_fg_ms";
Expand Down Expand Up @@ -553,6 +575,12 @@ protected Double getDefault() {
return 0.01;
}

@Override
protected Double getDefaultOnRcFetchFail() {
// Reduce the typical default by 2 orders of magnitude.
return getDefault() / 100;
}

@Override
protected String getDeviceCacheFlag() {
return "com.google.firebase.perf.SessionSamplingRate";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ abstract class ConfigurationFlag<T> {
/* Default value for this flag. */
protected abstract T getDefault();

/* Default value for this flag in the case that RC fetch explicitly failed. */
protected T getDefaultOnRcFetchFail() {
// Same as typical default unless overridden.
return getDefault();
}

/* Configuration flag ID for Firebase Remote Config. */
@Nullable
String getRemoteConfigFlag() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,9 @@ private FirebaseRemoteConfigValue getRemoteConfigValue(String key) {
public boolean isLastFetchFailed() {
return firebaseRemoteConfig == null
|| (firebaseRemoteConfig.getInfo().getLastFetchStatus()
== FirebaseRemoteConfig.LAST_FETCH_STATUS_FAILURE);
== FirebaseRemoteConfig.LAST_FETCH_STATUS_FAILURE)
|| (firebaseRemoteConfig.getInfo().getLastFetchStatus()
== FirebaseRemoteConfig.LAST_FETCH_STATUS_THROTTLED);
}

/**
Expand Down Expand Up @@ -344,7 +346,8 @@ private void triggerFirebaseRemoteConfigFetchAndActivateOnSuccessfulFetch() {
.addOnSuccessListener(executor, result -> syncConfigValues(firebaseRemoteConfig.getAll()))
.addOnFailureListener(
executor,
task -> {
ex -> {
logger.debug("Remote config fetch failed: %s", ex);
firebaseRemoteConfigLastFetchTimestampMs = FETCH_NEVER_HAPPENED_TIMESTAMP_MS;
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,18 @@ public void getSessionsCpuCaptureFrequencyForegroundMs_validMetadata_notSaveMeta
assertThat(testConfigResolver.getSessionsCpuCaptureFrequencyForegroundMs()).isEqualTo(100L);
}

@Test
public void
getSessionsCpuCaptureFrequencyForegroundMs_remoteConfigFetchFailed_returnDefaultRCValue() {
when(mockRemoteConfigManager.getLong(SESSIONS_CPU_CAPTURE_FREQUENCY_FG_MS_FRC_KEY))
.thenReturn(Optional.absent());
when(mockDeviceCacheManager.getLong(SESSIONS_CPU_CAPTURE_FREQUENCY_FG_MS_CACHE_KEY))
.thenReturn(Optional.absent());
when(mockRemoteConfigManager.isLastFetchFailed()).thenReturn(true);

assertThat(testConfigResolver.getSessionsCpuCaptureFrequencyForegroundMs()).isEqualTo(300L);
}

@Test
public void
getSessionsCpuCaptureFrequencyForegroundMs_invalidAndroidMetadataBundle_returnRemoteConfigValue() {
Expand Down Expand Up @@ -1051,6 +1063,18 @@ public void getSessionsMemoryCaptureFrequencyForegroundMs_validMetadata_notSaveM
assertThat(testConfigResolver.getSessionsMemoryCaptureFrequencyForegroundMs()).isEqualTo(100L);
}

@Test
public void
getSessionsMemoryCaptureFrequencyForegroundMs_remoteConfigFetchFailed_returnDefaultRCValue() {
when(mockRemoteConfigManager.getLong(SESSIONS_MEMORY_CAPTURE_FREQUENCY_FG_MS_FRC_KEY))
.thenReturn(Optional.absent());
when(mockDeviceCacheManager.getLong(SESSIONS_MEMORY_CAPTURE_FREQUENCY_FG_MS_CACHE_KEY))
.thenReturn(Optional.absent());
when(mockRemoteConfigManager.isLastFetchFailed()).thenReturn(true);

assertThat(testConfigResolver.getSessionsMemoryCaptureFrequencyForegroundMs()).isEqualTo(300L);
}

@Test
public void
getSessionsMemoryCaptureFrequencyForegroundMs_invalidAndroidMetadataBundle_returnRemoteConfigValue() {
Expand Down Expand Up @@ -1653,6 +1677,17 @@ public void getTraceSamplingRate_noRemoteConfig_returnsDefault() {
assertThat(testConfigResolver.getTraceSamplingRate()).isEqualTo(1.00);
}

@Test
public void getTraceSamplingRate_remoteConfigFetchFailed_returnsRCFailureDefault() {
when(mockRemoteConfigManager.getDouble(TRACE_SAMPLING_RATE_FRC_KEY))
.thenReturn(Optional.absent());
when(mockDeviceCacheManager.getDouble(TRACE_SAMPLING_RATE_CACHE_KEY))
.thenReturn(Optional.absent());
when(mockRemoteConfigManager.isLastFetchFailed()).thenReturn(true);

assertThat(testConfigResolver.getTraceSamplingRate()).isEqualTo(1.00 / 100);
}

@Test
public void getTraceSamplingRate_noRemoteConfigHasCache_returnsCache() {
when(mockRemoteConfigManager.getDouble(TRACE_SAMPLING_RATE_FRC_KEY))
Expand Down Expand Up @@ -1714,6 +1749,17 @@ public void getNetworkRequestSamplingRate_noRemoteConfig_returnsDefault() {
assertThat(testConfigResolver.getNetworkRequestSamplingRate()).isEqualTo(1.00);
}

@Test
public void getNetworkRequestSamplingRate_remoteConfigFetchFailed_returnsRCFailureDefault() {
when(mockRemoteConfigManager.getDouble(NETWORK_REQUEST_SAMPLING_RATE_FRC_KEY))
.thenReturn(Optional.absent());
when(mockDeviceCacheManager.getDouble(NETWORK_REQUEST_SAMPLING_RATE_CACHE_KEY))
.thenReturn(Optional.absent());
when(mockRemoteConfigManager.isLastFetchFailed()).thenReturn(true);

assertThat(testConfigResolver.getNetworkRequestSamplingRate()).isEqualTo(1.00 / 100);
}

@Test
public void getNetworkRequestSamplingRate_noRemoteConfigHasCache_returnsCache() {
when(mockRemoteConfigManager.getDouble(NETWORK_REQUEST_SAMPLING_RATE_FRC_KEY))
Expand Down Expand Up @@ -1935,6 +1981,17 @@ public void getSessionsSamplingRate_invalidRemoteConfig_returnCacheValue() {
verify(mockDeviceCacheManager, never()).setValue(any(), any());
}

@Test
public void getSessionsSamplingRate_remoteConfigFetchFailed_returnsRCFailureDefault() {
when(mockDeviceCacheManager.getDouble(SESSION_SAMPLING_RATE_CACHE_KEY))
.thenReturn(Optional.absent());
when(mockRemoteConfigManager.getDouble(SESSION_SAMPLING_RATE_FRC_KEY))
.thenReturn(Optional.absent());
when(mockRemoteConfigManager.isLastFetchFailed()).thenReturn(true);

assertThat(testConfigResolver.getSessionsSamplingRate()).isEqualTo(0.01 / 100);
}

@Test
public void getSessionsSamplingRate_validCache_returnCacheValue() {
when(mockDeviceCacheManager.getDouble(SESSION_SAMPLING_RATE_CACHE_KEY))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public void getInstance_SessionsCpuCaptureFrequencyForegroundMs_validateConstant
SessionsCpuCaptureFrequencyForegroundMs.getInstance();

assertThat(configFlag.getDefault()).isEqualTo(100L);
assertThat(configFlag.getDefaultOnRcFetchFail()).isEqualTo(300L);
assertThat(configFlag.getDeviceCacheFlag())
.isEqualTo("com.google.firebase.perf.SessionsCpuCaptureFrequencyForegroundMs");
assertThat(configFlag.getRemoteConfigFlag())
Expand All @@ -117,6 +118,7 @@ public void getInstance_SessionsMemoryCaptureFrequencyForegroundMs_validateConst
SessionsMemoryCaptureFrequencyForegroundMs.getInstance();

assertThat(configFlag.getDefault()).isEqualTo(100L);
assertThat(configFlag.getDefaultOnRcFetchFail()).isEqualTo(300L);
assertThat(configFlag.getDeviceCacheFlag())
.isEqualTo("com.google.firebase.perf.SessionsMemoryCaptureFrequencyForegroundMs");
assertThat(configFlag.getRemoteConfigFlag())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,16 @@ public void isLastFetchFailed_frcIsNonNullAndStatusFailed_returnsTrue() {
}

@Test
public void isLastFetchFailed_frcIsNonNullAndStatusOtherThanFailed_returnsFalse() {
public void isLastFetchFailed_frcIsNonNullAndStatusThrottled_returnsTrue() {
RemoteConfigManager testRemoteConfigManager =
setupTestRemoteConfigManager(createDefaultRcConfigMap());
simulateFirebaseRemoteConfigLastFetchStatus(FirebaseRemoteConfig.LAST_FETCH_STATUS_THROTTLED);

assertThat(testRemoteConfigManager.isLastFetchFailed()).isTrue();
}

@Test
public void isLastFetchFailed_frcIsNonNullAndStatusOtherThanFailedOrThrottled_returnsFalse() {
RemoteConfigManager testRemoteConfigManager =
setupTestRemoteConfigManager(createDefaultRcConfigMap());

Expand All @@ -822,9 +831,6 @@ public void isLastFetchFailed_frcIsNonNullAndStatusOtherThanFailed_returnsFalse(

simulateFirebaseRemoteConfigLastFetchStatus(FirebaseRemoteConfig.LAST_FETCH_STATUS_SUCCESS);
assertThat(testRemoteConfigManager.isLastFetchFailed()).isFalse();

simulateFirebaseRemoteConfigLastFetchStatus(FirebaseRemoteConfig.LAST_FETCH_STATUS_THROTTLED);
assertThat(testRemoteConfigManager.isLastFetchFailed()).isFalse();
}

@Test
Expand Down