fix(19.3-06): queue site-cache purges after commit and coalesce them (WR-01)
- PurgeSiteCacheAfterCommit queues the purge on a per-purger worker after commit: one request in flight, saves committed meanwhile collapse into one follow-up, so the admin request never waits for the site - new cooler.SiteCachePurger.Wait drains the queue; Handle.Close waits for it at shutdown (timeout plus one second) - Purge stays the synchronous send; exported names and signatures unchanged
This commit is contained in:
@@ -97,7 +97,7 @@ The `serve` command installs cooler at start-up (`cooler.Install`) and publishes
|
||||
|
||||
## Site cache purge
|
||||
|
||||
A server-rendered site in front of the API usually caches its pages for a few minutes (a Nuxt `routeRules` `swr` window, for example). Without help, an edit in the admin shows on the site only when that window ends. cooler closes the gap: after an admin save commits, it sends the site one purge request, and the site drops its route cache, so the next page view renders the change.
|
||||
A server-rendered site in front of the API usually caches its pages for a few minutes (a Nuxt `routeRules` `swr` window, for example). Without help, an edit in the admin shows on the site only when that window ends. cooler closes the gap: after an admin save commits, it queues a purge request to the site, and the site drops its route cache, so the next page view renders the change.
|
||||
|
||||
Configure the target once for the application:
|
||||
|
||||
@@ -110,7 +110,7 @@ Set both or neither; with neither, nothing is purged. The URL must be http or ht
|
||||
|
||||
### What a purge sends
|
||||
|
||||
Each committed save sends one request, with no debounce:
|
||||
Each committed save queues a purge; the request looks like this:
|
||||
|
||||
```http
|
||||
POST /api/__site__/cache/purge
|
||||
@@ -122,20 +122,24 @@ Content-Type: application/json
|
||||
|
||||
The kind names what was saved (a plugin picks it, for example `faq`, `menu`, `menu_entry`, `post` or `category`) and the slug which record; the site may log them. The site answers `{"purged": <n>}`, and cooler logs `cooler: site cache purged` with the kind, slug, status and count.
|
||||
|
||||
At most one request is in flight per process (per purger). Saves committed while it is sent collapse into one follow-up request carrying the last kind and slug, because each purge clears the site's whole route cache: a bulk delete of fifty rows sends at most two requests. The log line of such a follow-up adds `coalesced` with the number of saves it stands in for besides its own.
|
||||
|
||||
### Timing and failures
|
||||
|
||||
`cooler.PurgeSiteCacheAfterCommit` registers the request on the save's transaction through `lagoon.AfterCommit`. It is sent only after the commit, so a page view between the purge and the commit cannot re-cache the old content, and a save that fails or is rolled back sends nothing. Outside a transaction it is sent at once.
|
||||
`cooler.PurgeSiteCacheAfterCommit` registers the purge on the save's transaction through `lagoon.AfterCommit`. It is queued only after the commit, so a page view between the purge and the commit cannot re-cache the old content, and a save that fails or is rolled back sends nothing. Outside a transaction it is queued at once. Queuing never blocks: the admin request answers without waiting for the site, and a background worker sends the request.
|
||||
|
||||
A purge never fails a save. The request is bounded by `cooler.SiteCachePurgeTimeout` (2 s) and detached from the admin request's cancellation; an unreachable site, an error status or a timeout is logged as one `cooler: site cache purge failed` warning and the save answers as usual. Redirects are not followed: a 3xx answer is logged as a failed purge with its status, so the bearer token never goes to another URL and the POST never turns into a GET. Point `site_cache.purge_url` at the final URL.
|
||||
A purge never fails a save. Each request is bounded by `cooler.SiteCachePurgeTimeout` (2 s) and detached from the admin request's cancellation; an unreachable site, an error status or a timeout is logged as one `cooler: site cache purge failed` warning and the save answers as usual. Redirects are not followed: a 3xx answer is logged as a failed purge with its status, so the bearer token never goes to another URL and the POST never turns into a GET. Point `site_cache.purge_url` at the final URL.
|
||||
|
||||
Because the purge is fire and forget, a test or a console command that wants to see its effect calls `cooler.SiteCachePurger.Wait`, which returns once nothing is queued or in flight (or when its context ends). `serve` waits for a queued purge on shutdown, up to the timeout plus one second, through `cooler.Handle.Close`.
|
||||
|
||||
### Purging from a plugin
|
||||
|
||||
The `serve` command publishes the purger at start-up (`cooler.Install`), and `cooler.SiteCacheFromApp` returns it, or builds it from the keys in commands and tests that do not run `serve`. An admin controller calls it from its after-save hooks, which run inside the save transaction, and takes that transaction from `cabana.TxFromContext`:
|
||||
|
||||
```go src=modules/cooler/example_test.go#articlesController.purge
|
||||
// purge registers one site-cache purge for the saved article, sent after the
|
||||
// save transaction commits (at once outside one). Without a purge configured
|
||||
// the purger is nil and nothing is sent.
|
||||
// purge queues one site-cache purge for the saved article once the save
|
||||
// transaction commits (at once outside one); the save never waits for the
|
||||
// site. Without a purge configured the purger is nil and nothing is sent.
|
||||
func (c articlesController) purge(ctx context.Context, model any) error {
|
||||
article, ok := model.(*Article)
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user