Repository navigation
cap ttlAutopurge timer delay at 2^31-1 ms - #417
Open
OsamaAnsar wants to merge 1 commit into
Open
OsamaAnsar wants to merge 1 commit into
OsamaAnsar wants to merge 1 commit into
Conversation
setTimeout() treats delays over 2^31-1 ms (about 24.8 days) as 1ms, and warns with TimeoutOverflowWarning. With ttlAutopurge and a longer ttl, the purge timer fired right away, found that the item was not stale, and set another timer for the same huge remaining time, over and over. The delay is capped, and the existing logic that sets the timer again if the item is not stale yet waits for the rest. A ttl of exactly 2^31-1 fires at the moment the item becomes stale (not before), so that case sets the timer for 1ms more instead of for 0.
This branch has not been deployed
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.
With
ttlAutopurgeand attlover 2^31-1 ms (about 24.8 days), the purge timer wassetTimeout(..., ttl + 1)with no cap. Node turns delays over 2^31-1 into 1 ms (with aTimeoutOverflowWarning). The timer fired immediately, found the item not stale, and set a new timer for the still-huge remaining time, which fired immediately too, and so on. An idle cache with one entry kept spinning.Repro: one entry,
ttlAutopurge: true, a counting wrapper aroundsetTimeout, observed for 1 second on Node v24.19.0 (all numbers below are from that run):The fix caps the delay at 2^31-1. The existing "not stale yet, set the timer again for the time left" logic then does the rest. One edge needed care: with
ttl === 2^31-1the capped timer fires exactly when the age equals the ttl, which is not stale yet (>ttl) and has0ms remaining, and a remaining time of 0 sets no timer, so the entry would have stayed until the next access. It now sets the timer for 1 ms in that case.I looked for other
setTimeoutcalls that can get a huge delay. The only other one is thettlResolutiontimer, whose delay is the user's resolution setting rather than a TTL, so I left it alone.Tests are in the new
test/ttl-autopurge-max-delay.ts(it has its own fake clock, since advancing the shared one intest/ttl.tswould shift the timestamps in the other snapshots). They cover a 30 day ttl (delay capped, one more timer for what remains, not purged early, purged just after the ttl, no timer loop), a ttl of exactly 2^31-1, and a ttl that fits, which is unchanged. With only the source change reverted, 7 assertions fail; with only the|| 1part reverted, 2 fail; with the fix, all 17 pass.Checked on Node v24.19.0 on Windows: full
tap -t0run (coverage enforcement left on) passes with 0 failures,oxlint src testreports 0 warnings and 0 errors, andprettier --checkis clean. I did not run Linux or macOS, other Node versions, or a browser. (On this machine I rantapwith a small preload that falls back to junctions forsymlink, and installed@tapjs/clockwith--no-save, see #413. Neither is part of this PR.)