Repository navigation
cache: the Store.Set default bound is 1 second (v1.6.0, Refs #888) - #1151
Merged
Merged
Conversation
…stander for no more than that (v1.6.0, Refs #888) Red on main (5 s placeholder): TestSetTimeoutZeroSelectsTheDefault888 (context ends in 4.99996s), TestDefaultSetTimeoutIsOneSecond888, TestDefaultSetTimeoutIsInTheDoc888 and TestDefaultSetTimeoutBoundsTheLoopHold888 (epoll, 2 workers, a Set that ignores its context: the miss is answered after 5.003 s and a bystander waits 5.003 s).
A Set that never returns holds its request and, on epoll and io_uring, the event loop a sync request runs on, for the whole bound: a bystander request was delayed 951 ms at a 1 s bound and 2.95 s at 3 s (L16 review of #1131), so 5 s meant 5 s of a dead loop per hung fill. The field, the bound and the override are as they were (zero selects the default, a negative value waits for ever); only the default and its doc change. Release note: the cache store Set default bound is 1 s, subject to change before the tag.
|
Caution Review failedThe pull request is closed.
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 |
…e the bound; doc.go gains a Store.Set bound paragraph; test comments corrected; file renamed (Refs #888) Comments, doc and a file rename only, no behaviour change.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
FumingPower3925
marked this pull request as ready for review
October 28, 2026 09:07
This was referenced Oct 28, 2026
tests: the sync fall-through test feeds the rest of the reply only after the poll has returned
#1153
Merged
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.
The cache middleware's
Config.SetTimeoutdefault is 1 second. #1131 merged the bound with a placeholder of 5 s; the maintainer decided 1 s.Refs #888 (item 30).
Why 1 s
A
Store.Setthat never returns holds its request and, on epoll and io_uring, the event loop a sync request runs on, for the whole bound. The L16 review of #1131 measured a bystander request on the held loop delayed 951 ms at a 1 s bound and 2.95 s at 3 s, so 5 s meant 5 s of a dead loop per hung fill. Celeris's Redis and Memcached drivers set conn deadlines from the context's deadline, so a Set on a healthy link takes well under a millisecond and a 1 s bound is far above it.What changes
Only the default:
defaultSetTimeoutand the doc sentence "Zero selects the default, 1 second," inmiddleware/cache/config.go. The field, the bound, the override (any positive value) and the "negative waits for ever" behaviour are as before. No exported API change (mage apiCheck: matches 53 packages). The placeholder comments ("PENDING the maintainer's decision") are gone from config.go and from the test header.Release note: cache:
Config.SetTimeoutnow defaults to 1 second (the 5 s was a placeholder; v1.6.0 is unreleased). A Set not returned in 1 s is abandoned: the response is sent as usual withX-Cache: MISSand is not stored, and nothing is logged or counted unless the Set panics. A hung Set still holds the request, and on epoll and io_uring its event loop, for the whole bound. A store with a legitimately slower Set (Redis over a WAN, a saturated pool) should setcache.Config{SetTimeout: 5 * time.Second}; a negative value waits for ever. The default may change before the v1.6.0 tag.Tests (failing first)
Commits 1 and 2 are tests then fix; commit 1 is the tests only, red on main (5 s placeholder), in a linux/epoll container at
--cpus 2under-race:TestSetTimeoutZeroSelectsTheDefault888(now pins the literal 500 ms..1 s, not the constant)TestDefaultSetTimeoutIsOneSecond888(new: constant,applyDefaults, an explicit 3 s kept, a negative kept)TestDefaultSetTimeoutIsInTheDoc888(doc says "1 second,")TestDefaultSetTimeoutBoundsTheLoopHold888(new, linux; epoll, 2 workers, a Set that ignores its context,SetTimeoutunset)TestUnsetSetTimeoutBoundsTheStoresContext888Commit 2 is the fix: all green. The bystander test sends sequential pings on new connections until the miss is answered and takes the longest wait (the engine needs 2 workers, so a ping can land on the free loop; the first one the kernel hands to the held loop waits out the hold). It asserts miss and bystander within 2.5 s, the miss not before 800 ms, and a longest wait of at least 300 ms (otherwise the test measured nothing). Epoll only.
Mutants, all killed: default 3 s (3 tests red, the loop-hold one at 3.005 s), default 500 ms (loop-hold red: answered after 503 ms), a negative value defaulted (
IsOneSecondred), an explicit value ignored (IsOneSecondred).Runs (docker golang:1.27.3,
--cpus 2, one process at a time): the new tests-race -count=10ok; the wholemiddleware/cachepackage once under-race: only the io_uring subtests fail (no io_uring on Docker Desktop; 0 non-io_uring failures; the same set as main per the L16 review). golangci-lint v2.14.0 0 issues (linux and darwin);go vetwindows, darwin and linux clean;mage apiCheckok.Findings, not fixed here
From the L16 review of #1131 (
review-r1-opus.md), decisions the maintainer left open:*store.MemoryKVandMemoryStorethemselves. A struct embedding*store.MemoryKVis +175% on a miss (CacheMissOtherStore888: 789 to 2204 ns, 9 to 20 allocs; A/A -3.9%).store.Prefixed's cost is inferred from the same mechanism (the doc says about 1.4 us and 11 allocs per fill), not measured. An opt-out needs a marker thatstorecan implement (an exported method, or a module-internal package), andprefixedKVwould forward it only when the store it wraps is in-process.Get. A hung connection hangsGetbeforeSetis reached, so the Set bound protects only "Get works, Set hangs".SetTimeout1 ms, 2000 misses: 2000 goroutines and +37 MiB live heap (each abandoned Set pins up toMaxBodyBytes). Celeris's drivers honour the deadline, so only a third-party store that ignores ctx grows. A per-middleware cap (skip the Set past it) is the follow-up.MemoryStore(m3).CacheMissNoSingleflight+4.5% paired (A/A -1.9%), about 30 ns, allocations unchanged; most likely code placement. Optional: make the in-process fast path inlinable in the caller.slogat Error with its stack (cache.go); what remains is that a panic raised while the request is still waiting is re-raised with the value only, so the store goroutine's stack is lost.X-Cache: MISS. Only a panic in an abandoned Set is logged (cache.go). A store merely slower than the bound degrades the cache to permanent misses with no signal but the MISS ratio, and this happens more often at 1 s. The godoc ofSetTimeoutanddoc.gonow say so; a counter or log is a follow-up (API).driver/redis/conn.go~352), so each Set exceeding 1 s costs a reconnect.Timeout(3 s) is used only when the context has no deadline. Under the cache the Set always has the 1 s deadline, so the effective bound is 1 s: a user who raised the driver timeouts for a slow link also has to raiseSetTimeout.SetTimeout).CI note
The first run on
9a96459failedPortable fallback (macos-latest):TestSyncFallThroughAfterTheCapKeepsOrderAndTheError1104indriver/postgres(a data race,unexpected DataRow in extended query), which this PR does not touch. It is green on the current head.