Skip to content

Commit 8625315

Browse files
committed
fix(vnext): harden catalog search lifecycle
1 parent 179c6d0 commit 8625315

9 files changed

Lines changed: 1854 additions & 101 deletions

docs/adr/0005-parser-independent-relation-completion.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,11 @@ state preparation and suppresses later revision listeners; an accidental
484484
Promise result receives best-effort detached rejection draining. Coordinator
485485
disposal revokes the preparation closure before external cleanup. The hook
486486
remains package-private and is not a provider or session extension point.
487+
The combined search coordinator also installs one package-owned disposal
488+
target. Epoch self-quarantine makes the outer coordinator inert before
489+
subscription cleanup continues, so search work cannot outlive its epoch
490+
authority. The target is receiver-free, invoked at most once, and its failure
491+
cannot reopen disposal.
487492

488493
A search that discovers a higher epoch supersedes itself instead of publishing
489494
against its older captured revision. Pages and cache entries from different
@@ -751,7 +756,10 @@ in-flight sharing, one latest-wins consumer per owner, independent
751756
cancellation, absolute queue and execution deadlines, response decoding, and
752757
epoch publication. An owner captures its scope and dialect when prepared, so
753758
individual requests cannot substitute provider, scope, dialect, or epoch
754-
authority.
759+
authority. The authenticated dialect runtime owns its canonical provider ID;
760+
callers cannot pair an unrelated ID and runtime. Establishing the first
761+
baseline re-keys other joinable unobserved work in that scope, allowing
762+
newly-observed consumers to join it without duplicating a provider call.
755763

756764
Cache entries, loading/retry policy, refresh observers and their leases, the
757765
40 ms completion-response budget, pagination composition, ranking, session

src/vnext/__tests__/relation-catalog-epoch-coordinator.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,38 @@ describe("catalog epoch coordinator construction and membership", () => {
236236
expect(reads).toBe(0);
237237
});
238238

239+
it("validates and invokes the package disposal target exactly once", () => {
240+
expect(
241+
Reflect.apply(createSqlCatalogEpochCoordinator, undefined, [
242+
capturedProvider(),
243+
undefined,
244+
1,
245+
]),
246+
).toEqual({
247+
reason: "invalid-disposal-target",
248+
status: "unavailable",
249+
});
250+
251+
let disposalCalls = 0;
252+
const created = createSqlCatalogEpochCoordinator(
253+
capturedProvider(),
254+
undefined,
255+
() => {
256+
disposalCalls += 1;
257+
throw new Error("package disposal target failed");
258+
},
259+
);
260+
expect(created.status).toBe("created");
261+
if (created.status !== "created") {
262+
throw new Error("Expected a coordinator fixture");
263+
}
264+
expect(() => {
265+
created.coordinator.dispose();
266+
created.coordinator.dispose();
267+
}).not.toThrow();
268+
expect(disposalCalls).toBe(1);
269+
});
270+
239271
it("validates exact bounded well-formed scopes without raw errors", () => {
240272
const owner = coordinator();
241273
expect(

src/vnext/__tests__/relation-catalog-search-work.bench.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,6 @@ function owner(
185185
): SqlCatalogSearchWorkOwner {
186186
const prepared = service.prepareOwner(
187187
"benchmark-scope",
188-
"postgresql",
189188
POSTGRESQL_SQL_RELATION_DIALECT,
190189
{
191190
prepareCatalogChange: () => () => undefined,

0 commit comments

Comments
 (0)