Closes #27077: Load first run experiments synchronously.

Co-authored-by: Arturo Mejia <arturomejiamarmol@gmail.com>
Co-authored-by: Charlie Humphreys <chumphreys@mozilla.com>
Co-authored-by: jhugman <jhugman@users.noreply.github.com>
pull/543/head
Jonathan Almeida 2 years ago committed by Arturo Mejia
parent 6e99d6310b
commit 911f350642

@ -46,7 +46,7 @@ private const val EXPECTED_SUPPRESSION_COUNT = 17
* generally be replaced with a slow operation on a background thread launching onto the main thread
* when completed. However, in a very small number of cases, blocking may be impossible to avoid.
*/
private val EXPECTED_RUNBLOCKING_RANGE = 0..1 // CI has +1 counts compared to local runs: increment these together
private val EXPECTED_RUNBLOCKING_RANGE = 0..2 // CI has +1 counts compared to local runs: increment these together
/**
* The number of `ConstraintLayout`s we inflate that are children of a `RecyclerView` during this

@ -429,7 +429,6 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
// experiment on features close to startup.
// But we need viaduct (the RustHttp client) to be ready before we do.
components.analytics.experiments.apply {
initialize()
setupNimbusObserver(this)
}
}

@ -7,8 +7,8 @@ package org.mozilla.fenix.experiments
import android.content.Context
import android.net.Uri
import android.os.StrictMode
import mozilla.components.service.nimbus.NimbusApi
import mozilla.components.service.nimbus.Nimbus
import mozilla.components.service.nimbus.NimbusApi
import mozilla.components.service.nimbus.NimbusAppInfo
import mozilla.components.service.nimbus.NimbusDisabled
import mozilla.components.service.nimbus.NimbusServerSettings
@ -16,11 +16,13 @@ import mozilla.components.support.base.log.logger.Logger
import org.mozilla.experiments.nimbus.NimbusInterface
import org.mozilla.experiments.nimbus.internal.EnrolledExperiment
import org.mozilla.experiments.nimbus.internal.NimbusException
import org.mozilla.experiments.nimbus.joinOrTimeout
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.nimbus.FxNimbus
import org.mozilla.fenix.perf.runBlockingIncrement
/**
* Fenix specific observer of Nimbus events.
@ -34,6 +36,14 @@ private val observer = object : NimbusInterface.Observer {
}
}
/**
* The maximum amount of time the app launch will be blocked to load experiments from disk.
*
* This value was decided from analyzing the Focus metrics (nimbus_initial_fetch) for the ideal
* timeout. We should NOT change this value without collecting more metrics first.
*/
private const val TIME_OUT_LOADING_EXPERIMENT_FROM_DISK_MS = 200L
@Suppress("TooGenericExceptionCaught")
fun createNimbus(context: Context, url: String?): NimbusApi {
val errorReporter: ((String, Throwable) -> Unit) = reporter@{ message, e ->
@ -88,32 +98,32 @@ fun createNimbus(context: Context, url: String?): NimbusApi {
// generated code.
register(observer)
// This performs the minimal amount of work required to load branch and enrolment data
// into memory. If `getExperimentBranch` is called from another thread between here
// and the next nimbus disk write (setting `globalUserParticipation` or
// `applyPendingExperiments()`) then this has you covered.
// This call does its work on the db thread.
initialize()
val isFirstNimbusRun = context.settings().isFirstNimbusRun
// We always want `Nimbus.initialize` to happen ASAP and before any features (engine/UI)
// have been initialized. For that reason, we use runBlocking here to avoid
// inconsistency in the experiments.
// We can safely do this because Nimbus does most of it's work on background threads,
// except for loading the initial experiments from disk. For this reason, we have a
// `joinOrTimeout` to limit the blocking until TIME_OUT_LOADING_EXPERIMENT_FROM_DISK_MS.
runBlockingIncrement {
val job = initialize(
isFirstNimbusRun || url.isNullOrBlank(),
R.raw.initial_experiments,
)
// We only read from disk when loading first-run experiments. This is the only time
// that we should join and block. Otherwise, we don't want to wait.
if (isFirstNimbusRun) {
context.settings().isFirstNimbusRun = false
job.joinOrTimeout(TIME_OUT_LOADING_EXPERIMENT_FROM_DISK_MS)
}
}
if (!enabled) {
// This opts out of nimbus experiments. It involves writing to disk, so does its
// work on the db thread.
globalUserParticipation = enabled
}
if (context.settings().isFirstNimbusRun || url.isNullOrBlank()) {
setExperimentsLocally(R.raw.initial_experiments)
context.settings().isFirstNimbusRun = false
}
// We may have downloaded experiments on a previous run, so let's start using them
// now. We didn't do this earlier, so as to make getExperimentBranch and friends returns
// the same thing throughout the session. This call does its work on the db thread.
applyPendingExperiments()
// Now fetch the experiments from the server. These will be available for feature
// configuration on the next run of the app. This call launches on the fetch thread.
fetchExperiments()
}
} catch (e: Throwable) {
// Something went wrong. We'd like not to, but stability of the app is more important than

Loading…
Cancel
Save