Centralize snapshot restore transactions

This commit is contained in:
2026-07-26 11:35:45 +02:00
parent 214ad728cd
commit 4789e01aac
4 changed files with 42 additions and 46 deletions
+2 -1
View File
@@ -21,7 +21,8 @@ It must support circuits, device rows, project devices, drag-and-drop restructur
`src/frontend/components/circuit-grid-*.ts` modules. `src/frontend/components/circuit-grid-*.ts` modules.
- Critical multi-write commands use injected transaction repositories with real - Critical multi-write commands use injected transaction repositories with real
SQLite commit/rollback tests. SQLite commit/rollback tests.
- All supported runtime project-command stores share - All supported runtime project-command stores, including full snapshot
restoration, share
`src/db/repositories/project-command-transaction.persistence.ts` for the `src/db/repositories/project-command-transaction.persistence.ts` for the
atomic domain-write, revision and history transition boundary. Its applied atomic domain-write, revision and history transition boundary. Its applied
forward-command variant preserves derived CircuitDeviceRow override metadata. forward-command variant preserves derived CircuitDeviceRow override metadata.
+2 -1
View File
@@ -84,7 +84,8 @@ absteigend paginierte Revisions-Timeline ist ohne Befehls-Payloads über
`GET /api/projects/:projectId/history/revisions` lesbar. Ein zentraler `GET /api/projects/:projectId/history/revisions` lesbar. Ein zentraler
Dispatcher führt die nachfolgend beschriebenen typisierten Kommandos über Dispatcher führt die nachfolgend beschriebenen typisierten Kommandos über
öffentliche Command-, Undo- und Redo-Endpunkte aus. öffentliche Command-, Undo- und Redo-Endpunkte aus.
Alle unterstützten Runtime-Project-Command-Stores verwenden dabei Alle unterstützten Runtime-Project-Command-Stores einschließlich der
vollständigen Snapshot-Wiederherstellung verwenden dabei
`project-command-transaction.persistence.ts` als gemeinsame äußere `project-command-transaction.persistence.ts` als gemeinsame äußere
Transaktionsgrenze. Sie führt Fachänderung, Revisions-Append und Transaktionsgrenze. Sie führt Fachänderung, Revisions-Append und
History-Transition in fester Reihenfolge innerhalb derselben SQLite-Transaktion History-Transition in fester Reihenfolge innerhalb derselben SQLite-Transaktion
+4 -3
View File
@@ -346,9 +346,10 @@ Implemented foundation:
- obsolete direct Circuit, CircuitDeviceRow, CircuitList, DistributionBoard and - obsolete direct Circuit, CircuitDeviceRow, CircuitList, DistributionBoard and
Project repository writes are removed; integration fixtures live under Project repository writes are removed; integration fixtures live under
`tests/support` instead of production repositories `tests/support` instead of production repositories
- all supported runtime project-command stores share one tested transaction - all supported runtime project-command stores, including complete snapshot
wrapper for domain mutation, revision append and history transition; its restore, share one tested transaction wrapper for domain mutation, revision
applied-forward variant retains derived CircuitDeviceRow override metadata append and history transition; its applied-forward variant retains derived
CircuitDeviceRow override metadata
- the legacy consumer UI and application read/write endpoints are removed after verified data cutover - the legacy consumer UI and application read/write endpoints are removed after verified data cutover
- retained legacy rows are accessible only through explicit database upgrade tooling - retained legacy rows are accessible only through explicit database upgrade tooling
- project devices no longer persist duplicate legacy power, phase, cosPhi or remark fields - project devices no longer persist duplicate legacy power, phase, cosPhi or remark fields
@@ -25,8 +25,7 @@ import { legacyConsumerMigrationReports } from "../schema/legacy-consumer-migrat
import { projectDevices } from "../schema/project-devices.js"; import { projectDevices } from "../schema/project-devices.js";
import { projects } from "../schema/projects.js"; import { projects } from "../schema/projects.js";
import { rooms } from "../schema/rooms.js"; import { rooms } from "../schema/rooms.js";
import { applyProjectHistoryTransition } from "./project-history.persistence.js"; import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
import { appendProjectRevision } from "./project-revision.persistence.js";
import { import {
hashProjectStatePayload, hashProjectStatePayload,
readProjectStateSnapshot, readProjectStateSnapshot,
@@ -43,7 +42,17 @@ export class ProjectStateRestoreCommandRepository
throw new Error("Restore state belongs to a different project."); throw new Error("Restore state belongs to a different project.");
} }
return this.database.transaction((tx) => { return executeProjectCommandTransaction(
this.database,
input,
(tx) => this.applyCommand(tx, input)
);
}
private applyCommand(
tx: AppDatabase,
input: ExecuteProjectStateRestoreCommandInput
) {
const current = readProjectStateSnapshot(tx, input.projectId); const current = readProjectStateSnapshot(tx, input.projectId);
if (!current) { if (!current) {
throw new Error("Project not found."); throw new Error("Project not found.");
@@ -69,23 +78,7 @@ export class ProjectStateRestoreCommandRepository
replaceProjectState(tx, input.command.payload.targetState); replaceProjectState(tx, input.command.payload.targetState);
const revision = appendProjectRevision(tx, { return inverse;
projectId: input.projectId,
expectedRevision: input.expectedRevision,
source: input.source,
description: input.description,
actorId: input.actorId,
forward: input.command,
inverse,
});
applyProjectHistoryTransition(tx, {
projectId: input.projectId,
source: input.source,
recordedChangeSetId: revision.changeSetId,
targetChangeSetId: input.historyTargetChangeSetId,
});
return { revision, inverse };
});
} }
} }