A flag changed in the console hours ago still hasn't reached users of a Flutter app using firebase_remote_config; how do minimumFetchInterval and throttling explain it?
answer
- default window is 12 hours
- fetches inside the window use cache
- too-low interval hits the quota
- throttled code, throttle status
- real-time updates bypass the interval
basics
~10 sminimumFetchInterval defaults to 12 hours, so fetches inside that window reuse the cached config. Lowering it in production risks backend throttling (code throttled, lastFetchStatus throttle); onConfigUpdated delivers published changes regardless of the interval.
solid answer
~50 s`RemoteConfigSettings` has two durations, both required when you construct it. `minimumFetchInterval` is the maximum age of the cached config before it counts as stale, and it defaults to **12 hours**. Inside that window, extra `fetch()` or `fetchAndActivate()` calls do not download a new template, so a console change can take up to 12 hours to reach an app that only fetches at startup. The fix is not a near-zero interval in production. The guide reserves short intervals for development, because many devices fetching too often hit the backend quota. A throttled fetch throws a `FirebaseException` with code `throttled`, and `lastFetchStatus` becomes `RemoteConfigFetchStatus.throttle`. For fast propagation, listen to `onConfigUpdated`: real-time updates are fetched as soon as they are published, bypassing the interval. `fetchTimeout` (default 60 s; zero is treated as 60 s) only bounds how long one fetch waits.
code
dart · 21 linesimport 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/foundation.dart';
Future<void> configureRemoteConfig() async {
final rc = FirebaseRemoteConfig.instance;
await rc.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 10),
minimumFetchInterval:
kDebugMode ? const Duration(minutes: 5) : const Duration(hours: 1),
));
await rc.setDefaults(const {'new_checkout_enabled': false});
try {
await rc.fetchAndActivate();
} on FirebaseException catch (e) {
if (e.code == 'throttled') {
// Expected under load: keep the active values and try later.
}
}
print('status: ${rc.lastFetchStatus}, last fetch: ${rc.lastFetchTime}');
}go deeper
Remember the two settings: fetchTimeout limits one request, and minimumFetchInterval, 12 hours by default, limits how often the backend is actually asked.
Explain why fetches inside the interval return cached values, what throttled and RemoteConfigFetchStatus.throttle mean, and why short intervals are for development only.
Diagnose a stuck flag with lastFetchStatus, lastFetchTime and getValue().source, and fix propagation with onConfigUpdated rather than a near-zero interval.
Balance freshness against quota and startup latency across the whole install base, and decide which flags justify real-time propagation versus next-launch activation.
## Two settings, two different jobs **firebase_remote_config**, the FlutterFire plugin for Firebase Remote Config, takes its tuning through `setConfigSettings(RemoteConfigSettings(...))`. Both fields are required when you construct the settings object: | Setting | Meaning | Default | |---|---|---| | `fetchTimeout` | how long one fetch may wait for the backend | 60 s (a zero duration is replaced by 60 s) | | `minimumFetchInterval` | maximum age of the cached config before it is stale | 12 hours | Negative durations fail an assertion. They are easy to confuse: the timeout limits a single request, and the interval limits how often a request actually reaches the backend. ## Why a change can take hours to arrive With the default 12-hour interval, the guide states that configs are not fetched from the backend more than once in a 12-hour window, however many fetch calls the app makes. So: 1. A user opens the app at 09:00, and `fetchAndActivate()` downloads the template. 2. You flip `new_checkout_enabled` in the console at 10:00. 3. The user reopens the app at 11:00. The fetch is inside the window, so the cached template is used and the old value stays active. 4. Only after 21:00 does a fetch download the new value, and only then if the app fetches again. Nothing is broken: that is the configured behaviour. ## Throttling: what happens if you lower the interval - During development, the guide suggests a short interval, such as `Duration(minutes: 5)`, so a small team can iterate. - It warns that this is for **development only**. Shipping a very low interval to many devices will probably exceed the service's quota. - When that happens, `fetch()` or `fetchAndActivate()` throws a `FirebaseException` with code `throttled`, and `lastFetchStatus` reads `RemoteConfigFetchStatus.throttle`. The app keeps its active values. - `lastFetchTime` shows the last successful fetch. Before any successful fetch it is the Unix epoch. Treat `throttled` as expected, not fatal: log it, keep the active values and try later. Since firebase_remote_config 6.6.0, fetch failures are classified into codes such as `network-error`, `throttled` and `forbidden` instead of all reporting `internal`. ## The right fix for fast propagation For changes that must land quickly, such as switching off a broken checkout, **real-time Remote Config** is the supported route: - `onConfigUpdated` opens a connection to the backend. When a new template is published, the SDK fetches the changes **regardless of the minimum fetch interval**. - Your listener then calls `activate()` when it is safe to apply the change. - Keep a moderate interval, such as one hour or the default, for the startup fetch path. ## A production-ready configuration - Use a short interval only in debug builds, for example chosen with `kDebugMode`, and keep the default or an hour in release. - Keep `fetchTimeout` modest so a slow network does not hold up the first frame. Do not block `runApp` on a fetch that may take up to the full timeout. - Handle `FirebaseException` from the fetch and fall back to the active or default values. - Add an `onConfigUpdated` listener for flags that need to change within minutes. ## Diagnosing a stuck flag | Observation | Likely cause | |---|---| | `lastFetchStatus` is `success`, value old | inside the fetch interval; nothing new downloaded | | `lastFetchStatus` is `throttle` | interval too low in production | | `getValue(k).source` is `valueDefault` | app never activated a remote value for that key | | fetch throws `forbidden` | Remote Config API disabled for the project | Rollout policy, such as percentages, conditions and kill-switch design, is a release-engineering topic. The mechanics above are what the Flutter client controls.
- What is the difference between fetchTimeout and minimumFetchInterval?`fetchTimeout` bounds a single fetch request; it defaults to 60 seconds, and zero is treated as 60. `minimumFetchInterval` is the maximum age of the cached config; it defaults to 12 hours, and fetches inside the window do not download a new template. Lowering the timeout never makes changes arrive sooner.
- Does a listener on onConfigUpdated still wait for minimumFetchInterval?No. Real-time Remote Config fetches a newly published template as soon as the backend signals it, bypassing the minimum fetch interval. The listener still has to call `activate()` before getters see the new values.
saying these in an interview costs you the question
- Set minimumFetchInterval to zero in production so flags update instantly.
- Lowering fetchTimeout makes new config values arrive sooner.
- A throttled fetch wipes the active config back to defaults.
- Calling fetchAndActivate on every resume guarantees fresh values.
- minimumFetchInterval defaults to one minute.