Datafile caching and freshness

Your configuration ships as one JSON file, the datafile, served from https://cdn.avsb.cloud/{sdkKey}/datafile.json. SDKs read it, cache it, and evaluate flags from it locally. This page explains exactly how fresh that copy is and what happens when you publish a change.

The short version

Publish a change and it is live at the edge within seconds, because publishing purges the cached copy. An SDK picks it up on its next poll, or instantly if you have realtime updates on.

Response headers

A datafile read returns:

HeaderValueMeaning
Cache-Controlpublic, max-age=60, must-revalidateBrowsers and SDK HTTP caches may reuse the copy for 60 seconds, then must check back with the server before serving it again
CDN-Cache-Controlpublic, max-age=300The edge may hold the object for 5 minutes. This is a self-heal window for the rare case a purge is missed, not the normal path: a publish purges the object directly
ETagThe stored object's version, for example "a1b2c3"Send it back in If-None-Match to revalidate without downloading
X-CacheHIT, MISS, or BYPASSWhether the edge served it, fetched it, or skipped its cache

There is deliberately no Vary header. An earlier version varied on Accept-Encoding, which split one datafile into a compressed and an uncompressed cache entry; a purge could clear one and leave the other serving stale bytes. There is now exactly one cached representation per URL.

Cross-origin reads expose ETag, Cache-Tag, Cache-Control and X-Cache, so browser-side code can do conditional requests too. A datafile carries no Last-Modified header, so revalidate with the ETag.

Preview snapshots (preview/{experimentId}.json, one per experiment) are the exception: they are served no-store and never cached anywhere, so a re-preview always shows what you just built, and previewing one experiment never disturbs a preview of another.

Revalidating with ETag

Conditional requests are the cheap way to poll. Send the ETag you last received:

Shell
curl -sI https://cdn.avsb.cloud/sdk_production_xxxxxxxxxxxxxxxx/datafile.json \  -H 'If-None-Match: "a1b2c3"'
Shell2 lines

There are three possible answers, and each one is unambiguous:

  • 304 Not Modified means your copy is current. There is no body, and the response repeats the current ETag. Keep using what you have.
  • 200 OK means the datafile changed. The body is the new configuration and the ETag is the new version. This is what you get when the ETag you sent does not match what is stored, so a publish can never be missed by a poller that always sends its ETag.
  • 404 Not Found means there is no datafile for that key. Check the key: a feature-flag project shows it on the Environments page in the sidebar, and a web-experiments project shows it under Project settings, Snippet. Then confirm the environment has been published at least once.
A 304 always reflects the stored object

Earlier builds of the CDN answered any conditional request with a 304 without checking what was stored. A poller that always sent its ETag could stay on an old datafile indefinitely. The comparison is now against the stored object on every conditional read, so a 304 is a real statement about the current version.

Weak ETags (W/"a1b2c3") compare equal to their strong form, because a compressing proxy in front of you may rewrite the header. Lists ("old", "a1b2c3") and * work as HTTP specifies.

What happens when you publish

1

The new datafile is written

Saving a change that affects delivery, for example running a flag, editing a rule, or changing an audience, republishes the whole datafile for that environment and stores it.

2

The cached copy is purged

The publish then purges that exact object from the edge cache, so the next reader gets the new bytes rather than waiting out the cache lifetime.

3

Subscribed SDKs are notified

If realtime updates are on, connected SDKs receive a short "datafile updated" message and refetch immediately. The message carries no configuration, so the CDN stays the single source of truth.

4

Everyone else picks it up on the next poll

SDKs poll on an interval, 60 seconds by default in @avsbhq/browser and @avsbhq/node, and both are configurable. Between polls, evaluation continues against the copy already in memory.

The worst-case delay for an SDK without realtime updates is therefore one polling interval plus up to 5 minutes of the edge's CDN-Cache-Control max-age, if the purge somehow did not reach that edge location. In practice a publish purges and then reads the datafile back to confirm the purge worked, retrying if it did not, so most publishes reach every edge location within seconds.

Choosing how fresh you need to be

NeedSetting
Changes visible within a secondTurn on realtime updates in the SDK. It keeps one lightweight connection open and refetches when told to.
Changes visible within a minuteThe defaults already do this. No configuration needed.
Fewer network requestsRaise the polling interval, or use a bootstrap datafile and refresh on your own schedule.
No network at all at startupPass a bootstrap datafile so the SDK evaluates from the first line of code, then let polling take over.

Debugging a stale datafile

Work down this list. Each step rules out one layer.

  1. Confirm what the CDN is serving. curl -s https://cdn.avsb.cloud/{sdkKey}/datafile.json | head -c 400. Look at publishedAt. If it is old, the publish did not happen, and the answer is in the dashboard rather than in caching.
  2. Check which key you are reading. A staging key and a production key are different objects. The key is in the URL, so a wrong key shows up as a datafile that never changes no matter what you publish.
  3. Look at X-Cache. A HIT with an old publishedAt points at an edge copy that outlived its purge. Repeat the request with Cache-Control: no-cache to compare.
  4. Check your own layers. A CDN, service worker, or corporate proxy in front of your app can hold the file longer than we asked. curl from a server bypasses browser caches and tells you which side the staleness is on.
  5. Confirm the SDK refetched. Enable the SDK logger at debug level. Every fetch and every skipped fetch is logged, including the URL and the status.
Was this helpful?