Revert "fix(resolution): filter stale-target edges so watch sync survives FK violations (#455) (#463)"
This reverts commit 1dfaf30a8b.
Switching to #462's approach — a single, lower-layer filter inside
QueryBuilder.insertEdges itself instead of three filters spread across
the resolution layer. The DB-layer filter protects every caller (current
and future) automatically and doesn't depend on the queries-layer
nodeCache invalidation staying perfect. See #455 for the bug.
The CHANGELOG entry for the user-facing fix is re-added on top of #462.
This commit is contained in:
@@ -812,19 +812,6 @@ export function synthesizeCallbackEdges(queries: QueryBuilder, ctx: ResolutionCo
|
||||
seen.add(key);
|
||||
merged.push(e);
|
||||
}
|
||||
if (merged.length > 0) {
|
||||
// Defense-in-depth (issues #42, #455): drop edges whose source/target
|
||||
// no longer resolves to a real node. Some channel maps cache native
|
||||
// Node refs across a resolver lifetime (WeakMap-keyed by context), so
|
||||
// a file rewrite between map build and synthesis can leave stale IDs
|
||||
// here. One FK violation aborts the whole batch — better to skip the
|
||||
// dead edges and emit the rest than lose every synthesized edge.
|
||||
const allIds = new Set<string>();
|
||||
for (const e of merged) { allIds.add(e.source); allIds.add(e.target); }
|
||||
const existing = queries.getNodesByIds([...allIds]);
|
||||
const validEdges = merged.filter((e) => existing.has(e.source) && existing.has(e.target));
|
||||
if (validEdges.length > 0) queries.insertEdges(validEdges);
|
||||
return validEdges.length;
|
||||
}
|
||||
return 0;
|
||||
if (merged.length > 0) queries.insertEdges(merged);
|
||||
return merged.length;
|
||||
}
|
||||
|
||||
+4
-28
@@ -607,28 +607,6 @@ export class ReferenceResolver {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Defense-in-depth: drop edges whose source or target is no longer in
|
||||
* the nodes table. PR #62 (issue #42) applied this filter at the
|
||||
* extraction-layer `insertEdges` site; #455 reports the same
|
||||
* `FOREIGN KEY constraint failed` reappearing here at the
|
||||
* resolution-layer site during watch sync, where a resolver lookup that
|
||||
* crosses a framework-specific cache can hand us a target whose node
|
||||
* was removed by a concurrent file rewrite. One batched, cache-aware
|
||||
* `getNodesByIds` query is enough to skip those edges quietly instead
|
||||
* of aborting the whole sync.
|
||||
*/
|
||||
private filterEdgesByExistingNodes(edges: Edge[]): Edge[] {
|
||||
if (edges.length === 0) return edges;
|
||||
const allIds = new Set<string>();
|
||||
for (const e of edges) {
|
||||
allIds.add(e.source);
|
||||
allIds.add(e.target);
|
||||
}
|
||||
const existing = this.queries.getNodesByIds([...allIds]);
|
||||
return edges.filter((e) => existing.has(e.source) && existing.has(e.target));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve and persist edges to database
|
||||
*/
|
||||
@@ -642,9 +620,8 @@ export class ReferenceResolver {
|
||||
const edges = this.createEdges(result.resolved);
|
||||
|
||||
// Insert edges into database
|
||||
const validEdges = this.filterEdgesByExistingNodes(edges);
|
||||
if (validEdges.length > 0) {
|
||||
this.queries.insertEdges(validEdges);
|
||||
if (edges.length > 0) {
|
||||
this.queries.insertEdges(edges);
|
||||
}
|
||||
|
||||
// Clean up resolved refs from unresolved_refs table so metrics are accurate
|
||||
@@ -691,9 +668,8 @@ export class ReferenceResolver {
|
||||
|
||||
// Persist edges immediately
|
||||
const edges = this.createEdges(result.resolved);
|
||||
const validEdges = this.filterEdgesByExistingNodes(edges);
|
||||
if (validEdges.length > 0) {
|
||||
this.queries.insertEdges(validEdges);
|
||||
if (edges.length > 0) {
|
||||
this.queries.insertEdges(edges);
|
||||
}
|
||||
|
||||
// Clean up resolved refs so they don't appear in the next batch
|
||||
|
||||
Reference in New Issue
Block a user