Skip to content

Quad writes commit before search projector runs with no rollback on projector failure #8

Description

@EthanThatOneKid

Summary

LibsqlQuadStore.createTransaction().commit() calls commitPatchToLibsql() (which persists quads to the database) before searchIndexProjector.projectNovelQuads() (which updates the search index). If the search projector throws, quads are already committed to the database with no rollback, leaving the search index stale.

Location

src/libsql/quad-store/libsql-quad-store.ts:71-104

` s
commit: async (patch, context) => {
const { novelInsertions, novelQuadIds, labelTouchedSubjects } =
await commitPatchToLibsql(patch, this.options, context);
// ^ quads are now in the DB, no turning back

if (!skipSearchIndexProjection && this.options.searchIndexProjector) {
await this.options.searchIndexProjector.projectNovelQuads(
novelInsertions, novelQuadIds, labelTouchedSubjects,
);
// ^ if this throws, the quads from commitPatchToLibsql are orphaned
}
}
`

Evidence

Test "LibsqlQuadStore commit - quads persist even when searchProjector throws" in src/libsql/failure-point-tests.test.ts demonstrates this: a mock projector that throws still leaves the quad persisted in the database.

Impact

  • Low-medium severity in practice. The quads table is the source of truth;
    ebuildLibsqlSearchIndexFromQuads() exists to fix a stale index. A projector failure on one commit leaves a temporary search inconsistency that the next successful commit or explicit rebuild resolves.
  • Mitigation exists: users can call
    eindex() or the next successful commit will restore search consistency.
  • No data loss — only a temporary blind spot in search results.

Suggested Fix

Option A (simplest): Move the search projector call before the quad flush, or defer the quad write until after the projector succeeds. Risk: reversed gap — quads are lost if projector succeeds but flush fails.

Option B (more robust): Wrap commitPatchToLibsql + projectNovelQuads in a single SQL transaction. This requires the transaction to span two concerns (SQL write + projector function call), which may be impractical if the projector is an external service.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions