Repository navigation
fix(video): read only the camera's own notified captions in the dedup scan (part of #706) - #867
Merged
Merged
Conversation
… scan Part of #706. The store matches a namespace as a text prefix, so the recent-caption scan for camera.side also read camera.side_gate's notified captions, and its 200-row budget was spent before the database's rows reached Python's namespace check: a busy prefix-sharing camera hid side's own recent notifications, and a repeated scene notified again. - Each stored caption now names its camera, and the recent-caption scan filters on it in the database, before its page limit. - A background task adds the field to captions stored before it existed: a pending check each start, one run per database (advisory lock; a waiting run looks again every minute), a keyset walk in 2000-row batches that leaves updated_at alone, bounded statement/lock waits with retries, and a connection closed unless the attempt finished cleanly. It is cancelled at unload before the pool closes. - The similarity search is unchanged: a camera filter there made Postgres score every caption of a busy camera (0.6 s at 45k rows), and planner statistics traded that for partial results. Its prefix crowd-out stays open in #706. - get_recent_camera_activity had no caller since 1f68e4f; removed. - TODOS: the #828 frozen-server hang past the turn budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019kuwyteUa5Q6rkkB8hgwwj
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
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.
Part of #706. #706 stays open: this fixes the recent-caption scan, and one of the issue's three readers (the similarity search) is unchanged by design. See below.
What was wrong
The langgraph Postgres store matches a namespace as a text prefix (
prefix LIKE 'video_analysis.side%'), so a read forcamera.sidealso returnscamera.side_gate's captions._in a name is aLIKEwildcard as well. The recent-caption scan reads a camera's notified captions from the last 30 minutes, up to 200 rows in 4 pages. Rows from other cameras were dropped in Python only after the store applied its limit. So a busyside_gatecould fill all 200 rows,side's own recent notifications were never read, and a repeated scene notified again.Changes
camerafield, and the recent-caption scan filters on it in the database, before its page limit.core/caption_store.py) adds the field to captions stored before it existed:updated_atis never changed.get_recent_camera_activityremoved. It is the third reader Cameras whose names share a prefix read each other's stored captions #706 names, and nothing has called it since 1f68e4f.What is not fixed (why #706 stays open)
The similarity search (
_search_own_captions) is the same as onmain: no database filter, a wide limit of 50, and the Python namespace filter. A camera with 50 or more closer matches whose name extends this one's can still push this camera's matches out. That givesno_match, the scan is never reached, and a static scene can notify again.The two database-side fixes I measured both trade one problem for another. Measurements are from pg17 with pgvector 0.8.7, on 100k synthetic captions with 1024-dimension vectors and an HNSW index:
CREATE STATISTICSef_search=40)hnsw.iterative_scanPrepared generic plans did not undo the statistics; timings stayed steady over 40 runs per connection. The search's fix belongs with #706 and #707. Measurements are posted on #706.
Testing
make lintpasses andmake typecheckreports 0 errors.make test: 6490 passed, 1 skipped.test_caption_store.pycovers:test_video_analyzer_novelty.pyuses a fake store that matches namespaces the wayLIKEdoes (_as a wildcard) and applies the filter before the limit. Covered:side_gaterows no longer hideside's notificationtest_embedding_record.py: the backfill starts with a database, is cancelled at unload, and doesn't start without a database.store_pkeyupdated_atis unchanged, unrelated namespaces are untouched, no advisory lock is left behind, and pooled connections are back on their default timeoutsReview
gstack
/review: 3 cycles, not converged, because cycle 3 still needed small fixes, all applied. Codex ran in every cycle, alongside the Claude adversarial pass, the red team, and the testing, maintainability, security, performance, data-migration and simplification reviewers. Main design changes during review:Ships in the 3.45.0 bundle.
🤖 Generated with Claude Code
https://claude.ai/code/session_019kuwyteUa5Q6rkkB8hgwwj