fix(cloud-agent-next): prefix-scan preparation events by range, not LIKE - #4761
Conversation
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryReviewed the incremental commit adding ASCII validation for preparation Files Reviewed (3 files)
Previous Review Summary (commit 7b91d5e)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 7b91d5e)Status: No Issues Found | Recommendation: Merge Executive SummaryReviewed the switch from Files Reviewed (4 files)
Reviewed by claude-sonnet-5 · Input: 24 · Output: 4.4K · Cached: 467.7K Review guidance: REVIEW.md from base branch |
Summary
The
cloud-agent-nextsession storage had a single SQLLIKE, infindByEntityPrefix(queries/events.ts), used to load preparation-historyevents by
entity_idprefix. That prefix embeds a preparationattemptId. Whena session's stored
attemptIdis very large, the pattern passed toLIKEexceeds SQLite's max
LIKEpattern length (50 KB) and throws "LIKE or GLOBpattern too complex", which resets the Durable Object.
reconcileStalePreparationAttemptsruns that query on every client reconnect, so once a session holds an oversized
attemptIdit fails on every reconnect and cannot deliver its message.This replaces the
LIKEwith a range scan that has no pattern-length limit, andbounds the id length at ingest so an oversized value cannot be stored in the
first place. The affected query path was added with the preparation-history
feature in #4583.
Changes
queries/events.ts:findByEntityPrefixnow matches prefixes with ahalf-open range scan (
entity_id >= prefix AND entity_id < prefixUpperBound(prefix))instead of
like(entity_id,${prefix}%). Adds an exportedprefixUpperBoundhelper (increments the final code unit of the prefix) and removes the now
unused
likeimport.preparation-history.ts:materializePreparationEventrejects a preparingevent whose
attemptIdortriggerMessageIdexceeds 256 characters before itis stored, and logs a warning with the
sessionIdand the offending lengths.events.test.tscovers theprefixUpperBoundbound arithmetic,including that a 100 KB prefix no longer throws.
preparation-history.test.tscovers the oversized-id rejection (event dropped, nothing stored).
Verification
No manual end-to-end run. The failure lives in the Durable Object SQLite path,
and there is no local Durable Object harness in the service to exercise the real
query, so verification is by automated coverage plus static checks.
pnpm --filter cloud-agent-next testfor the two changed test files (22 pass)typecheck,lint(oxlint, 0 errors),format(oxfmt) all passVisual Changes
N/A
Reviewer Notes
entity_idvalues in use (ASCII paths ending in/), the range scanis equivalent to
LIKE prefix%. It also stops treating%/_in a prefix aswildcards, and removes the 50 KB pattern limit that caused the crash.
prefixUpperBoundreturnsnullfor an empty prefix or one that is entirelyU+FFFF; callers then fall back to a lower-bound-only scan (matches all rows at
or after the prefix, same as the previous
LIKE '%'for an empty prefix).attemptIdis a UUID(36 chars). The warn log surfaces the case if it ever happens, so the upstream
emitter can be traced rather than the value being dropped silently.
prefix query is expressed.