Skip to content

chore(data): resolve five data-plane hygiene items (#178) - #208

Closed
xiaocui-big wants to merge 1 commit into
bytefolk:mainfrom
xiaocui-big:chore/data-plane-hygiene-178
Closed

xiaocui-big wants to merge 1 commit into
bytefolk:mainfrom
xiaocui-big:chore/data-plane-hygiene-178

Conversation

@xiaocui-big

Copy link
Copy Markdown

Summary

Changes

  1. embeddings_text uniqueness — added UNIQUE (file_id, chunk_index) constraint. The write path (indexer.go) deletes by file_id then batch-inserts; this gives the invariant database-level teeth. Integration test proves duplicate (file_id, chunk_index) is rejected.
  2. memories FK indexes — added partial indexes on source_file_id and created_by_user_id (both ON DELETE SET NULL) to avoid sequential scans on every file/user delete.
  3. memory_relations cascade alignment — changed all three FKs (workspace_id, source_id, target_id) from default NO ACTION to ON DELETE CASCADE, matching the memories cascade so a workspace delete cannot fail on orphan edges.
  4. N+1 fix in indexgeneration.List — replaced per-build listGenerations call with a single listGenerationsForBuilds query using build_id = ANY($1).
  5. Keyset cursor for ListRelations — added Cursor field to ListRelationsQuery, return type changed to *ListRelationsResult with NextCursor. Reuses memory/list.go's encodeListCursor/decodeListCursor helpers with a relation-specific filter hash.

Test plan

  • Migration 0024 applies cleanly on a populated database (not only empty schema).
  • TestEmbeddingsTextChunkUniqueness passes — duplicate (file_id, chunk_index) is rejected, different chunk_index for same file succeeds.
  • EXPLAIN on DELETE FROM files WHERE id = $1 shows index scan on idx_memories_source_file_id instead of sequential scan.
  • indexgeneration.List returns identical results to before, with one query instead of N+1.
  • ListRelations with a cursor returns the next page correctly; cursor with changed filters is rejected.
  • Existing ListRelations callers (API handler, integration tests) pass with the new *ListRelationsResult return type.
  • CI passes.

1. embeddings_text: add UNIQUE (file_id, chunk_index) constraint with
   integration test proving duplicate rejection.
2. memories: add partial indexes on source_file_id and created_by_user_id
   (both ON DELETE SET NULL) to avoid sequential scans on file/user delete.
3. memory_relations: align FK referential actions with memories CASCADE
   (workspace_id, source_id, target_id all ON DELETE CASCADE).
4. indexgeneration List: replace N+1 per-build generation query with a
   single batch query using build_id = ANY($1).
5. ListRelations: add keyset cursor reusing memory/list.go's
   encodeListCursor/decodeListCursor helpers, with filter hash.

All schema changes ship as migration 0024 with Down blocks.
@waterbro-8

Copy link
Copy Markdown
Collaborator

Blocking: migration 0024 is already owned by another open PR

This PR is not ready to merge, for a reason that has nothing to do with the quality of the five items it addresses. The five items themselves look well-chosen; item 1 in particular ("the write path already deletes by file_id then batch-inserts, give the invariant DB-level teeth") is the right instinct.

The problem is the filename.

The collision

main currently ends at 0023_workspace_import_merge.sql. This PR adds:

server/internal/db/migrations/0024_data_plane_hygiene.sql

But migration 0024 is already taken by #194:

codex/fix-pr-183  →  server/internal/db/migrations/0024_files_lexical_search.sql

Both branches are based on main at 0023, so both independently claim the number 24. Migration numbers in this repo are a single global sequence, not a per-branch namespace, and Goose resolves them by numeric version. Two files with the same version is not a merge conflict that Git will surface as textual conflict — Git will happily keep both files, and the failure lands at startup, on a real database, as a duplicate-version error or as "found N missing migrations".

The full current picture

branch PR owns
codex/fix-pr-183 #194 0024 files_lexical_search
codex/fix-pr-180 #197 0025 ann_hnsw_indexes
codex/fix-pr-185 #195 0026 data_plane_hygiene
xiaocui-big/mem #208 (this PR) 0024 data_plane_hygiene

Note the last two rows: 0026_data_plane_hygiene.sql already exists on codex/fix-pr-185, and this PR introduces 0024_data_plane_hygiene.sql. Same subject matter, two different numbers, two different branches, neither aware of the other. Whichever lands second will either be renumbered or silently shadowed.

What I need from you before this can merge

The ordering constraint is not mine to waive — docs/MIGRATION_SEQUENCE.md on codex/fix-pr-183 defines the cumulative order as #194#197#195, and #197 is currently on HOLD pending a text-planner acceptance decision. So:

  1. Confirm 0024 is fix(search): publish lexical route corrections (successor to #183) #194's, and renumber this PR's migration to the next free number after whatever fix(data): publish migration and cursor corrections (successor to #185) #195 ultimately lands. That is at minimum 0027, and it should be chosen at the moment this branch is rebased on the final main, not now.
  2. If items 1–3 of this PR are genuinely the same work as codex/fix-pr-185's 0026_data_plane_hygiene.sql, then this PR is a duplicate of part of fix(data): publish migration and cursor corrections (successor to #185) #195 and should be closed in favour of it rather than raced against it. Please compare the two files before deciding — I am flagging the name collision, but you are the one who can tell whether the contents are also the same five items.
  3. Also note this is a fork-head PR (xiaocui-big/mem). Fork heads do not receive the required HTTP, CLI and MCP lifecycle status context, so this PR can never show green checks regardless of its content. It will need to be moved to a branch in bytefolk/mem before it can satisfy branch protection.

Separately, on the body

The Test plan is still all unchecked:

- [ ] Migration 0024 applies cleanly on a populated database (not only empty schema).
- [ ] TestEmbeddingsTextChunkUniqueness passes ...
- [ ] EXPLAIN on DELETE FROM files WHERE id = $1 shows index scan ...
- [ ] indexgeneration.List returns identical results to before ...
- [ ] ListRelations with a cursor returns the next page correctly ...
- [ ] Existing ListRelations callers ... pass with the new *ListRelationsResult return type.

Every one of these is exactly the right thing to check, which is why I would rather see them checked than removed. In particular the first one — "applies cleanly on a populated database (not only empty schema)" — is the check that would have caught the collision above, since it is the populated path where a duplicate version number actually announces itself. Items 4 and 6 are behaviour-preservation claims about a return-type change (*ListRelationsResult), and item 5 is the cursor-with-changed-filters rejection; all three deserve a recorded result, not a checkbox.

This repo's convention is a provenance ledger pinned to a specific commit SHA. Please add one at the rebased head rather than referencing earlier runs.

@xiaocui-big

Copy link
Copy Markdown
Author

Closing in favor of #195.

After comparing with codex/fix-pr-185's 0026_data_plane_hygiene.sql, this PR covers the same five items from #178:

  • Items 1–3 (migration): identical schema changes (uniqueness constraint, partial FK indexes, cascade alignment)
  • Items 4–5 (Go code): same N+1 fix and keyset cursor for ListRelations

#195 additionally includes a defensive deduplication step before adding the uniqueness constraint, which is a nice safety improvement.

Since #195 is part of the coordinated migration sequence (#194#197#195) and already owns the 0026 slot, this PR is redundant. Deferring to #195.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(data): five data-plane hygiene items from the index audit

2 participants