fix(data): publish migration and cursor corrections (successor to #185) - #195
PeterGuy326 wants to merge 3 commits into
Conversation
|
Independent automated preflight: HOLD on deployment order at The source tree has migrations 1–23 and 26, without 24/25. The runner uses Goose without WithAllowMissing, so deploying this candidate before the lower numbered migrations makes their later introduction invalid. This is source-confirmed, not a newly executed PostgreSQL upgrade test. An enforced deployment sequence or coordinated, verified-unreleased numbering is required before this PR can be treated as independently deployable. The actual cursor, populated uniqueness, and bounded-query fixes passed independent source/compile checks; the implementation worker's real PostgreSQL evidence remains separately attributed. Latest 20/20 successful checks do not prove the cross-PR upgrade sequence. Follow-up is assigned, with no migration-policy waiver or deployed-history rewrite authorized. |
Code ReviewOverall code quality is good and test coverage is solid. Here are a few specific issues and suggestions: 1. [Medium] Migration 0026's DELETE deduplication may be slow on large datasetsDELETE FROM embeddings_text
WHERE id NOT IN (
SELECT DISTINCT ON (file_id, chunk_index) id
FROM embeddings_text
ORDER BY file_id, chunk_index, id
);
Suggestion: Add a pre-check to skip the DELETE if there are no duplicates: DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM embeddings_text
GROUP BY file_id, chunk_index
HAVING count(*) > 1
LIMIT 1
) THEN
DELETE FROM embeddings_text
WHERE id NOT IN (
SELECT DISTINCT ON (file_id, chunk_index) id
FROM embeddings_text
ORDER BY file_id, chunk_index, id
);
END IF;
END $$;This way, on the vast majority of production tables that have no duplicates, the migration only needs a lightweight GROUP BY check to pass. 2. [Low] Two valuable comments were removedThese two comments were removed from // The anchor memory must exist, be visible under the caller's allowed
// paths, and not be forgotten — mirroring Get — so listing relations never
// leaks the existence of a memory the caller cannot read.// BFS from target following supersedes/corrects edges in their forward
// direction: if we ever reach sourceID, there is a cycle.The first explains the existence-leakage guard for the security check — this is critical context for understanding why the anchor check is necessary. The second explains the BFS strategy of 3. [Low] Three unrelated fixes bundled in one migrationThe PR description says "each is a single DDL statement and none warrants its own schema version." However, Item 3 (the FK cascade change) is semantically the heaviest — it drops and recreates three foreign key constraints. If a deployed environment has manually adjusted these FKs (e.g., temporarily changed to DEFERRABLE), this migration would overwrite them. Consider documenting this assumption in the PR description or as a migration comment. 4. [Nit]
|
waterbro-8
left a comment
There was a problem hiding this comment.
Code review(draft,给作者的清单,非批准)
一、keyset 分页:形状是对的,而且做对了一件容易漏的事
我特意核了排序与游标谓词是否自洽,因为这是这类改动最常错的地方:
- 谓词
(r.created_at < $t OR (r.created_at = $t AND r.id > $id)),而 SQL 的排序是ORDER BY r.created_at DESC, r.id(id升序)。两者一致,不会出现"同一时间戳的行被跳过或重复"。这条我对照了 main 上的原实现与这条分支的实现,排序没有被改动,新增的谓词是配得上它的。 - 取
q.Limit+1行来探测是否还有下一页,len(out) <= q.Limit时不出next_cursor。✓ - 游标取自裁剪后那一页的最后一行(
result.Relations[len-1]),不是out[q.Limit]——这点很容易写错,这里是对的。 - 游标绑定了过滤条件的哈希(workspace + memory + direction + relationType 四项参与 sha256)。这是我认为最值得记一笔的地方:它让一个游标无法被拿到另一组过滤条件下重放。很多 keyset 实现会漏掉这个约束,导致跨查询的游标串味。
- 非法游标走
ErrInvalidCursor→ handler 映射成 400invalid_cursor,不是 500。✓
二、0026 迁移:三条都是能站住的,而且写清楚了取舍
- Item 1(唯一性):先按
DISTINCT ON (file_id, chunk_index) ... ORDER BY file_id, chunk_index, id去重,再加UNIQUE (file_id, chunk_index)。注释明说"保留 UUID 最小的那一行是为了确定性选择,UUID 顺序不等于插入顺序"——这句话很关键,它承认了去重规则是人为约定而非"保留原始那行"。我不认为这是缺陷(写入路径本来就是先删后插,理论上不会有重复),但这个诚实度值得保持。 - Item 2(部分索引):
idx_memories_source_file_id与idx_memories_created_by_user_id都是WHERE ... IS NOT NULL的部分索引,正是ON DELETE SET NULL反查需要的形状,而且部分索引不会为空值浪费空间。顺带一提:source_file_id这个索引正好也是我在 #210 上提出的那条守卫缺陷(只查memories.path、不查source_file_id)如果要修,所需要的底层支撑——两条 PR 在这点上是有协同的。 - Item 3(级联对齐):
memory_relations的三个 FK 改为与memories.workspace_id一致的ON DELETE CASCADE。理由写得很实在:memories会被 workspace 删除连带清掉,而memory_relations不级联的话,删 workspace 会因为还残留引用它的边而失败。这是真的会咬人的不一致。 Down把三处 FK 还原成无级联、删掉索引、删掉约束,顺序与 Up 相反。✓
三、非阻塞,但建议补一句的两点
- 这次迁移在生产库上不是无感的,而且它碰的是最大的那张表。 Item 1 的
DELETE ... WHERE id NOT IN (...)加ADD CONSTRAINT UNIQUE,都要全表扫描embeddings_text并加锁(约束建立期间对写入是排他的)。这不是缺陷,但docs/MIGRATION_SEQUENCE.md目前只讲了顺序与严格性,没讲迁移窗口。考虑到 0024(重写files+ 建索引)、0025(在三个embeddings_*上建 HNSW)、0026(又是embeddings_text)是连着跑的,建议在序列文档里补一句"这三次迁移会依次对最大的几张表加锁,请预留窗口"。 - Item 1 的
Down是不保数据的(被去重删掉的行不会回来),迁移注释里没提。这是 down 迁移的常态,我不反对;只是既然注释对其他取舍都写得很细,这里补一句"down 不恢复被去重的行"会更完整。
四、接口变更的处理是完整的
ListRelations 的 Go 签名从 ([]Relation, error) 改成 (*ListRelationsResult, error),涉及三处:api.go 的 MemoryService 接口、handlers_memory.go 的实际调用、以及 handlers_memory_test.go 里的 stub——三处都改了,没有留下编译不过的悬空实现。HTTP 层面是向后兼容的:relations 字段仍在,只是新增了可选的 next_cursor 与可选的 cursor 查询参数。
五、我没有声称验证的事
本机没有 PostgreSQL,我没有跑过 migrations_test.go、也没有跑过分页的集成测试。以上结论来自逐行读迁移、SQL 与分页逻辑,不是运行时观测。
另外需要点明:这条 PR 的 CI 绿不等于它可以先合。docs/MIGRATION_SEQUENCE.md 已经写明它是链上第 3 环(#195 HOLD behind #197),我同意这个约束——0026 声明的前驱是 schema 25,跳序部署会让 Goose 正确地在启动时失败。
(本次为独立评审:我不是本 PR 作者,也不是其 head 的推送者。draft 状态下我不投批准票。)
fdccb43 to
0a48f84
Compare
) ## Linked draft successor — original #183 remains open Refs #176. ## Current follow-up: `c0421168bd145077bf164f91c0d2b454a79760ef` - Fixed the remaining exported MCP route enum: `tools/list` now includes `lexical`. The in-process MCP regression reproduced the missing enum before the fix and now verifies both schema and `tools/call` HTTP forwarding. All 9 MCP tests pass on Linux; affected-package vet passes. - Added a strict, populated PostgreSQL sequential-upgrade regression and a DB-free embedded-migration continuity guard. Actual PostgreSQL 16.14 / pgvector 0.8.2 advances 23 → 24, verifies lexical backfill and full history, and accepts ordinary production startup afterward. No `AllowMissing` option or migration renumbering. - Cumulative source/base order is #194 → #197 → #195. #197 remains HOLD for text-query planner acceptance; #195 remains HOLD behind it. See `docs/MIGRATION_SEQUENCE.md` for deployment and existing-gap recovery boundaries. - Tests ran at `2f2e965b6794863d7f5a38a748262da4691beb35`; this final head only corrects the documented verification command to `scripts/verify.sh integration`. Fresh exact-head CI and independent review remain required. The earlier evidence below is historical, not a fresh-head approval. Preserves @sun-970 / liyuanyang's complete authored chain through `13ebe9efa6ba3c733199d374e8db3d107f598ca2` for #176. This draft makes the bounded reviewed corrections accessible; it does not replace independent review, CI, or human approval. Do not close #183 before a replacement is verified and merged. The original fork's Git-data write returned HTTP 404 and its repository permissions report `push:false`. No ACL override was attempted. This canonical branch preserves original commit identities and hashes. Added blobs, trees, and commits were checked against local Git hashes; no force update was used. ## Corrections - `scripts/verify.sh`: expect migration 24, matching this branch's lexical migration. The former expectation of 23 caused the PostgreSQL validation failure. - Refresh audited Web development dependencies, traceable to #192 commit `11e02e21ef2c3dbd2dae26e4376872e54e78ecb5` via a separate `cherry-pick -x` commit. Audit threshold unchanged. - No additional search/ranking changes beyond original #183. ## Validation at head `3d885a8427e06fd7175011ef0188a06f32028a3f` - PASS: migration to 24 on PostgreSQL 16.14 / pgvector 0.8.2 and PostgreSQL 17.10 / pgvector 0.8.3. - PASS: `TestLexicalSearchWithoutWorker`, five cases: lexical without worker, text/auto fail closed, CJK trigram, path restriction. Go tests cross-compiled for Linux and executed against real disposable PostgreSQL with `GOMAXPROCS=2` and serial package builds. - PASS: audited lockfile check; shared fix from #192 remains explicit, not attributed to this feature. - NOT CLAIMED: provider-backed vector retrieval or production benchmark quality. - REQUIRED: fresh CI on this exact head and independent review. Original-head CI/review is not approval of this draft. Original feature scope: model-free file-corpus lexical route, migration 0024, API/CLI/docs, managed-provider bypass. Scope excludes tokenizer, generation lifecycle, and ranking redesign.
平台组 Review(崔泽生,assigned by @冯浩然)一、总体评价结构清晰、文档充分的 PR,解决 #178 五项数据面卫生中的三项(migration 0026),同时修复关系分页游标方向 bug、index generation listing N+1 查询。CI 22/22 全绿。PR body 明确标注"非独立可部署"和 HOLD behind #197。 二、Migration 0026 正确性Item 1:
Item 2:
Item 3:
Up/Down 对称性:
三、memory/relation.go 游标分页——核心 bug 修复正确排序
集成测试验证了游标跨 filter 失效、同时间戳分页不重复不遗漏 ✅ 被删除的注释:同意 sun-970 的判断——anchor memory 安全不变量说明和 wouldCycle BFS 策略说明是"WHY is non-obvious"注释,建议恢复。 四、indexgeneration/service.go N+1 修复——干净
五、词法搜索路由(附加变更)
六、依赖链Goose 严格模式,跳序部署会正确失败。三个 PR 连续对大表加锁,生产部署需预留维护窗口。 七、建议级问题
八、核心判断代码质量高,变更正确,测试覆盖充分。依赖链管理严格,文档诚实。Draft 状态下不投批准票。等待 #197 先行解决、conflict 解决后,建议合并。 |
4536392 to
95af9fe
Compare
…178) 1. Add UNIQUE (file_id, chunk_index) on embeddings_text with defensive deduplication before the constraint. 2. Add partial indexes on memories(source_file_id) and memories(created_by_user_id) to avoid seq scans on ON DELETE SET NULL. 3. Add ON DELETE CASCADE to memory_relations FKs to align with the memories cascade and prevent orphan-edge failures on workspace delete. 4. Fix N+1 in indexgeneration List: batch-fetch generations for all builds in one query instead of per-build. 5. Add keyset cursor to ListRelations reusing the existing encodeListCursor/decodeListCursor helpers from memory/list.go. Includes migration 0025 with Down blocks and an integration test proving the embeddings_text uniqueness constraint rejects duplicates.
d1a3a95 to
ac4a3c9
Compare
Refs #178.
After #197 closed
#197 was superseded by #219 (0025 HNSW on main). This PR no longer stacks on
codex/fix-pr-180. It is rebased onto currentmainand keeps only the #178 data-plane hygiene slice:memory_relationswith workspace deletionHead:
ac4a3c9(3 cherry-picks: 5cbd05b → rename 0026 → cursor/populated verification). Lexical/HNSW files that were already on main were dropped.Not claimed: live production latency, or that #197 merged.