@effect-agent/storage-sqlite 0.0.1-beta.4 → 0.0.1-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +32 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/errors.ts +9 -9
- package/src/sqlite-journal.ts +103 -82
package/dist/index.mjs
CHANGED
|
@@ -5,19 +5,19 @@ import * as SqlClient from "effect/unstable/sql/SqlClient";
|
|
|
5
5
|
import { NodeCrypto } from "@effect/platform-node";
|
|
6
6
|
//#region src/errors.ts
|
|
7
7
|
/** The SQLite file uses a private-development format this adapter cannot read. */
|
|
8
|
-
var SqliteStorageCompatibilityError = class extends Schema.
|
|
8
|
+
var SqliteStorageCompatibilityError = class extends Schema.TaggedError()("SqliteStorageCompatibilityError", {
|
|
9
9
|
actualVersion: Schema.Int,
|
|
10
10
|
message: Schema.String,
|
|
11
11
|
supportedVersion: Schema.Int
|
|
12
12
|
}) {};
|
|
13
13
|
/** Stored bytes failed the current Schema and cannot be used as recovery truth. */
|
|
14
|
-
var SqliteStorageCorruptionError = class extends Schema.
|
|
14
|
+
var SqliteStorageCorruptionError = class extends Schema.TaggedError()("SqliteStorageCorruptionError", {
|
|
15
15
|
message: Schema.String,
|
|
16
16
|
rowKey: Schema.String,
|
|
17
17
|
table: Schema.String
|
|
18
18
|
}) {};
|
|
19
19
|
/** SQLite infrastructure failed while opening or operating the store. */
|
|
20
|
-
var SqliteStorageError = class extends Schema.
|
|
20
|
+
var SqliteStorageError = class extends Schema.TaggedError()("SqliteStorageError", {
|
|
21
21
|
cause: Schema.optionalKey(Schema.Defect()),
|
|
22
22
|
message: Schema.String,
|
|
23
23
|
operation: Schema.String
|
|
@@ -27,7 +27,7 @@ var SqliteStorageError = class extends Schema.TaggedErrorClass()("SqliteStorageE
|
|
|
27
27
|
* SubmissionLedger port as the typed `LedgerError` with this error preserved as its cause,
|
|
28
28
|
* so the adapter-level tag is never erased.
|
|
29
29
|
*/
|
|
30
|
-
var SqliteLedgerError = class extends Schema.
|
|
30
|
+
var SqliteLedgerError = class extends Schema.TaggedError()("SqliteLedgerError", {
|
|
31
31
|
cause: Schema.optionalKey(Schema.Defect()),
|
|
32
32
|
message: Schema.String,
|
|
33
33
|
operation: Schema.String
|
|
@@ -36,7 +36,7 @@ var SqliteLedgerError = class extends Schema.TaggedErrorClass()("SqliteLedgerErr
|
|
|
36
36
|
* A canonical batch retry conflicts with existing append state. Tail conflicts carry the
|
|
37
37
|
* actual committed tail as a diagnostic resume hint.
|
|
38
38
|
*/
|
|
39
|
-
var SqliteAppendConflict = class extends Schema.
|
|
39
|
+
var SqliteAppendConflict = class extends Schema.TaggedError()("SqliteAppendConflict", {
|
|
40
40
|
message: Schema.String,
|
|
41
41
|
reason: Schema.Literals([
|
|
42
42
|
"batch-digest",
|
|
@@ -51,7 +51,7 @@ var SqliteAppendConflict = class extends Schema.TaggedErrorClass()("SqliteAppend
|
|
|
51
51
|
* require the exact registered epoch, so both older and newer unregistered epochs are fenced;
|
|
52
52
|
* a newer epoch takes over by materializing first.
|
|
53
53
|
*/
|
|
54
|
-
var SqliteFenceRejected = class extends Schema.
|
|
54
|
+
var SqliteFenceRejected = class extends Schema.TaggedError()("SqliteFenceRejected", {
|
|
55
55
|
actualEpoch: ProducerEpoch,
|
|
56
56
|
message: Schema.String,
|
|
57
57
|
producerEpoch: ProducerEpoch
|
|
@@ -61,13 +61,13 @@ var SqliteFenceRejected = class extends Schema.TaggedErrorClass()("SqliteFenceRe
|
|
|
61
61
|
* timeout (SQLITE_BUSY / SQLITE_LOCKED). Another transiently coexisting owner is writing;
|
|
62
62
|
* the operation did not mutate canonical state and is safe to retry.
|
|
63
63
|
*/
|
|
64
|
-
var SqliteWriteContention = class extends Schema.
|
|
64
|
+
var SqliteWriteContention = class extends Schema.TaggedError()("SqliteWriteContention", {
|
|
65
65
|
cause: Schema.optionalKey(Schema.Defect()),
|
|
66
66
|
message: Schema.String,
|
|
67
67
|
operation: Schema.String
|
|
68
68
|
}) {};
|
|
69
69
|
/** A checkpoint conflicts with a previously stored checkpoint at the same offset. */
|
|
70
|
-
var SqliteCheckpointConflict = class extends Schema.
|
|
70
|
+
var SqliteCheckpointConflict = class extends Schema.TaggedError()("SqliteCheckpointConflict", { message: Schema.String }) {};
|
|
71
71
|
const SqliteStorageFailpointLocation = Schema.Literals([
|
|
72
72
|
"materialize:before",
|
|
73
73
|
"materialize:after",
|
|
@@ -123,7 +123,7 @@ const SqliteStorageFailpointLocation = Schema.Literals([
|
|
|
123
123
|
"ledger:child-settled:after"
|
|
124
124
|
]);
|
|
125
125
|
/** Deterministic test-only fault or pause injected at a SQLite operation boundary. */
|
|
126
|
-
var SqliteStorageFailpointError = class extends Schema.
|
|
126
|
+
var SqliteStorageFailpointError = class extends Schema.TaggedError()("SqliteStorageFailpointError", { location: SqliteStorageFailpointLocation }) {
|
|
127
127
|
get message() {
|
|
128
128
|
return `Injected SQLite storage failure at ${this.location}.`;
|
|
129
129
|
}
|
|
@@ -550,6 +550,25 @@ const makeJournal = (sql, failpoint) => {
|
|
|
550
550
|
yield* Effect.orDie(connection.executeUnprepared("ROLLBACK", [], void 0));
|
|
551
551
|
return yield* exit;
|
|
552
552
|
})).pipe(Effect.withSpan("SqliteJournal.withWriteTransaction", { attributes: { operation } })));
|
|
553
|
+
/**
|
|
554
|
+
* Runs a read-only snapshot under a deferred transaction. Effect's SQLite client now
|
|
555
|
+
* starts every writable-client `withTransaction` with `BEGIN IMMEDIATE`, which is the
|
|
556
|
+
* right default for mutations but would make exports take the write lock and block a
|
|
557
|
+
* concurrent append. Reserving the connection and beginning explicitly preserves the
|
|
558
|
+
* adapter's snapshot-with-concurrent-writer contract. As with the write helper, a failed
|
|
559
|
+
* `BEGIN` is reported directly because there is no transaction to roll back.
|
|
560
|
+
*/
|
|
561
|
+
const withReadTransaction = (operation) => (effect) => Effect.uninterruptibleMask((restore) => Effect.scoped(Effect.gen(function* () {
|
|
562
|
+
const connection = yield* sql.reserve.pipe(Effect.mapError(storageError(operation)));
|
|
563
|
+
yield* connection.executeUnprepared("BEGIN", [], void 0).pipe(Effect.mapError(storageError(operation)));
|
|
564
|
+
const exit = yield* restore(Effect.provideService(effect, sql.transactionService, [connection, 0])).pipe(Effect.exit);
|
|
565
|
+
if (Exit.isSuccess(exit)) {
|
|
566
|
+
yield* connection.executeUnprepared("COMMIT", [], void 0).pipe(Effect.mapError(storageError(operation)));
|
|
567
|
+
return exit.value;
|
|
568
|
+
}
|
|
569
|
+
yield* Effect.orDie(connection.executeUnprepared("ROLLBACK", [], void 0));
|
|
570
|
+
return yield* exit;
|
|
571
|
+
})).pipe(Effect.withSpan("SqliteJournal.withReadTransaction", { attributes: { operation } })));
|
|
553
572
|
const materialize = Effect.fn("SqliteJournal.materialize")(function* (conversationId, createdAt, emptyTailDigest, producerEpoch) {
|
|
554
573
|
if (conversationId.length > MAX_IDENTIFIER_LENGTH || storedTextBytes(emptyTailDigest) > MAX_STORED_TEXT_BYTES) return yield* SqliteStorageError.make({
|
|
555
574
|
operation: "materialize conversation",
|
|
@@ -783,7 +802,7 @@ const makeJournal = (sql, failpoint) => {
|
|
|
783
802
|
return yield* decodeRows(Schema.Array(RecordRow), "effect_agent_canonical_records", `${request.conversationId}>${request.fromSequenceExclusive}`, rows);
|
|
784
803
|
});
|
|
785
804
|
const exportConversation = Effect.fn("SqliteJournal.exportConversation")(function* (conversationId) {
|
|
786
|
-
return yield*
|
|
805
|
+
return yield* withReadTransaction("export transaction")(Effect.gen(function* () {
|
|
787
806
|
const conversationRows = yield* sql`
|
|
788
807
|
SELECT
|
|
789
808
|
conversation_id,
|
|
@@ -836,7 +855,7 @@ const makeJournal = (sql, failpoint) => {
|
|
|
836
855
|
records: yield* decodeRows(Schema.Array(RecordRow), "effect_agent_canonical_records", conversationId, recordRows),
|
|
837
856
|
checkpoints: yield* decodeRows(Schema.Array(CheckpointRow), "effect_agent_checkpoints", conversationId, checkpointRows)
|
|
838
857
|
});
|
|
839
|
-
}))
|
|
858
|
+
}));
|
|
840
859
|
});
|
|
841
860
|
const saveCheckpoint = Effect.fn("SqliteJournal.saveCheckpoint")(function* (checkpoint) {
|
|
842
861
|
if (checkpoint.conversationId.length > MAX_IDENTIFIER_LENGTH || storedTextBytes(checkpoint.checkpointJson) > MAX_STORED_TEXT_BYTES) return yield* SqliteStorageError.make({
|
|
@@ -935,7 +954,7 @@ const makeJournal = (sql, failpoint) => {
|
|
|
935
954
|
read,
|
|
936
955
|
saveCheckpoint,
|
|
937
956
|
scanStoredPayloads: Effect.fn("SqliteJournal.scanStoredPayloads")(function* () {
|
|
938
|
-
return yield*
|
|
957
|
+
return yield* withReadTransaction("startup scan transaction")(Effect.gen(function* () {
|
|
939
958
|
const conversations = yield* sql`
|
|
940
959
|
SELECT
|
|
941
960
|
conversation_id,
|
|
@@ -983,7 +1002,7 @@ const makeJournal = (sql, failpoint) => {
|
|
|
983
1002
|
records: yield* decodeRows(Schema.Array(RecordRow), "effect_agent_canonical_records", "startup_scan", records),
|
|
984
1003
|
checkpoints: yield* decodeRows(Schema.Array(CheckpointRow), "effect_agent_checkpoints", "startup_scan", checkpoints)
|
|
985
1004
|
};
|
|
986
|
-
}))
|
|
1005
|
+
}));
|
|
987
1006
|
}),
|
|
988
1007
|
withWriteTransaction
|
|
989
1008
|
};
|