Skip to content

Make module loader TTL bounded by default#1345

Open
Zetazzz wants to merge 1 commit into
mainfrom
fix/module-loader-bounded-ttl
Open

Make module loader TTL bounded by default#1345
Zetazzz wants to merge 1 commit into
mainfrom
fix/module-loader-bounded-ttl

Conversation

@Zetazzz

@Zetazzz Zetazzz commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Change createModuleLoader so cache hits do not refresh TTL by default.
  • Add an explicit updateAgeOnGet option for loaders that intentionally need sliding TTL / idle-expiry semantics.
  • Add unit coverage for bounded default TTL behavior, opt-in sliding TTL behavior, and database-scoped invalidation across databaseId / databaseId:apiId cache keys.

Why

createModuleLoader is used for runtime settings that may change while the server process is still running. If cache hits refresh the TTL by default, a hot setting effectively becomes a sliding cache entry: every read pushes the expiration window forward. Under steady traffic, that can keep a stale value alive far beyond the configured ttlMs, and in practice it may not be reloaded from the database until traffic pauses or the process invalidates the key explicitly.

The safer default is to treat ttlMs as a bounded staleness window: once a value is cached, it can be reused until that original window expires, but reads do not extend the window. This makes freshness easier to reason about for loader-backed settings.

Loaders that truly want read-time TTL refresh can still opt in with updateAgeOnGet: true. Requiring that option to be explicit is intentional: sliding TTL is a different freshness contract, and a maintainer adding it is very likely making a deliberate trade-off for an idle-expiry cache rather than accidentally inheriting it as the global default.

Example

Suppose authSettingsLoader caches auth/OAuth runtime settings with a 5 minute TTL, and login traffic reads those settings continuously.

With the old default (updateAgeOnGet: true):

  • settings are loaded at 12:00;
  • an admin or generated API update changes the settings at 12:02;
  • each login request keeps refreshing the cached entry's age;
  • under continuous traffic, the 12:00 value can remain in use well past 12:05 because the cache entry keeps getting renewed on reads.

With the new default (updateAgeOnGet: false):

  • settings are still loaded at 12:00;
  • reads between 12:00 and 12:05 can reuse the cached value;
  • after the original 5 minute TTL expires, the next read reloads from the database, even if the setting was read constantly during that window.

That keeps the configured TTL as the maximum normal staleness window for loader-backed settings, while preserving an explicit escape hatch for loaders that intentionally want sliding idle-expiry behavior.

Testing

  • ../../node_modules/.bin/jest --config jest.config.js src/loaders/__tests__/create-loader.test.ts --runInBand
  • ../../node_modules/.bin/jest --config jest.config.js --runInBand
  • ../../node_modules/.bin/makage build --dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant