Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 5 additions & 35 deletions packages/plugin/src/features/magic-context/compartment-storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getHarness } from "../../shared/harness";
import type { Database, Statement as PreparedStatement } from "../../shared/sqlite";
import { runWriteTransaction } from "../../shared/write-transaction";
import { isCompartmentLeaseHeld } from "./compartment-lease";
import { getIncrementDepthStatement } from "./compression-depth-storage";
import { clearCachedM0M1 } from "./storage-meta-shared";
Expand Down Expand Up @@ -379,12 +380,8 @@ export function replaceAllCompartmentStateAndBumpDepth(
depthEndOrdinal: number,
): boolean {
const now = Date.now();
db.exec("BEGIN IMMEDIATE");
let finished = false;
try {
return runWriteTransaction(db, () => {
if (!isCompartmentLeaseHeld(db, sessionId, holderId)) {
db.exec("ROLLBACK");
finished = true;
return false;
}

Expand All @@ -402,19 +399,8 @@ export function replaceAllCompartmentStateAndBumpDepth(
stmt.run(sessionId, ordinal, getHarness());
}
}

db.exec("COMMIT");
finished = true;
return true;
} finally {
if (!finished) {
try {
db.exec("ROLLBACK");
} catch {
// Transaction may already be closed by SQLite after an error.
}
}
}
});
}

export interface CompartmentDateRanges {
Expand Down Expand Up @@ -587,19 +573,13 @@ export function promoteRecompStaging(
})();
}

db.exec("BEGIN IMMEDIATE");
let finished = false;
try {
return runWriteTransaction(db, () => {
if (!isCompartmentLeaseHeld(db, sessionId, holderId)) {
db.exec("ROLLBACK");
finished = true;
return null;
}

const staging = getRecompStaging(db, sessionId);
if (!staging || staging.compartments.length === 0) {
db.exec("ROLLBACK");
finished = true;
return null;
}
// Replace real tables
Expand All @@ -615,18 +595,8 @@ export function promoteRecompStaging(

clearCachedM0M1(db, sessionId);

db.exec("COMMIT");
finished = true;
return { compartments: staging.compartments, facts: staging.facts };
} finally {
if (!finished) {
try {
db.exec("ROLLBACK");
} catch {
// Transaction may already be closed by SQLite after an error.
}
}
}
});
}

/** Clear staging tables for a session (on cancel/abandon or after successful promote). */
Expand Down
25 changes: 4 additions & 21 deletions packages/plugin/src/features/magic-context/dreamer/lease.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Database } from "../../../shared/sqlite";
import { runWriteTransaction } from "../../../shared/write-transaction";
import { deleteDreamState, getDreamState, setDreamState } from "./storage-dream-state";

const LEASE_HOLDER_KEY = "dreaming_lease_holder";
Expand Down Expand Up @@ -43,27 +44,9 @@ export function peekLeaseHolderAndExpiry(db: Database, expectedHolder: string):
// = false under WAL snapshot isolation and both write — double-acquiring the
// lease and spawning duplicate dreamer workers. busy_timeout (set in
// initializeDatabase) makes the loser wait rather than throw SQLITE_BUSY.
function runImmediate<T>(db: Database, body: () => T): T {
db.exec("BEGIN IMMEDIATE");
let committed = false;
try {
const result = body();
db.exec("COMMIT");
committed = true;
return result;
} finally {
if (!committed) {
try {
db.exec("ROLLBACK");
} catch {
// already rolled back / no active transaction
}
}
}
}

export function acquireLease(db: Database, holderId: string): boolean {
return runImmediate(db, () => {
return runWriteTransaction(db, () => {
if (isLeaseActive(db)) {
const existingHolder = getLeaseHolder(db);
if (existingHolder && existingHolder !== holderId) {
Expand All @@ -80,7 +63,7 @@ export function acquireLease(db: Database, holderId: string): boolean {
}

export function renewLease(db: Database, holderId: string): boolean {
return runImmediate(db, () => {
return runWriteTransaction(db, () => {
if (getLeaseHolder(db) !== holderId || !isLeaseActive(db)) {
return false;
}
Expand All @@ -93,7 +76,7 @@ export function renewLease(db: Database, holderId: string): boolean {
}

export function releaseLease(db: Database, holderId: string): void {
runImmediate(db, () => {
runWriteTransaction(db, () => {
if (getLeaseHolder(db) !== holderId) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Database } from "../../../shared/sqlite";
import { runWriteTransaction } from "../../../shared/write-transaction";

export const GIT_SWEEP_COOLDOWN_MS = 10 * 60 * 1000;
// Commit indexing can include two embedding drains (the indexer drain plus the
Expand Down Expand Up @@ -59,25 +60,6 @@ export interface AcquireGitSweepLeaseOptions {
ignoreCooldown?: boolean;
}

function runImmediate<T>(db: Database, body: () => T): T {
db.exec("BEGIN IMMEDIATE");
let committed = false;
try {
const result = body();
db.exec("COMMIT");
committed = true;
return result;
} finally {
if (!committed) {
try {
db.exec("ROLLBACK");
} catch {
// already rolled back / no active transaction
}
}
}
}

function rowToState(row: GitSweepCoordinatorRow): GitSweepCoordinatorState {
return {
projectPath: row.project_path,
Expand Down Expand Up @@ -110,7 +92,7 @@ export function acquireGitSweepLease(
const cooldownMs = options.cooldownMs ?? GIT_SWEEP_COOLDOWN_MS;
const leaseTtlMs = options.leaseTtlMs ?? GIT_SWEEP_LEASE_TTL_MS;

return runImmediate(db, () => {
return runWriteTransaction(db, () => {
const now = Date.now();
const row = getGitSweepCoordinatorState(db, projectPath);
if (row?.leaseHolder && row.leaseExpiresAt !== null && row.leaseExpiresAt > now) {
Expand Down Expand Up @@ -173,7 +155,7 @@ export function renewGitSweepLease(
holderId: string,
leaseTtlMs = GIT_SWEEP_LEASE_TTL_MS,
): boolean {
return runImmediate(db, () => {
return runWriteTransaction(db, () => {
const now = Date.now();
const leaseExpiresAt = now + leaseTtlMs;
const result = db
Expand All @@ -194,7 +176,7 @@ export function markGitSweepSuccessAndRelease(
projectPath: string,
holderId: string,
): boolean {
return runImmediate(db, () => {
return runWriteTransaction(db, () => {
const now = Date.now();
const result = db
.prepare(
Expand All @@ -212,7 +194,7 @@ export function markGitSweepSuccessAndRelease(
}

export function releaseGitSweepLease(db: Database, projectPath: string, holderId: string): void {
runImmediate(db, () => {
runWriteTransaction(db, () => {
db.prepare(
`UPDATE git_sweep_coordinator
SET lease_holder = NULL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getHarness } from "../../../shared/harness";
import { shouldKeepSubagents } from "../../../shared/keep-subagents";
import { log } from "../../../shared/logger";
import type { Database } from "../../../shared/sqlite";
import { runWriteTransaction } from "../../../shared/write-transaction";
import { peekLeaseHolderAndExpiry, renewLease } from "../dreamer/lease";
import { recordChildInvocation } from "../subagent-token-capture";
import { isAftAvailable } from "./aft-availability";
Expand Down Expand Up @@ -423,9 +424,7 @@ export function commitKeyFiles(args: {
);
const generatedAt = Date.now();
const bump = args.bumpVersion ?? bumpKeyFilesVersion;
args.db.exec("BEGIN IMMEDIATE");
let committed = false;
try {
return runWriteTransaction(args.db, () => {
if (!peekLeaseHolderAndExpiry(args.db, args.leaseHolderId)) {
log(`key-files commit aborted: lease lost (holder ${args.leaseHolderId})`);
return null;
Expand All @@ -440,21 +439,11 @@ export function commitKeyFiles(args: {
args.configHash,
);
const version = bump(args.db, projectPath);
args.db.exec("COMMIT");
committed = true;
log(
`key-files committed: ${resolved.length} files, version=${version}, ${resolved.filter((r) => r.staleReason).length} pre-stale`,
);
return version;
} finally {
if (!committed) {
try {
args.db.exec("ROLLBACK");
} catch {
// no active transaction
}
}
}
});
}

async function runKeyFilesLlm(args: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { existsSync, readFileSync, realpathSync } from "node:fs";
import { join, resolve, sep } from "node:path";
import { log } from "../../../shared/logger";
import type { Database } from "../../../shared/sqlite";
import { runWriteTransaction } from "../../../shared/write-transaction";

export type KeyFileStaleReason = "missing" | "content_drift";

Expand Down Expand Up @@ -140,9 +141,7 @@ export function replaceProjectKeyFiles(
const generatedByModel = files[0]?.generatedByModel ?? null;
const configHash = files[0]?.generationConfigHash ?? sha256("{}");

db.exec("BEGIN IMMEDIATE");
let committed = false;
try {
return runWriteTransaction(db, () => {
db.prepare("DELETE FROM project_key_files WHERE project_path = ?").run(resolvedProjectPath);
insertResolvedKeyFiles(
db,
Expand All @@ -152,19 +151,8 @@ export function replaceProjectKeyFiles(
generatedByModel,
configHash,
);
const version = bumpKeyFilesVersion(db, resolvedProjectPath);
db.exec("COMMIT");
committed = true;
return version;
} finally {
if (!committed) {
try {
db.exec("ROLLBACK");
} catch {
// no active transaction
}
}
}
return bumpKeyFilesVersion(db, resolvedProjectPath);
});
}

export function insertResolvedKeyFiles(
Expand Down
19 changes: 4 additions & 15 deletions packages/plugin/src/features/magic-context/message-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { RawMessage } from "../../hooks/magic-context/read-session-raw";
import { getHarness } from "../../shared/harness";
import type { Database, Statement as PreparedStatement } from "../../shared/sqlite";
import { removeSystemReminders } from "../../shared/system-directive";
import { runWriteTransaction } from "../../shared/write-transaction";
import { clearCompressionDepth } from "./compression-depth-storage";

interface MessageHistoryIndexRow {
Expand Down Expand Up @@ -218,9 +219,7 @@ export function indexMessagesAfterOrdinal(
// is reflected, so the second skips those ordinals and inserts nothing
// duplicate. The bulk SELECT of existing message-ids is still avoided (it
// held the writer lock too long on ~30k-row sessions).
db.exec("BEGIN IMMEDIATE");
let committed = false;
try {
return runWriteTransaction(db, () => {
// Re-read under the lock: another process may have advanced the
// watermark between the caller's out-of-transaction read and now.
const effectiveWatermark = Math.max(
Expand All @@ -245,18 +244,8 @@ export function indexMessagesAfterOrdinal(
// Never regress a higher watermark a concurrent writer may have set.
const newWatermark = Math.max(effectiveWatermark, finalWatermark);
getUpsertIndexStatement(db).run(sessionId, newWatermark, now, getHarness());
db.exec("COMMIT");
committed = true;
} finally {
if (!committed) {
try {
db.exec("ROLLBACK");
} catch {
// already rolled back / no active transaction
}
}
}
return inserted;
return inserted;
});
}

export function ensureMessagesIndexed(
Expand Down
40 changes: 3 additions & 37 deletions packages/plugin/src/features/magic-context/workspaces.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createHash } from "node:crypto";
import type { Database } from "../../shared/sqlite";
import { runWriteTransaction } from "../../shared/write-transaction";
import { V2_MEMORY_CATEGORIES } from "./memory/constants";
import { normalizeStoredProjectPath, storedPathBelongsToIdentity } from "./project-identity";

Expand Down Expand Up @@ -271,11 +272,6 @@ export function computeWorkspaceEpochFingerprint(
return hash.digest("hex");
}

function isInTransaction(db: Database): boolean {
const candidate = db as unknown as { inTransaction?: unknown; isTransaction?: unknown };
return candidate.inTransaction === true || candidate.isTransaction === true;
}

function workspaceMembersForIdentity(db: Database, identity: string): string[] {
if (!tableExists(db, "workspace_members")) return [identity];
const rows = db
Expand Down Expand Up @@ -313,22 +309,7 @@ export function bumpEpochsForWorkspaceMembers(
now = Date.now(),
): void {
const run = () => bumpEpochRows(db, workspaceMembersForIdentity(db, identity), now);
if (isInTransaction(db)) {
run();
return;
}
db.exec("BEGIN IMMEDIATE");
try {
run();
db.exec("COMMIT");
} catch (error) {
try {
db.exec("ROLLBACK");
} catch {
// ignore rollback failures from an already-closed transaction
}
throw error;
}
runWriteTransaction(db, run);
}

export function bumpEpochsForWorkspaceMemberSet(
Expand All @@ -337,20 +318,5 @@ export function bumpEpochsForWorkspaceMemberSet(
now = Date.now(),
): void {
const run = () => bumpEpochRows(db, identities, now);
if (isInTransaction(db)) {
run();
return;
}
db.exec("BEGIN IMMEDIATE");
try {
run();
db.exec("COMMIT");
} catch (error) {
try {
db.exec("ROLLBACK");
} catch {
// ignore rollback failures from an already-closed transaction
}
throw error;
}
runWriteTransaction(db, run);
}
Loading
Loading