TTL-cache CMS (WordPress) reads — removes the ~3s per-request floor - #257
Merged
Merged
Conversation
…nd trip
The ODDK theme awaits a CMS call in global `app.use` middleware on every
non-static request, so every page paid a WordPress round trip before its own
work started. Measured on staging:
/static/js/home_page.js 0.44s (express.static, before the middleware)
/robots.txt 2.9s
a 404 13.3s
That ~3s floor is why the home page still took ~2.4s after the CKAN
organization_list cache landed - the remaining time was not CKAN.
The specific call is CmsModel.getListOfPosts({type: 'page'}), used to build the
navbar. Every method on CmsModel is a read of slow-changing blog content, so
this caches them with the same TtlCache already used for CKAN actions:
single-flight so concurrent misses collapse into one upstream call, and
stale-on-error so a WordPress blip serves the last good value instead of
silently emptying the navbar (the theme currently catches and falls back to []).
Opt-in via CMS_CACHE_TTL, default 0, so portals that have not asked for it are
unaffected.
Two small correctness fixes fall out of keying a cache on the query:
- getListOfPostsWithMeta used Object.assign(query, this.baseQuery), mutating
the caller's object and writing `status` into it. A caller reusing an object
therefore produced a different cache key on the second call. Now
Object.assign({}, query, ...).
- getListOfPages likewise set query.type = 'page' on the caller's object.
The cache instance is injectable so tests get isolation; at runtime every model
shares one, which is the point - plugins/wp and plugins/ckan_pages each build
their own model and should not each hold a private copy of the same content.
tests/plugins/wp-cache.test.js: 8 tests, all passing. The 4 pre-existing
failures in tests/plugins/wp.test.js are unchanged - verified identical with
and without this commit by stashing it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to
#256. That PR cached CKAN'sorganization_listand took the ODDK home page from 11.7s to ~2.4s in production — but ~2.4s is still slow, and the remaining time is not CKAN.The measurement
The ODDK theme awaits a CMS call in global
app.usemiddleware on every non-static request, so every page pays a WordPress round trip before its own work starts:/static/js/home_page.jsexpress.static, mounted before the middleware/robots.txtA ~3s floor on anything routed through the app.
/robots.txtreturns a few hundred bytes and touches no data source, so that time is essentially all WordPress.The specific call is
CmsModel.getListOfPosts({ type: 'page' }), used to build the navbar.What this does
Every method on
CmsModelis a read of slow-changing blog content, so this caches them using the sameTtlCachealready shipped for CKAN actions in#256:[], so a WP outage currently silently empties the navbarCMS_CACHE_TTL(default0), so portals that have not asked for it are unaffectedCached:
getListOfPostsWithMeta(whichgetListOfPostsandgetListOfPagesboth route through),getPost,getCategories,getSiteInfo.Two correctness fixes that fall out
Keying a cache on the query exposed argument mutation:
getListOfPostsWithMetadidObject.assign(query, this.baseQuery)— mutating the caller's object and writingstatusinto it. A caller reusing an object would therefore produce a different cache key on the second call. NowObject.assign({}, query, ...).getListOfPageslikewise setquery.type = 'page'on the caller's object.Both are covered by tests asserting the passed object is untouched.
Design note
The cache instance is injectable so tests get isolation; at runtime every model shares one, which is the point —
plugins/wpandplugins/ckan_pageseach construct their ownCmsModeland should not each hold a private copy of the same blog content. (The first draft of the tests failed precisely because they assumed isolation from a shared cache — worth knowing if you add more.)Testing
tests/plugins/wp-cache.test.js— 8 tests, all passing: the middleware call is cached, distinct queries stay separate,getSiteInfo/getCategoriescached, caching is a no-op at TTL 0, concurrent reads collapse to one call, a failed refresh serves stale, and neither list method mutates its argument.tests/plugins/wp.test.jshas 4 pre-existing failures on this branch — verified identical with and without this commit by stashing it, so not introduced here.Rollout
Nothing changes until a portal sets
CMS_CACHE_TTL. For ODDK I would suggest 300s: the navbar and featured posts are editorial content where a few minutes of staleness is unremarkable, and even 300s removes essentially every per-request call under real traffic.Expected effect on ODDK, based on the numbers above: the ~3s floor collapses, so the cached home page should drop from ~2.4s to well under a second, and
/robots.txtand other light routes become near-instant.Worth noting separately: the 13.3s 404 path is not addressed here and looks like its own liability.