Conversation
Seven tests in six adapters set a value that expires in 100 ms, then read it back expecting it to still be there. When set() and get() together take longer than 100 ms under CI load, the read finds the value expired. The postgres copy needed a retry in the #2180 branch run. - postgres, mysql, sqlite and cloudflare-kv decide expiry with Date.now(), so these tests now freeze Date and move it past the TTL instead of sleeping. They can't race the clock, and run 200 ms faster. - redis and valkey expire keys on the server's clock, so those tests get a 1 s window instead of 100 ms. The postgres and mysql copies also turn off Keyv's own expiry check, as the sqlite copy already did. With it on, Keyv deleted the expired value itself, so the tests passed even when the adapter left the expires column empty, the bug they are meant to catch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X5LJCvt3pkR7FtfyAdzg5x
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
What the check runs: What fails: "sets, gets, checks, and deletes a value", on all three attempts.
Fix: none exists yet. Proposed patch for a separate PR: poll until the delete is visible, instead of asserting it on the very next read. expect(await store.delete(key)).toBe(true);
// KV is eventually consistent: a read right after a delete can still be served from cache.
await vi.waitFor(async () => expect(await store.get(key)).toBeUndefined(), { timeout: 65_000, interval: 2_000 });
expect(await store.has(key)).toBe(false);That also needs Re-running the job once now. Generated by Claude Code |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2182 +/- ##
===========================================
+ Coverage 99.96% 100.00% +0.03%
===========================================
Files 56 56
Lines 5790 5790
Branches 996 998 +2
===========================================
+ Hits 5788 5790 +2
+ Misses 2 0 -2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Please check if the PR fulfills these requirements
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Test fix (timing-dependent tests).
Problem
Seven tests in six adapters set a value that expires in 100 ms, then read it back straight away expecting it to still be there. If
set()plus that firstget()take longer than 100 ms, the read finds the value already expired and the test fails. That happens under CI load, when every package tests at once.The Postgres copy needed a retry in the #2180 branch run: "populates the expires column for non-JSON encoded values…", 592 ms, retry x1.
Reproduced locally by delaying the first
get()by 150 ms, standing in for a slow round trip. Every current test failed: SQLite, Postgres, both Valkey tests, and Redis.While checking these I found a second problem. The Postgres and MySQL copies of "populates the expires column…" can't catch the bug they are named after:
checkExpired, on by default) deletes the expired value itself.expirescolumn empty.NULLin that column: the current test still passed.checkExpiredoff for exactly this reason.Changes
Postgres, MySQL, SQLite and Cloudflare KV (the adapter decides expiry with
Date.now()):Datewithvi.useFakeTimers({ toFake: ["Date"] }), then move it past the TTL withvi.setSystemTime()instead of sleeping.Dateis faked, so the database drivers' real timers keep running.Redis and Valkey (the server expires keys on its own clock, so faking
Datecan't help):Postgres and MySQL "populates the expires column…":
checkExpiredoff, as the SQLite copy already does, so expiry comes only from the store'sexpirescolumn.The other short-expiry tests I checked only assert expiry after a wait, which is safe, so I left them alone.
Verification
150 ms delay on the first read: each current test fails, and the fixed version passes. Run for SQLite, Postgres, both Valkey tests, and Redis.
Postgres adapter patched to leave
expiresempty: the current test passes, which is the gap described above. The fixed test fails withexpected 'calco' to be undefined.Full runs with retries off:
test/test.tstest/test.tstest/set.test.tsredis-servertest/set.test.tsredis-serverstanding inMySQL: not run locally, since no server is available here. Its change mirrors the Postgres one, and its
get,hasandclearExpiredreadDate.now()the same way. CI covers it.Lint:
biome check --error-on-warningsis clean on all six files.🤖 Generated with Claude Code
https://claude.ai/code/session_01X5LJCvt3pkR7FtfyAdzg5x
Generated by Claude Code