Skip to content
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
4 changes: 2 additions & 2 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@
"filename": "kinto-remote-settings/README.rst",
"hashed_secret": "3650b7f537d2e64041a1e2ae269361c7480737ab",
"is_verified": false,
"line_number": 413
"line_number": 420
}
],
"kinto-remote-settings/tests/changes/config.ini": [
Expand Down Expand Up @@ -261,5 +261,5 @@
}
]
},
"generated_at": "2026-03-04T15:02:45Z"
"generated_at": "2026-03-19T18:01:04Z"
}
11 changes: 9 additions & 2 deletions kinto-remote-settings/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ Like `cache control in Kinto collections <https://kinto.readthedocs.io/en/stable

kinto.monitor.changes.record_cache_expires_seconds = 60

If cache busting query parameters then responses can be cached more agressively.
If the setting below is set then a different cache control expiration will be set:
If cache busting query parameters are present then responses can be cached more finely.

When ``_expected=0``, the minimum TTL will be used (if defined):

.. code-block:: ini

kinto.monitor.changes.record_cache_minimum_expires_seconds = 60

With real timestamp values ``_expected=1773913097658``, the maximum will be used:

.. code-block:: ini

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,28 @@ def _handle_cache_expires(request, bid, cid):
settings = request.registry.settings
prefix = f"{bid}.{cid}.record_cache"
default_expires = settings.get(f"{prefix}_expires_seconds")
minimum_expires = settings.get(f"{prefix}_minimum_expires_seconds", default_expires)
maximum_expires = settings.get(f"{prefix}_maximum_expires_seconds", default_expires)

has_cache_busting = "_expected" in request.GET
cache_expires = maximum_expires if has_cache_busting else default_expires
if cache_bust_value := request.GET.get("_expected"):
# If cache busting value is "0", or starts with "9999" (random cache busts values
# in our libraries), then we reduce the CDN cache TTL. This should help our users
# when QA'ing their changes.
#
# Important:
# - `_expected=0` is used in the Rust client when polling changes:
# https://github.com/mozilla/application-services/blob/3c2907c0d/components/remote_settings/src/client.rs#L657-L662
# - `_expected=0` is used in Desktop client on `.get()` with an empty DB
# https://searchfox.org/firefox-main/rev/fa20ce29b1a82b/services/settings/RemoteSettingsClient.sys.mjs#496
# https://searchfox.org/firefox-main/rev/fa20ce29b1a82b/services/settings/RemoteSettingsClient.sys.mjs#658-666
if cache_bust_value == "0" or cache_bust_value.startswith("9999"):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice and clean

cache_expires = minimum_expires
else:
cache_expires = maximum_expires
else:
# If cache busting is not present, we use the default expires value.
# This can only happen in the /records endpoint since _expected is mandatory in /changeset.
cache_expires = default_expires

if cache_expires is not None:
request.response.cache_expires(seconds=int(cache_expires))
Expand Down
23 changes: 20 additions & 3 deletions kinto-remote-settings/tests/changes/test_changeset.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def get_app_settings(cls, extras=None):
settings["record_cache_expires_seconds"] = "180"
settings["main.record_cache_expires_seconds"] = "360"
settings["blocklists.certificates.record_cache_expires_seconds"] = 1234
settings["blocklists.certificates.record_cache_minimum_expires_seconds"] = 42
settings["blocklists.certificates.record_cache_maximum_expires_seconds"] = 3600
return settings

def test_changeset_is_accessible(self):
Expand Down Expand Up @@ -153,9 +155,24 @@ def test_limit_is_bounded(self):
def test_extra_param_is_allowed(self):
self.app.get(self.changeset_uri + "&_extra=abc", headers=self.headers)

def test_cache_control_headers_are_set(self):
resp = self.app.get(self.changeset_uri, headers=self.headers)
assert resp.headers["Cache-Control"] == "max-age=1234"
def test_cache_control_headers_are_set_to_maximum_if_expected_is_set(self):
resp = self.app.get(
"/buckets/blocklists/collections/certificates/changeset?_expected=1773913097658",
headers=self.headers,
)
assert resp.headers["Cache-Control"] == "max-age=3600"

def test_cache_control_headers_are_set_to_minimum_if_0_or_9999(self):
resp = self.app.get(
"/buckets/blocklists/collections/certificates/changeset?_expected=0",
headers=self.headers,
)
assert resp.headers["Cache-Control"] == "max-age=42"
resp = self.app.get(
"/buckets/blocklists/collections/certificates/changeset?_expected=99999996565",
headers=self.headers,
)
assert resp.headers["Cache-Control"] == "max-age=42"

def test_cache_control_can_be_set_per_bucket(self):
self.create_collection("main", "cfr")
Expand Down
Loading