@effect-agent/storage-sqlite 0.1.0-beta.45 → 0.1.0-beta.46
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/SqliteActivityStore.d.mts +14 -0
- package/dist/SqliteActivityStore.mjs +310 -0
- package/dist/SqliteActivityStore.mjs.map +1 -0
- package/dist/SqliteScheduleStore.d.mts +14 -0
- package/dist/SqliteScheduleStore.mjs +255 -0
- package/dist/SqliteScheduleStore.mjs.map +1 -0
- package/dist/SqliteStorageConfig.d.mts +34 -0
- package/dist/SqliteStorageConfig.mjs +39 -0
- package/dist/SqliteStorageConfig.mjs.map +1 -0
- package/dist/{sqlite-storage-failpoint-Cr0SXflP.d.mts → SqliteStorageError-W1oM5ka3.d.mts} +6 -15
- package/dist/SqliteStorageError.d.mts +2 -0
- package/dist/SqliteStorageError.mjs +144 -0
- package/dist/SqliteStorageError.mjs.map +1 -0
- package/dist/SqliteStorageFailpoint.d.mts +17 -0
- package/dist/{sqlite-storage-failpoint-DIwWk5dw.mjs → SqliteStorageFailpoint.mjs} +5 -3
- package/dist/SqliteStorageFailpoint.mjs.map +1 -0
- package/dist/{testing.d.mts → SqliteStorageFailpointTesting.d.mts} +3 -3
- package/dist/{testing.mjs → SqliteStorageFailpointTesting.mjs} +3 -3
- package/dist/SqliteStorageFailpointTesting.mjs.map +1 -0
- package/dist/SqliteStorageVersion-DwQbn4bw.d.mts +9 -0
- package/dist/SqliteStorageVersion.d.mts +2 -0
- package/dist/SqliteStorageVersion.mjs +8 -0
- package/dist/SqliteStorageVersion.mjs.map +1 -0
- package/dist/SqliteSubmissionLedger.d.mts +23 -0
- package/dist/SqliteSubmissionLedger.mjs +1656 -0
- package/dist/SqliteSubmissionLedger.mjs.map +1 -0
- package/dist/SqliteSubscriptionStore.d.mts +13 -0
- package/dist/SqliteSubscriptionStore.mjs +596 -0
- package/dist/SqliteSubscriptionStore.mjs.map +1 -0
- package/dist/SqliteThreadStore.d.mts +49 -0
- package/dist/SqliteThreadStore.mjs +422 -0
- package/dist/SqliteThreadStore.mjs.map +1 -0
- package/dist/index.d.mts +10 -108
- package/dist/index.mjs +10 -4265
- package/dist/migrations-B82C9A3r.mjs +293 -0
- package/dist/migrations-B82C9A3r.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/sqlite-journal-C5SZnSdC.mjs +656 -0
- package/dist/sqlite-journal-C5SZnSdC.mjs.map +1 -0
- package/package.json +1 -1
- package/src/{sqlite-activity-store.ts → SqliteActivityStore.ts} +2 -2
- package/src/{sqlite-schedule-store.ts → SqliteScheduleStore.ts} +9 -7
- package/src/{errors.ts → SqliteStorageError.ts} +1 -1
- package/src/{sqlite-storage-failpoint.ts → SqliteStorageFailpoint.ts} +4 -1
- package/src/{sqlite-storage-failpoint-testing.ts → SqliteStorageFailpointTesting.ts} +1 -1
- package/src/SqliteStorageVersion.ts +2 -0
- package/src/{sqlite-ledger.ts → SqliteSubmissionLedger.ts} +17 -15
- package/src/{sqlite-subscription-store.ts → SqliteSubscriptionStore.ts} +14 -12
- package/src/{sqlite-thread-store.ts → SqliteThreadStore.ts} +19 -21
- package/src/index.ts +9 -10
- package/src/{sqlite-journal.ts → internal/sqlite-journal.ts} +5 -5
- package/dist/index.mjs.map +0 -1
- package/dist/sqlite-storage-failpoint-DIwWk5dw.mjs.map +0 -1
- package/dist/testing.mjs.map +0 -1
- package/src/sqlite-memory-store.ts +0 -7
- package/src/testing.ts +0 -2
- /package/src/{sqlite-storage-config.ts → SqliteStorageConfig.ts} +0 -0
- /package/src/{migrations.ts → internal/migrations.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqliteSubscriptionStore.mjs","names":[],"sources":["../src/SqliteSubscriptionStore.ts"],"sourcesContent":["import { Digest } from \"@effect-agent/thread/Records\";\nimport { type SubscriptionFailpointError } from \"@effect-agent/thread/Subscription\";\nimport {\n AcceptedEvent,\n DeliveryChange,\n SourcePartition,\n SubscriptionDelivery,\n SubscriptionDeliveryKey,\n SubscriptionError,\n SubscriptionFailpoint,\n SubscriptionKey,\n SubscriptionLimits,\n SubscriptionName,\n SubscriptionRecord,\n SubscriptionScanCursors,\n SubscriptionStore,\n subscriptionDeliveryKeyString,\n} from \"@effect-agent/thread/Subscription\";\nimport {\n applySubscriptionDeliveryChange,\n sameAcceptedEventIdentity,\n sameSourcePartition,\n subscriptionCanSelect,\n subscriptionDeliveryCanSelect,\n} from \"@effect-agent/thread/SubscriptionTransition\";\nimport { Clock, Effect, Layer, Result, Schema } from \"effect\";\nimport * as SqlClientService from \"effect/unstable/sql/SqlClient\";\nimport type { SqlError } from \"effect/unstable/sql/SqlError\";\n\nimport { initializeSqliteJournal } from \"./internal/sqlite-journal.ts\";\nimport type { SqliteStorageConfig } from \"./SqliteStorageConfig.ts\";\nimport type { SqliteStorageFailpoint } from \"./SqliteStorageFailpoint.ts\";\nimport type { SqliteStorageInitializationError } from \"./SqliteThreadStore.ts\";\n\nconst StoredJson = Schema.String.check(Schema.isMaxLength(16 * 1024 * 1024));\nconst CountRow = Schema.Struct({ count: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)) });\nconst SequenceRow = Schema.Struct({ sequence: Schema.Natural });\n\nconst ScanRow = Schema.Struct({\n event_scan_cursor: Schema.String,\n delivery_scan_cursor: Schema.String,\n recovery_scan_cursor: Schema.Natural,\n});\n\nconst JsonRow = Schema.Struct({ record_json: StoredJson });\n\nconst RegistrationRow = Schema.Struct({\n owner_id: Schema.String,\n subscription_id: Schema.String,\n ordinal: Schema.Natural,\n source_name: Schema.String,\n source_version: Schema.String,\n matching_key: Schema.String,\n state: SubscriptionRecord.fields.state,\n expires_at_millis: Schema.Number,\n recovery_at_millis: Schema.NullOr(Schema.Number),\n record_json: StoredJson,\n});\n\nconst EventRow = Schema.Struct({\n event_id: Schema.String,\n source_name: Schema.String,\n source_version: Schema.String,\n matching_key: Schema.String,\n payload_digest: Digest,\n cutoff: Schema.Natural,\n cursor: Schema.Natural,\n routing_complete: Schema.Number,\n next_attempt_at_millis: Schema.Number,\n record_json: StoredJson,\n});\n\nconst DeliveryRow = Schema.Struct({\n owner_id: Schema.String,\n subscription_id: Schema.String,\n event_id: Schema.String,\n delivery_key: Schema.String,\n state: SubscriptionDelivery.fields.state,\n next_attempt_at_millis: Schema.Number,\n record_json: StoredJson,\n});\n\nconst error = (reason: SubscriptionError[\"reason\"], code: string) =>\n SubscriptionError.make({ reason, code });\n\nconst unavailable = (operation: string) => error(\"storage\", operation);\nconst corrupt = (operation: string) => error(\"corrupt\", operation);\nconst bytes = (value: unknown) => new TextEncoder().encode(JSON.stringify(value)).byteLength;\n\nconst validate = <A, I>(schema: Schema.Codec<A, I>, value: unknown, code: string) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => error(\"validation\", code)));\n\nconst encode = <A, I>(schema: Schema.Codec<A, I>, value: A, code: string) =>\n Schema.encodeEffect(Schema.fromJsonString(schema))(value).pipe(\n Effect.mapError(() => corrupt(code)),\n );\n\nconst decode = <A, I>(schema: Schema.Codec<A, I>, value: string, code: string) =>\n Schema.decodeEffect(Schema.fromJsonString(schema))(value).pipe(\n Effect.mapError(() => corrupt(code)),\n );\n\nconst decodeRows = <A, I>(schema: Schema.Codec<A, I>, rows: unknown, code: string) =>\n Schema.decodeUnknownEffect(Schema.Array(schema))(rows).pipe(Effect.mapError(() => corrupt(code)));\n\nconst sameDeliveryIdentity = (left: SubscriptionDelivery, right: SubscriptionDelivery): boolean =>\n subscriptionDeliveryKeyString(left.key) === subscriptionDeliveryKeyString(right.key) &&\n left.deliveryId === right.deliveryId &&\n left.source.name === right.source.name &&\n left.source.version === right.source.version &&\n left.threadId === right.threadId &&\n left.admissionKey === right.admissionKey &&\n left.subscriptionFingerprint === right.subscriptionFingerprint &&\n left.eventDigest === right.eventDigest;\n\nconst makeSubscriptionStore = Effect.fn(\"SqliteSubscriptionStore.make\")(function* (\n owned: SourcePartition,\n) {\n const partition = yield* validate(SourcePartition, owned, \"partition\");\n const sql = yield* SqlClientService.SqlClient;\n const failpoint = yield* SubscriptionFailpoint;\n\n yield* initializeSqliteJournal();\n yield* sql`\n INSERT INTO effect_agent_subscription_sequences (\n tenant_id, source_address, sequence, event_scan_cursor, delivery_scan_cursor, recovery_scan_cursor\n ) VALUES (${partition.tenantId}, ${partition.address}, 0, '', '', 0) ON CONFLICT DO NOTHING\n `.pipe(Effect.mapError(() => unavailable(\"initialize subscription partition\")));\n\n const transact = <A>(effect: Effect.Effect<A, SubscriptionError | SubscriptionFailpointError>) =>\n sql\n .withTransaction(effect)\n .pipe(Effect.catchTag(\"SqlError\", () => Effect.fail(unavailable(\"transaction\"))));\n\n const query = <A extends object>(\n effect: Effect.Effect<ReadonlyArray<A>, SqlError>,\n code: string,\n ) => effect.pipe(Effect.mapError(() => unavailable(code)));\n\n const requirePartition = (candidate: SourcePartition, code: string) =>\n sameSourcePartition(candidate, partition)\n ? Effect.void\n : Effect.fail(error(\"validation\", code));\n\n const requireKey = Effect.fn(\"SqliteSubscriptionStore.requireKey\")(function* (\n input: SubscriptionKey,\n code: string,\n ) {\n const key = yield* validate(SubscriptionKey, input, code);\n\n yield* requirePartition(key.partition, code);\n\n return key;\n });\n\n const readRegistration = Effect.fn(\"SqliteSubscriptionStore.readRegistration\")(function* (\n key: SubscriptionKey,\n code: string,\n ) {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT owner_id, subscription_id, ordinal, source_name, source_version, matching_key, state,\n expires_at_millis, recovery_at_millis, record_json FROM effect_agent_subscriptions\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n AND owner_id=${key.ownerId} AND subscription_id=${key.subscriptionId}\n `,\n code,\n );\n\n const decodedRows = yield* decodeRows(RegistrationRow, rows, code);\n\n if (decodedRows.length > 1) return yield* corrupt(code);\n const row = decodedRows[0];\n\n if (row === undefined) return null;\n const record = yield* decode(SubscriptionRecord, row.record_json, code);\n\n if (\n !sameSourcePartition(record.key.partition, partition) ||\n record.key.ownerId !== row.owner_id ||\n record.key.subscriptionId !== row.subscription_id ||\n record.ordinal !== row.ordinal ||\n record.configuration.source.name !== row.source_name ||\n record.configuration.source.version !== row.source_version ||\n record.configuration.matchingKey !== row.matching_key ||\n record.state !== row.state ||\n record.configuration.expiresAtMillis !== row.expires_at_millis ||\n (record.recovery?.nextAttemptAtMillis ?? null) !== row.recovery_at_millis\n )\n return yield* corrupt(`${code}-projection`);\n\n return record;\n });\n\n const readEvent = Effect.fn(\"SqliteSubscriptionStore.readEvent\")(function* (\n eventId: string,\n code: string,\n ) {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT event_id, source_name, source_version, matching_key, payload_digest, cutoff, cursor,\n routing_complete, next_attempt_at_millis, record_json FROM effect_agent_subscription_events\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND event_id=${eventId}\n `,\n code,\n );\n\n const decodedRows = yield* decodeRows(EventRow, rows, code);\n\n if (decodedRows.length > 1) return yield* corrupt(code);\n const row = decodedRows[0];\n\n if (row === undefined) return null;\n const event = yield* decode(AcceptedEvent, row.record_json, code);\n\n if (\n !sameSourcePartition(event.partition, partition) ||\n event.eventId !== row.event_id ||\n event.source.name !== row.source_name ||\n event.source.version !== row.source_version ||\n event.matchingKey !== row.matching_key ||\n event.payloadDigest !== row.payload_digest ||\n event.cutoff !== row.cutoff ||\n event.cursor !== row.cursor ||\n (event.routingComplete ? 1 : 0) !== row.routing_complete ||\n event.nextAttemptAtMillis !== row.next_attempt_at_millis\n )\n return yield* corrupt(`${code}-projection`);\n\n return event;\n });\n\n const readDelivery = Effect.fn(\"SqliteSubscriptionStore.readDelivery\")(function* (\n key: SubscriptionDeliveryKey,\n code: string,\n ) {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT owner_id, subscription_id, event_id, delivery_key, state, next_attempt_at_millis, record_json\n FROM effect_agent_subscription_deliveries\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n AND owner_id=${key.subscription.ownerId} AND subscription_id=${key.subscription.subscriptionId}\n AND event_id=${key.eventId}\n `,\n code,\n );\n\n const decodedRows = yield* decodeRows(DeliveryRow, rows, code);\n\n if (decodedRows.length > 1) return yield* corrupt(code);\n const row = decodedRows[0];\n\n if (row === undefined) return null;\n const delivery = yield* decode(SubscriptionDelivery, row.record_json, code);\n\n if (\n !sameSourcePartition(delivery.key.subscription.partition, partition) ||\n delivery.key.subscription.ownerId !== row.owner_id ||\n delivery.key.subscription.subscriptionId !== row.subscription_id ||\n delivery.key.eventId !== row.event_id ||\n subscriptionDeliveryKeyString(delivery.key) !== row.delivery_key ||\n delivery.state !== row.state ||\n delivery.retry.nextAttemptAtMillis !== row.next_attempt_at_millis\n )\n return yield* corrupt(`${code}-projection`);\n\n return delivery;\n });\n\n const count = Effect.fn(\"SqliteSubscriptionStore.count\")(function* (\n statement: Effect.Effect<ReadonlyArray<Record<string, unknown>>, SqlError>,\n code: string,\n ) {\n const rows = yield* query(statement, code);\n const decoded = yield* decodeRows(CountRow, rows, code);\n\n if (decoded.length !== 1) return yield* corrupt(code);\n\n return decoded[0].count;\n });\n\n const nextSequence = Effect.fn(\"SqliteSubscriptionStore.nextSequence\")(function* () {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n UPDATE effect_agent_subscription_sequences SET sequence=sequence+1\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n RETURNING sequence\n `,\n \"advance subscription sequence\",\n );\n\n const decoded = yield* decodeRows(SequenceRow, rows, \"advance subscription sequence\");\n\n if (decoded.length !== 1) return yield* corrupt(\"subscription sequence\");\n\n return decoded[0].sequence;\n });\n\n const writeRegistration = Effect.fn(\"SqliteSubscriptionStore.writeRegistration\")(function* (\n record: SubscriptionRecord,\n ) {\n const json = yield* encode(SubscriptionRecord, record, \"encode subscription\");\n\n yield* query(\n sql<Record<string, unknown>>`\n UPDATE effect_agent_subscriptions SET state=${record.state}, expires_at_millis=${record.configuration.expiresAtMillis},\n recovery_at_millis=${record.recovery?.nextAttemptAtMillis ?? null}, record_json=${json}\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n AND owner_id=${record.key.ownerId} AND subscription_id=${record.key.subscriptionId}\n `,\n \"write subscription\",\n );\n });\n\n const writeEvent = Effect.fn(\"SqliteSubscriptionStore.writeEvent\")(function* (\n event: AcceptedEvent,\n ) {\n const json = yield* encode(AcceptedEvent, event, \"encode event\");\n\n yield* query(\n sql<Record<string, unknown>>`\n UPDATE effect_agent_subscription_events SET cursor=${event.cursor}, routing_complete=${event.routingComplete ? 1 : 0},\n next_attempt_at_millis=${event.nextAttemptAtMillis}, record_json=${json}\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND event_id=${event.eventId}\n `,\n \"write event\",\n );\n });\n\n const writeDelivery = Effect.fn(\"SqliteSubscriptionStore.writeDelivery\")(function* (\n delivery: SubscriptionDelivery,\n ) {\n const json = yield* encode(SubscriptionDelivery, delivery, \"encode delivery\");\n\n yield* query(\n sql<Record<string, unknown>>`\n UPDATE effect_agent_subscription_deliveries SET state=${delivery.state},\n next_attempt_at_millis=${delivery.retry.nextAttemptAtMillis}, record_json=${json}\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n AND owner_id=${delivery.key.subscription.ownerId} AND subscription_id=${delivery.key.subscription.subscriptionId}\n AND event_id=${delivery.key.eventId}\n `,\n \"write delivery\",\n );\n });\n\n const register: SubscriptionStore[\"Service\"][\"register\"] = Effect.fn(\n \"SqliteSubscriptionStore.register\",\n )(function* (input, inputLimits) {\n const record = yield* validate(SubscriptionRecord, input, \"register-record\");\n const limits = yield* validate(SubscriptionLimits, inputLimits, \"register-limits\");\n\n yield* requirePartition(record.key.partition, \"register-partition\");\n\n const result = yield* transact(\n Effect.gen(function* () {\n const existing = yield* readRegistration(record.key, \"register-existing\");\n\n if (existing !== null) {\n if (existing.creationFingerprint !== record.creationFingerprint)\n return yield* error(\"conflict\", \"registration-identity\");\n\n return { value: existing, changed: false } as const;\n }\n if (bytes(record.configuration.context) > limits.maxContextBytes)\n return yield* error(\"capacity\", \"context-bytes\");\n if (bytes(record.configuration.parameters) > limits.maxPayloadBytes)\n return yield* error(\"capacity\", \"parameters-bytes\");\n if (\n record.configuration.expiresAtMillis - record.createdAtMillis >\n limits.maxLifetimeMillis\n )\n return yield* error(\"capacity\", \"lifetime\");\n if (\n (yield* count(\n sql<\n Record<string, unknown>\n >`SELECT COUNT(*) AS count FROM effect_agent_subscriptions WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}`,\n \"count registrations\",\n )) >= limits.maxRegistrations\n )\n return yield* error(\"capacity\", \"registrations\");\n if (\n (yield* count(\n sql<\n Record<string, unknown>\n >`SELECT COUNT(*) AS count FROM effect_agent_subscriptions WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND owner_id=${record.key.ownerId}`,\n \"count owner registrations\",\n )) >= limits.maxRegistrationsPerOwner\n )\n return yield* error(\"capacity\", \"owner-registrations\");\n const assigned = { ...record, ordinal: yield* nextSequence() };\n const json = yield* encode(SubscriptionRecord, assigned, \"encode registration\");\n\n yield* failpoint.hit(\"subscription:register:before\");\n yield* query(\n sql<Record<string, unknown>>`\n INSERT INTO effect_agent_subscriptions (tenant_id, source_address, owner_id, subscription_id, ordinal,\n source_name, source_version, matching_key, state, expires_at_millis, recovery_at_millis, record_json)\n VALUES (${partition.tenantId}, ${partition.address}, ${assigned.key.ownerId}, ${assigned.key.subscriptionId}, ${assigned.ordinal},\n ${assigned.configuration.source.name}, ${assigned.configuration.source.version}, ${assigned.configuration.matchingKey}, ${assigned.state},\n ${assigned.configuration.expiresAtMillis}, ${assigned.recovery?.nextAttemptAtMillis ?? null}, ${json})\n `,\n \"insert registration\",\n );\n\n return { value: assigned, changed: true } as const;\n }),\n );\n\n if (result.changed) yield* failpoint.hit(\"subscription:register:after\");\n\n return result.value;\n });\n\n const get: SubscriptionStore[\"Service\"][\"get\"] = Effect.fn(\"SqliteSubscriptionStore.get\")(\n function* (input) {\n return yield* readRegistration(yield* requireKey(input, \"get-key\"), \"get subscription\");\n },\n );\n\n const list: SubscriptionStore[\"Service\"][\"list\"] = Effect.fn(\"SqliteSubscriptionStore.list\")(\n function* (ownerId, after, limit) {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT owner_id, subscription_id, ordinal FROM effect_agent_subscriptions WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n AND owner_id=${ownerId} AND ordinal>${after} ORDER BY ordinal LIMIT ${limit}\n `,\n \"list subscriptions\",\n );\n\n const decoded = yield* decodeRows(\n Schema.Struct({\n owner_id: Schema.String,\n subscription_id: Schema.String,\n ordinal: Schema.Natural,\n }),\n rows,\n \"list subscriptions\",\n );\n\n return yield* Effect.forEach(\n decoded,\n Effect.fn(\"SqliteSubscriptionStore.listRecord\")(function* (row) {\n const record = yield* readRegistration(\n { partition, ownerId: row.owner_id, subscriptionId: row.subscription_id },\n \"list subscription\",\n );\n\n if (record === null || record.ordinal !== row.ordinal)\n return yield* corrupt(\"list subscription projection\");\n\n return record;\n }),\n );\n },\n );\n\n const cancel: SubscriptionStore[\"Service\"][\"cancel\"] = Effect.fn(\n \"SqliteSubscriptionStore.cancel\",\n )(function* (input) {\n const key = yield* requireKey(input, \"cancel-key\");\n\n const result = yield* transact(\n Effect.gen(function* () {\n const current = yield* readRegistration(key, \"cancel subscription\");\n\n if (current === null) return yield* error(\"not-found\", \"subscription\");\n if (current.state === \"cancelled\") return { value: current, changed: false } as const;\n const updated = { ...current, state: \"cancelled\" as const, recovery: null };\n\n yield* failpoint.hit(\"subscription:cancel:before\");\n yield* writeRegistration(updated);\n\n return { value: updated, changed: true } as const;\n }),\n );\n\n if (result.changed) yield* failpoint.hit(\"subscription:cancel:after\");\n\n return result.value;\n });\n\n const accept: SubscriptionStore[\"Service\"][\"accept\"] = Effect.fn(\n \"SqliteSubscriptionStore.accept\",\n )(function* (input, inputLimits) {\n const event = yield* validate(AcceptedEvent, input, \"accept-event\");\n const limits = yield* validate(SubscriptionLimits, inputLimits, \"accept-limits\");\n\n yield* requirePartition(event.partition, \"accept-partition\");\n\n const result = yield* transact(\n Effect.gen(function* () {\n const existing = yield* readEvent(event.eventId, \"accept event\");\n\n if (existing !== null) {\n if (!sameAcceptedEventIdentity(existing, event))\n return yield* error(\"conflict\", \"event-identity\");\n\n return { value: existing, changed: false } as const;\n }\n if (bytes(event.payload) > limits.maxPayloadBytes)\n return yield* error(\"capacity\", \"payload-bytes\");\n if (\n (yield* count(\n sql<\n Record<string, unknown>\n >`SELECT COUNT(*) AS count FROM effect_agent_subscription_events WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}`,\n \"count events\",\n )) >= limits.maxEvents\n )\n return yield* error(\"capacity\", \"events\");\n\n const accepted: AcceptedEvent = {\n ...event,\n cutoff: yield* nextSequence(),\n cursor: 0,\n routingComplete: false,\n routingFailure: null,\n };\n\n const json = yield* encode(AcceptedEvent, accepted, \"encode accepted event\");\n\n yield* failpoint.hit(\"subscription:accept:before\");\n yield* query(\n sql<Record<string, unknown>>`\n INSERT INTO effect_agent_subscription_events (tenant_id, source_address, event_id, source_name, source_version,\n matching_key, payload_digest, cutoff, cursor, routing_complete, next_attempt_at_millis, record_json)\n VALUES (${partition.tenantId}, ${partition.address}, ${accepted.eventId}, ${accepted.source.name}, ${accepted.source.version},\n ${accepted.matchingKey}, ${accepted.payloadDigest}, ${accepted.cutoff}, ${accepted.cursor}, 0, ${accepted.nextAttemptAtMillis}, ${json})\n `,\n \"insert event\",\n );\n\n return { value: accepted, changed: true } as const;\n }),\n );\n\n if (result.changed) yield* failpoint.hit(\"subscription:accept:after\");\n\n return result.value;\n });\n\n const event: SubscriptionStore[\"Service\"][\"event\"] = Effect.fn(\"SqliteSubscriptionStore.event\")(\n (eventId) => readEvent(eventId, \"get event\"),\n );\n\n const pendingEvents: SubscriptionStore[\"Service\"][\"pendingEvents\"] = Effect.fn(\n \"SqliteSubscriptionStore.pendingEvents\",\n )(function* (nowMillis, after, limit) {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT event_id FROM effect_agent_subscription_events WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n AND routing_complete=0 AND next_attempt_at_millis<=${nowMillis} AND event_id>${after} ORDER BY event_id LIMIT ${limit}\n `,\n \"pending events\",\n );\n\n return yield* decodeRows(\n Schema.Struct({ event_id: Schema.String }),\n rows,\n \"pending event keys\",\n ).pipe(Effect.map((items) => items.map((item) => item.event_id)));\n });\n\n const candidates: SubscriptionStore[\"Service\"][\"candidates\"] = Effect.fn(\n \"SqliteSubscriptionStore.candidates\",\n )(function* (input, limit) {\n const supplied = yield* validate(AcceptedEvent, input, \"candidates-event\");\n\n yield* requirePartition(supplied.partition, \"candidates-partition\");\n const stored = yield* readEvent(supplied.eventId, \"candidates event\");\n\n if (stored === null) return yield* error(\"not-found\", \"event\");\n if (!sameAcceptedEventIdentity(stored, supplied)) return yield* error(\"conflict\", \"event\");\n\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT owner_id, subscription_id, ordinal FROM effect_agent_subscriptions WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n AND source_name=${stored.source.name} AND source_version=${stored.source.version} AND matching_key=${stored.matchingKey}\n AND ordinal>${stored.cursor} AND ordinal<=${stored.cutoff} ORDER BY ordinal LIMIT ${limit}\n `,\n \"subscription candidates\",\n );\n\n const decoded = yield* decodeRows(\n Schema.Struct({\n owner_id: Schema.String,\n subscription_id: Schema.String,\n ordinal: Schema.Natural,\n }),\n rows,\n \"subscription candidates\",\n );\n\n return yield* Effect.forEach(\n decoded,\n Effect.fn(\"SqliteSubscriptionStore.candidateRecord\")(function* (row) {\n const record = yield* readRegistration(\n { partition, ownerId: row.owner_id, subscriptionId: row.subscription_id },\n \"subscription candidate\",\n );\n\n if (record === null || record.ordinal !== row.ordinal)\n return yield* corrupt(\"subscription candidate projection\");\n\n return record;\n }),\n );\n });\n\n const insertDelivery = Effect.fn(\"SqliteSubscriptionStore.insertDelivery\")(function* (\n delivery: SubscriptionDelivery,\n ) {\n const json = yield* encode(SubscriptionDelivery, delivery, \"encode selected delivery\");\n\n yield* query(\n sql<Record<string, unknown>>`\n INSERT INTO effect_agent_subscription_deliveries (tenant_id, source_address, owner_id, subscription_id, event_id,\n delivery_key, state, next_attempt_at_millis, record_json)\n VALUES (${partition.tenantId}, ${partition.address}, ${delivery.key.subscription.ownerId}, ${delivery.key.subscription.subscriptionId},\n ${delivery.key.eventId}, ${subscriptionDeliveryKeyString(delivery.key)}, ${delivery.state}, ${delivery.retry.nextAttemptAtMillis}, ${json})\n `,\n \"insert delivery\",\n );\n });\n\n const select: SubscriptionStore[\"Service\"][\"select\"] = Effect.fn(\n \"SqliteSubscriptionStore.select\",\n )(function* (inputEvent, inputDeliveries, cursor, complete, nowMillis, inputLimits) {\n const supplied = yield* validate(AcceptedEvent, inputEvent, \"select-event\");\n\n const deliveries = yield* validate(\n Schema.Array(SubscriptionDelivery),\n inputDeliveries,\n \"select-deliveries\",\n );\n\n const limits = yield* validate(SubscriptionLimits, inputLimits, \"select-limits\");\n\n yield* requirePartition(supplied.partition, \"select-partition\");\n for (const candidate of deliveries)\n yield* requirePartition(candidate.key.subscription.partition, \"select-delivery-partition\");\n\n const changed = yield* transact(\n Effect.gen(function* () {\n const accepted = yield* readEvent(supplied.eventId, \"select event\");\n\n if (accepted === null) return yield* error(\"not-found\", \"event\");\n if (!sameAcceptedEventIdentity(accepted, supplied) || accepted.cursor !== supplied.cursor)\n return yield* error(\"conflict\", \"event-cursor\");\n if (accepted.routingComplete) return false;\n if (!Number.isSafeInteger(cursor) || cursor < accepted.cursor || cursor > accepted.cutoff)\n return yield* error(\"validation\", \"cursor\");\n yield* failpoint.hit(\"subscription:select:before\");\n const effectiveNowMillis = Math.max(nowMillis, yield* Clock.currentTimeMillis);\n const additions: Array<{ delivery: SubscriptionDelivery; record: SubscriptionRecord }> = [];\n\n for (const delivery of deliveries) {\n const record = yield* readRegistration(delivery.key.subscription, \"select registration\");\n\n if (record === null) return yield* error(\"not-found\", \"subscription\");\n if (\n !subscriptionDeliveryCanSelect(delivery, record, accepted) ||\n delivery.key.eventId !== accepted.eventId ||\n delivery.source.name !== accepted.source.name ||\n delivery.source.version !== accepted.source.version ||\n record.ordinal <= accepted.cursor ||\n record.ordinal > cursor\n )\n return yield* error(\"conflict\", \"selection\");\n const existing = yield* readDelivery(delivery.key, \"select existing delivery\");\n\n if (existing !== null) {\n if (!sameDeliveryIdentity(existing, delivery))\n return yield* error(\"conflict\", \"delivery-identity\");\n continue;\n }\n if (!subscriptionCanSelect(record, accepted, effectiveNowMillis, false)) continue;\n additions.push({ delivery, record });\n }\n\n const total = yield* count(\n sql<\n Record<string, unknown>\n >`SELECT COUNT(*) AS count FROM effect_agent_subscription_deliveries WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}`,\n \"count deliveries\",\n );\n\n if (total + additions.length > limits.maxDeliveries)\n return yield* error(\"capacity\", \"deliveries\");\n for (const ownerId of new Set(additions.map(({ record }) => record.key.ownerId))) {\n const existing = yield* count(\n sql<\n Record<string, unknown>\n >`SELECT COUNT(*) AS count FROM effect_agent_subscription_deliveries WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND owner_id=${ownerId}`,\n \"count owner deliveries\",\n );\n\n if (\n existing + additions.filter(({ record }) => record.key.ownerId === ownerId).length >\n limits.maxDeliveriesPerOwner\n )\n return yield* error(\"capacity\", \"owner-deliveries\");\n }\n for (const addition of additions) {\n yield* insertDelivery(addition.delivery);\n if (addition.record.configuration.mode === \"once\")\n yield* writeRegistration({ ...addition.record, state: \"consumed\", recovery: null });\n }\n yield* writeEvent({ ...accepted, cursor, routingComplete: complete, routingFailure: null });\n\n return true;\n }),\n );\n\n if (changed) yield* failpoint.hit(\"subscription:select:after\");\n });\n\n const catchUp: SubscriptionStore[\"Service\"][\"catchUp\"] = Effect.fn(\n \"SqliteSubscriptionStore.catchUp\",\n )(function* (inputEvent, inputDelivery, nowMillis, inputLimits) {\n const supplied = yield* validate(AcceptedEvent, inputEvent, \"catch-up-event\");\n const delivery = yield* validate(SubscriptionDelivery, inputDelivery, \"catch-up-delivery\");\n const limits = yield* validate(SubscriptionLimits, inputLimits, \"catch-up-limits\");\n\n yield* requirePartition(supplied.partition, \"catch-up-partition\");\n yield* requirePartition(delivery.key.subscription.partition, \"catch-up-delivery-partition\");\n\n const changed = yield* transact(\n Effect.gen(function* () {\n const accepted = yield* readEvent(supplied.eventId, \"catch-up event\");\n const record = yield* readRegistration(delivery.key.subscription, \"catch-up subscription\");\n\n if (accepted === null || record === null)\n return yield* error(\"not-found\", accepted === null ? \"event\" : \"subscription\");\n if (\n !sameAcceptedEventIdentity(accepted, supplied) ||\n !subscriptionDeliveryCanSelect(delivery, record, accepted) ||\n delivery.key.eventId !== accepted.eventId ||\n delivery.source.name !== accepted.source.name ||\n delivery.source.version !== accepted.source.version ||\n record.configuration.mode !== \"once\"\n )\n return yield* error(\"conflict\", \"catch-up-identity\");\n const existing = yield* readDelivery(delivery.key, \"catch-up existing delivery\");\n\n if (existing !== null) {\n if (!sameDeliveryIdentity(existing, delivery))\n return yield* error(\"conflict\", \"delivery-identity\");\n\n return false;\n }\n yield* failpoint.hit(\"subscription:catch-up:before\");\n const effectiveNowMillis = Math.max(nowMillis, yield* Clock.currentTimeMillis);\n\n if (!subscriptionCanSelect(record, accepted, effectiveNowMillis, true))\n return yield* error(\"conflict\", \"catch-up-eligibility\");\n if (\n (yield* count(\n sql<\n Record<string, unknown>\n >`SELECT COUNT(*) AS count FROM effect_agent_subscription_deliveries WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}`,\n \"count deliveries\",\n )) >= limits.maxDeliveries\n )\n return yield* error(\"capacity\", \"deliveries\");\n if (\n (yield* count(\n sql<\n Record<string, unknown>\n >`SELECT COUNT(*) AS count FROM effect_agent_subscription_deliveries WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND owner_id=${record.key.ownerId}`,\n \"count owner deliveries\",\n )) >= limits.maxDeliveriesPerOwner\n )\n return yield* error(\"capacity\", \"owner-deliveries\");\n yield* insertDelivery(delivery);\n yield* writeRegistration({ ...record, state: \"consumed\", recovery: null });\n\n return true;\n }),\n );\n\n if (changed) yield* failpoint.hit(\"subscription:catch-up:after\");\n });\n\n const deferEvent: SubscriptionStore[\"Service\"][\"deferEvent\"] = Effect.fn(\n \"SqliteSubscriptionStore.deferEvent\",\n )(function* (eventId, nextAttemptAtMillis, code) {\n const routingFailure =\n code === undefined\n ? \"routing-failed\"\n : yield* validate(SubscriptionName, code, \"routing-failure\");\n\n yield* transact(\n Effect.gen(function* () {\n const accepted = yield* readEvent(eventId, \"defer event\");\n\n if (accepted === null) return yield* error(\"not-found\", \"event\");\n yield* failpoint.hit(\"subscription:defer-event:before\");\n yield* writeEvent({ ...accepted, nextAttemptAtMillis, routingFailure });\n }),\n );\n yield* failpoint.hit(\"subscription:defer-event:after\");\n });\n\n const delivery: SubscriptionStore[\"Service\"][\"delivery\"] = Effect.fn(\n \"SqliteSubscriptionStore.delivery\",\n )(function* (input) {\n const key = yield* validate(SubscriptionDeliveryKey, input, \"delivery-key\");\n\n yield* requirePartition(key.subscription.partition, \"delivery-partition\");\n\n return yield* readDelivery(key, \"get delivery\");\n });\n\n const pendingDeliveries: SubscriptionStore[\"Service\"][\"pendingDeliveries\"] = Effect.fn(\n \"SqliteSubscriptionStore.pendingDeliveries\",\n )(function* (nowMillis, after, limit) {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT owner_id, subscription_id, event_id FROM effect_agent_subscription_deliveries\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND state NOT IN ('delivered','refused')\n AND next_attempt_at_millis<=${nowMillis} AND delivery_key>${after} ORDER BY delivery_key LIMIT ${limit}\n `,\n \"pending deliveries\",\n );\n\n const rowSchema = Schema.Struct({\n owner_id: Schema.String,\n subscription_id: Schema.String,\n event_id: Schema.String,\n });\n\n return yield* decodeRows(rowSchema, rows, \"pending delivery keys\").pipe(\n Effect.map((items) =>\n items.map((item) => ({\n subscription: { partition, ownerId: item.owner_id, subscriptionId: item.subscription_id },\n eventId: item.event_id,\n })),\n ),\n );\n });\n\n const listDeliveries: SubscriptionStore[\"Service\"][\"listDeliveries\"] = Effect.fn(\n \"SqliteSubscriptionStore.listDeliveries\",\n )(function* (input, after, limit) {\n const key = yield* requireKey(input, \"list-deliveries-key\");\n\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT record_json FROM effect_agent_subscription_deliveries WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n AND owner_id=${key.ownerId} AND subscription_id=${key.subscriptionId} AND delivery_key>${after} ORDER BY delivery_key LIMIT ${limit}\n `,\n \"list deliveries\",\n );\n\n const decoded = yield* decodeRows(JsonRow, rows, \"list deliveries\");\n\n return yield* Effect.forEach(decoded, (row) =>\n decode(SubscriptionDelivery, row.record_json, \"list delivery\"),\n );\n });\n\n const changeDelivery: SubscriptionStore[\"Service\"][\"changeDelivery\"] = Effect.fn(\n \"SqliteSubscriptionStore.changeDelivery\",\n )(function* (inputKey, inputDeliveryId, inputChange) {\n const key = yield* validate(SubscriptionDeliveryKey, inputKey, \"change-delivery-key\");\n const deliveryId = yield* validate(Digest, inputDeliveryId, \"change-delivery-id\");\n const change = yield* validate(DeliveryChange, inputChange, \"change-delivery-change\");\n\n yield* requirePartition(key.subscription.partition, \"change-delivery-partition\");\n\n const result = yield* transact(\n Effect.gen(function* () {\n const existing = yield* readDelivery(key, \"change delivery\");\n const record = yield* readRegistration(key.subscription, \"change delivery subscription\");\n\n if (existing === null || record === null)\n return yield* error(\"not-found\", existing === null ? \"delivery\" : \"subscription\");\n yield* failpoint.hit(`subscription:delivery-${change._tag.toLowerCase()}:before`);\n\n const effectiveChange =\n change._tag === \"Prepare\"\n ? { ...change, nowMillis: Math.max(change.nowMillis, yield* Clock.currentTimeMillis) }\n : change;\n\n const transition = applySubscriptionDeliveryChange(\n existing,\n record,\n deliveryId,\n effectiveChange,\n );\n\n if (Result.isFailure(transition)) return yield* transition.failure;\n if (transition.success === existing) return { value: existing, changed: false } as const;\n yield* writeDelivery(transition.success);\n\n return { value: transition.success, changed: true } as const;\n }),\n );\n\n if (result.changed)\n yield* failpoint.hit(`subscription:delivery-${change._tag.toLowerCase()}:after`);\n\n return result.value;\n });\n\n const recovering: SubscriptionStore[\"Service\"][\"recovering\"] = Effect.fn(\n \"SqliteSubscriptionStore.recovering\",\n )(function* (nowMillis, after, limit) {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT owner_id, subscription_id, ordinal FROM effect_agent_subscriptions\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND state='active'\n AND recovery_at_millis IS NOT NULL AND recovery_at_millis<=${nowMillis} AND ordinal>${after}\n ORDER BY ordinal LIMIT ${limit}\n `,\n \"recovering subscriptions\",\n );\n\n const rowSchema = Schema.Struct({\n owner_id: Schema.String,\n subscription_id: Schema.String,\n ordinal: Schema.Natural,\n });\n\n return yield* decodeRows(rowSchema, rows, \"recovering subscription keys\").pipe(\n Effect.map((items) =>\n items.map((item) => ({\n key: { partition, ownerId: item.owner_id, subscriptionId: item.subscription_id },\n ordinal: item.ordinal,\n })),\n ),\n );\n });\n\n const deferRecovery: SubscriptionStore[\"Service\"][\"deferRecovery\"] = Effect.fn(\n \"SqliteSubscriptionStore.deferRecovery\",\n )(function* (input, recovery) {\n const key = yield* requireKey(input, \"defer-recovery-key\");\n\n yield* transact(\n Effect.gen(function* () {\n const record = yield* readRegistration(key, \"defer recovery\");\n\n if (record === null) return yield* error(\"not-found\", \"subscription\");\n yield* failpoint.hit(\"subscription:defer-recovery:before\");\n yield* writeRegistration({\n ...record,\n recovery: record.state === \"active\" ? recovery : null,\n });\n }),\n );\n yield* failpoint.hit(\"subscription:defer-recovery:after\");\n });\n\n const readScanCursors: SubscriptionStore[\"Service\"][\"readScanCursors\"] = Effect.gen(function* () {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT event_scan_cursor, delivery_scan_cursor, recovery_scan_cursor\n FROM effect_agent_subscription_sequences\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n `,\n \"read subscription scan cursors\",\n );\n\n const decoded = yield* decodeRows(ScanRow, rows, \"read subscription scan cursors\");\n\n if (decoded.length !== 1) return yield* corrupt(\"subscription scan cursors\");\n\n return {\n events: decoded[0].event_scan_cursor,\n deliveries: decoded[0].delivery_scan_cursor,\n recovery: decoded[0].recovery_scan_cursor,\n };\n });\n\n const advanceScanCursors: SubscriptionStore[\"Service\"][\"advanceScanCursors\"] = Effect.fn(\n \"SqliteSubscriptionStore.advanceScanCursors\",\n )(function* (input) {\n const cursors = yield* validate(SubscriptionScanCursors, input, \"scan-cursors\");\n\n yield* transact(\n Effect.gen(function* () {\n yield* failpoint.hit(\"subscription:advance-scan-cursors:before\");\n yield* query(\n sql<Record<string, unknown>>`\n UPDATE effect_agent_subscription_sequences\n SET event_scan_cursor=${cursors.events}, delivery_scan_cursor=${cursors.deliveries}, recovery_scan_cursor=${cursors.recovery}\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n `,\n \"advance subscription scan cursors\",\n );\n }),\n );\n yield* failpoint.hit(\"subscription:advance-scan-cursors:after\");\n });\n\n const indexedDeadline = query(\n sql<Record<string, unknown>>`\n SELECT MIN(deadline) AS deadline FROM (\n SELECT next_attempt_at_millis AS deadline FROM effect_agent_subscription_events\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND routing_complete=0\n UNION ALL SELECT next_attempt_at_millis FROM effect_agent_subscription_deliveries\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND state NOT IN ('delivered','refused')\n UNION ALL SELECT recovery_at_millis FROM effect_agent_subscriptions\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND state='active' AND recovery_at_millis IS NOT NULL\n )\n `,\n \"next subscription deadline\",\n ).pipe(\n Effect.flatMap((rows) =>\n decodeRows(\n Schema.Struct({ deadline: Schema.NullOr(Schema.Number) }),\n rows,\n \"next subscription deadline\",\n ),\n ),\n Effect.flatMap((rows) =>\n rows.length === 1\n ? Effect.succeed(rows[0].deadline)\n : Effect.fail(corrupt(\"next subscription deadline\")),\n ),\n );\n\n const nextDeadline = Effect.gen(function* () {\n const cursors = yield* readScanCursors;\n\n if (cursors.events !== \"\" || cursors.deliveries !== \"\" || cursors.recovery !== 0) return 0;\n\n return yield* indexedDeadline;\n });\n\n return SubscriptionStore.of({\n partition,\n register,\n get,\n list,\n cancel,\n accept,\n event,\n pendingEvents,\n candidates,\n select,\n catchUp,\n deferEvent,\n delivery,\n pendingDeliveries,\n listDeliveries,\n changeDelivery,\n recovering,\n deferRecovery,\n readScanCursors,\n advanceScanCursors,\n nextDeadline,\n });\n});\n\nexport const subscriptionStoreLayer = (\n partition: SourcePartition,\n): Layer.Layer<\n SubscriptionStore,\n SqliteStorageInitializationError | SubscriptionError,\n SqliteStorageConfig | SqliteStorageFailpoint | SqlClientService.SqlClient\n> => Layer.effect(SubscriptionStore, makeSubscriptionStore(partition));\n"],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { SqliteStorageConfig } from "./SqliteStorageConfig.mjs";
|
|
2
|
+
import { a as SqliteStorageCompatibilityError, o as SqliteStorageCorruptionError, s as SqliteStorageError } from "./SqliteStorageError-W1oM5ka3.mjs";
|
|
3
|
+
import { SqliteStorageFailpoint, SqliteStorageFailpointHandler } from "./SqliteStorageFailpoint.mjs";
|
|
4
|
+
import { Crypto, Layer } from "effect";
|
|
5
|
+
import * as SqlClientService from "effect/unstable/sql/SqlClient";
|
|
6
|
+
import { ThreadStore } from "@effect-agent/thread/ThreadStore";
|
|
7
|
+
declare namespace SqliteThreadStore_d_exports {
|
|
8
|
+
export { SqliteStorageInitializationError, SqliteStorageOptions, layer, storageConfigLayer, storageFailpointLayer, threadStoreLayer };
|
|
9
|
+
}
|
|
10
|
+
interface SqliteStorageOptions {
|
|
11
|
+
readonly filename: string;
|
|
12
|
+
readonly observationPollInterval?: number | undefined;
|
|
13
|
+
/** Bounded SQLITE_BUSY retry window for write-lock acquisition, in milliseconds. */
|
|
14
|
+
readonly busyTimeout?: number | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Submission ownership lease duration in milliseconds (D5). Defaults to
|
|
17
|
+
* `DEFAULT_OWNERSHIP_LEASE_DURATION` from `@effect-agent/thread`.
|
|
18
|
+
*/
|
|
19
|
+
readonly ownershipLeaseDuration?: number | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Re-verify every stored payload and digest chain while opening the store. Defaults to
|
|
22
|
+
* off: per-operation Schema decoding and the digest chain already fail clearly on corrupt
|
|
23
|
+
* rows without scanning the whole database on every open.
|
|
24
|
+
*/
|
|
25
|
+
readonly verifyOnOpen?: boolean | undefined;
|
|
26
|
+
readonly failpoint?: SqliteStorageFailpointHandler | undefined;
|
|
27
|
+
}
|
|
28
|
+
type SqliteStorageInitializationError = SqliteStorageCompatibilityError | SqliteStorageCorruptionError | SqliteStorageError;
|
|
29
|
+
/**
|
|
30
|
+
* SQLite Thread Store implementation with configuration, failpoint, SQL, and Crypto
|
|
31
|
+
* authority kept visible in its input channel.
|
|
32
|
+
*/
|
|
33
|
+
declare const threadStoreLayer: Layer.Layer<ThreadStore, SqliteStorageInitializationError, SqliteStorageConfig | SqliteStorageFailpoint | SqlClientService.SqlClient | Crypto.Crypto>;
|
|
34
|
+
/**
|
|
35
|
+
* Validated SQLite storage configuration Layer with the documented defaults applied. Shared
|
|
36
|
+
* by the ThreadStore and SubmissionLedger convenience layers so their defaults cannot
|
|
37
|
+
* drift.
|
|
38
|
+
*/
|
|
39
|
+
declare const storageConfigLayer: (options: SqliteStorageOptions) => Layer.Layer<SqliteStorageConfig, SqliteStorageError>;
|
|
40
|
+
/** The failpoint Layer selected by convenience options: explicit handler or the no-op default. */
|
|
41
|
+
declare const storageFailpointLayer: (options: SqliteStorageOptions) => Layer.Layer<SqliteStorageFailpoint>;
|
|
42
|
+
/**
|
|
43
|
+
* A composition-root convenience Layer for canonical Threads. Durable accepted work is
|
|
44
|
+
* served by the separate SubmissionLedger port.
|
|
45
|
+
*/
|
|
46
|
+
declare const layer: (options: SqliteStorageOptions) => Layer.Layer<ThreadStore, SqliteStorageInitializationError>;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { SqliteStorageInitializationError, SqliteStorageOptions, layer, storageConfigLayer, storageFailpointLayer, SqliteThreadStore_d_exports as t, threadStoreLayer };
|
|
49
|
+
//# sourceMappingURL=SqliteThreadStore.d.mts.map
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { SqliteStorageConfig, SqliteStorageConfigValue } from "./SqliteStorageConfig.mjs";
|
|
3
|
+
import { SqliteAppendConflict, SqliteCheckpointConflict, SqliteFenceRejected, SqliteStorageCorruptionError, SqliteStorageError } from "./SqliteStorageError.mjs";
|
|
4
|
+
import { SqliteStorageFailpoint } from "./SqliteStorageFailpoint.mjs";
|
|
5
|
+
import { a as initializeSqliteJournal, n as RawCheckpoint, r as RawReadRequest, t as RawAppendRequest } from "./sqlite-journal-C5SZnSdC.mjs";
|
|
6
|
+
import { CanonicalBatch, CanonicalRecord, CanonicalRecordEnvelope, CanonicalSequence, Digest, ObservationOffset } from "@effect-agent/thread/Records";
|
|
7
|
+
import { Clock, Context, Crypto, Duration, Effect, Layer, Option, Ref, Schema, Stream } from "effect";
|
|
8
|
+
import { SqliteClient } from "@effect/sql-sqlite-node";
|
|
9
|
+
import { EMPTY_TAIL_DIGEST, digestCanonicalBatch } from "@effect-agent/thread/Digest";
|
|
10
|
+
import { DEFAULT_OWNERSHIP_LEASE_DURATION } from "@effect-agent/thread/SubmissionLedger";
|
|
11
|
+
import { NodeCrypto } from "@effect/platform-node";
|
|
12
|
+
import { AppendConflict, AppendResult, CheckpointRejected, FenceRejected, FencedAppendRequest, LoadCheckpointRequest, SaveCheckpointRequest, ThreadCheckpoint, ThreadExport, ThreadExportRequest, ThreadMaterialization, ThreadNotMaterialized, ThreadObservation, ThreadRead, ThreadStore, ThreadStoreError, ThreadTail, ThreadTailRequest } from "@effect-agent/thread/ThreadStore";
|
|
13
|
+
//#region src/SqliteThreadStore.ts
|
|
14
|
+
var SqliteThreadStore_exports = /* @__PURE__ */ __exportAll({
|
|
15
|
+
layer: () => layer,
|
|
16
|
+
storageConfigLayer: () => storageConfigLayer,
|
|
17
|
+
storageFailpointLayer: () => storageFailpointLayer,
|
|
18
|
+
threadStoreLayer: () => threadStoreLayer
|
|
19
|
+
});
|
|
20
|
+
const OffsetText = Schema.String.check(Schema.isMaxLength(4096));
|
|
21
|
+
const SQLITE_OFFSET_PREFIX = "effect-agent-sqlite@1:";
|
|
22
|
+
const ZERO_CANONICAL_SEQUENCE = Schema.decodeSync(CanonicalSequence)(0);
|
|
23
|
+
const isDigest = Schema.is(Digest);
|
|
24
|
+
const isSqliteFenceRejected = Schema.is(SqliteFenceRejected);
|
|
25
|
+
const isSqliteAppendConflict = Schema.is(SqliteAppendConflict);
|
|
26
|
+
const isSqliteCheckpointConflict = Schema.is(SqliteCheckpointConflict);
|
|
27
|
+
const storeError = (operation, error) => ThreadStoreError.make({
|
|
28
|
+
cause: error,
|
|
29
|
+
operation,
|
|
30
|
+
message: error.message
|
|
31
|
+
});
|
|
32
|
+
const schemaStoreError = (operation, error) => ThreadStoreError.make({
|
|
33
|
+
cause: error,
|
|
34
|
+
operation,
|
|
35
|
+
message: error.message
|
|
36
|
+
});
|
|
37
|
+
const makeOffset = Effect.fn("SqliteThreadStore.makeOffset")(function* (threadId, sequence) {
|
|
38
|
+
return yield* Schema.decodeUnknownEffect(CanonicalSequence)(sequence).pipe(Effect.flatMap((validatedSequence) => Schema.decodeUnknownEffect(ObservationOffset)(`${SQLITE_OFFSET_PREFIX}${encodeURIComponent(threadId)}:${validatedSequence}`)), Effect.mapError((error) => schemaStoreError("encode observation offset", error)));
|
|
39
|
+
});
|
|
40
|
+
const parseOffset = Effect.fn("SqliteThreadStore.parseOffset")(function* (threadId, offset) {
|
|
41
|
+
if (offset === void 0) return ZERO_CANONICAL_SEQUENCE;
|
|
42
|
+
const text = yield* Schema.decodeUnknownEffect(OffsetText)(offset).pipe(Effect.mapError((error) => schemaStoreError("decode observation offset", error)));
|
|
43
|
+
const threadPrefix = `${SQLITE_OFFSET_PREFIX}${encodeURIComponent(threadId)}:`;
|
|
44
|
+
if (!text.startsWith(threadPrefix)) return yield* ThreadStoreError.make({
|
|
45
|
+
operation: "decode observation offset",
|
|
46
|
+
message: "The observation offset belongs to a different adapter, storage version, or Thread."
|
|
47
|
+
});
|
|
48
|
+
const sequenceText = text.slice(threadPrefix.length);
|
|
49
|
+
if (!/^(0|[1-9][0-9]*)$/.test(sequenceText)) return yield* ThreadStoreError.make({
|
|
50
|
+
operation: "decode observation offset",
|
|
51
|
+
message: "The observation offset is malformed."
|
|
52
|
+
});
|
|
53
|
+
return yield* Schema.decodeUnknownEffect(CanonicalSequence)(Number(sequenceText)).pipe(Effect.mapError((error) => schemaStoreError("decode observation offset", error)));
|
|
54
|
+
});
|
|
55
|
+
const mapFence = (threadId, error) => FenceRejected.make({
|
|
56
|
+
threadId,
|
|
57
|
+
actualEpoch: error.actualEpoch,
|
|
58
|
+
attemptedEpoch: error.producerEpoch
|
|
59
|
+
});
|
|
60
|
+
const encodeCanonicalRecord = Effect.fn("SqliteThreadStore.encodeCanonicalRecord")(function* (record) {
|
|
61
|
+
return yield* Schema.encodeEffect(Schema.fromJsonString(CanonicalRecord))(record).pipe(Effect.mapError((error) => schemaStoreError("encode canonical record", error)));
|
|
62
|
+
});
|
|
63
|
+
const encodeCanonicalBatch = Effect.fn("SqliteThreadStore.encodeCanonicalBatch")(function* (batch) {
|
|
64
|
+
return yield* Schema.encodeEffect(Schema.fromJsonString(CanonicalBatch))(batch).pipe(Effect.mapError((error) => schemaStoreError("encode canonical batch", error)));
|
|
65
|
+
});
|
|
66
|
+
const encodeCheckpoint = Effect.fn("SqliteThreadStore.encodeCheckpoint")(function* (checkpoint) {
|
|
67
|
+
return yield* Schema.encodeEffect(Schema.fromJsonString(ThreadCheckpoint))(checkpoint).pipe(Effect.mapError((error) => schemaStoreError("encode checkpoint", error)));
|
|
68
|
+
});
|
|
69
|
+
const decodeEnvelope = Effect.fn("SqliteThreadStore.decodeEnvelope")(function* (row) {
|
|
70
|
+
const record = yield* Schema.decodeEffect(Schema.fromJsonString(CanonicalRecord))(row.record_json).pipe(Effect.mapError((error) => ThreadStoreError.make({
|
|
71
|
+
operation: "decode canonical record",
|
|
72
|
+
message: error.message
|
|
73
|
+
})));
|
|
74
|
+
const threadId = yield* Schema.decodeUnknownEffect(CanonicalRecordEnvelope.fields.threadId)(row.thread_id).pipe(Effect.mapError((error) => schemaStoreError("decode thread identity", error)));
|
|
75
|
+
const offset = yield* makeOffset(threadId, row.sequence);
|
|
76
|
+
const batchId = yield* Schema.decodeUnknownEffect(CanonicalRecordEnvelope.fields.batchId)(row.batch_id).pipe(Effect.mapError((error) => schemaStoreError("decode batch identity", error)));
|
|
77
|
+
return CanonicalRecordEnvelope.make({
|
|
78
|
+
threadId,
|
|
79
|
+
batchId,
|
|
80
|
+
sequence: row.sequence,
|
|
81
|
+
offset,
|
|
82
|
+
record
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
const decodeCheckpoint = Effect.fn("SqliteThreadStore.decodeCheckpoint")(function* (checkpointJson) {
|
|
86
|
+
return yield* Schema.decodeEffect(Schema.fromJsonString(ThreadCheckpoint))(checkpointJson).pipe(Effect.mapError((error) => schemaStoreError("decode checkpoint", error)));
|
|
87
|
+
});
|
|
88
|
+
const requireThread = Effect.fn("SqliteThreadStore.requireThread")(function* (journal, threadId) {
|
|
89
|
+
const rows = yield* journal.getThread(threadId).pipe(Effect.mapError((error) => storeError("read thread", error)));
|
|
90
|
+
if (rows.length === 0) return yield* ThreadNotMaterialized.make({ threadId });
|
|
91
|
+
return rows[0];
|
|
92
|
+
});
|
|
93
|
+
const tailDigestAt = Effect.fn("SqliteThreadStore.tailDigestAt")(function* (journal, threadId, sequence) {
|
|
94
|
+
if (sequence === 0) return EMPTY_TAIL_DIGEST;
|
|
95
|
+
const digests = yield* journal.getTailDigestAt(threadId, sequence).pipe(Effect.mapError((error) => storeError("read checkpoint digest", error)));
|
|
96
|
+
if (digests.length !== 1) return yield* CheckpointRejected.make({
|
|
97
|
+
threadId,
|
|
98
|
+
reason: "digest-mismatch"
|
|
99
|
+
});
|
|
100
|
+
return yield* Schema.decodeUnknownEffect(Digest)(digests[0]).pipe(Effect.mapError((error) => schemaStoreError("decode checkpoint digest", error)));
|
|
101
|
+
});
|
|
102
|
+
const groupByKey = (rows, key) => {
|
|
103
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
104
|
+
for (const row of rows) {
|
|
105
|
+
const existing = grouped.get(key(row));
|
|
106
|
+
if (existing === void 0) grouped.set(key(row), [row]);
|
|
107
|
+
else existing.push(row);
|
|
108
|
+
}
|
|
109
|
+
return grouped;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Opt-in full integrity audit (`verifyOnOpen`). Every stored payload is decoded, re-encoded,
|
|
113
|
+
* and re-digested against the canonical chain. Routine opens skip this scan: per-operation
|
|
114
|
+
* Schema decoding plus the digest chain already fail clearly on corrupt rows.
|
|
115
|
+
*/
|
|
116
|
+
const decodeStartupPayloads = Effect.fn("SqliteThreadStore.decodeStartupPayloads")(function* (journal, crypto) {
|
|
117
|
+
const stored = yield* journal.scanStoredPayloads();
|
|
118
|
+
const batches = yield* Effect.forEach(stored.batches, (batch) => Schema.decodeEffect(Schema.fromJsonString(CanonicalBatch))(batch.batch_json).pipe(Effect.map((decoded) => ({
|
|
119
|
+
decoded,
|
|
120
|
+
row: batch
|
|
121
|
+
})), Effect.mapError((error) => SqliteStorageCorruptionError.make({
|
|
122
|
+
table: "effect_agent_canonical_batches",
|
|
123
|
+
rowKey: `${batch.thread_id}/${batch.batch_id}`,
|
|
124
|
+
message: error.message
|
|
125
|
+
}))));
|
|
126
|
+
const records = yield* Effect.forEach(stored.records, (record) => Schema.decodeEffect(Schema.fromJsonString(CanonicalRecord))(record.record_json).pipe(Effect.map((decoded) => ({
|
|
127
|
+
decoded,
|
|
128
|
+
row: record
|
|
129
|
+
})), Effect.mapError((error) => SqliteStorageCorruptionError.make({
|
|
130
|
+
table: "effect_agent_canonical_records",
|
|
131
|
+
rowKey: `${record.thread_id}/${record.sequence}`,
|
|
132
|
+
message: error.message
|
|
133
|
+
}))));
|
|
134
|
+
const checkpoints = yield* Effect.forEach(stored.checkpoints, (checkpoint) => Schema.decodeEffect(Schema.fromJsonString(ThreadCheckpoint))(checkpoint.checkpoint_json).pipe(Effect.map((decoded) => ({
|
|
135
|
+
decoded,
|
|
136
|
+
row: checkpoint
|
|
137
|
+
})), Effect.mapError((error) => SqliteStorageCorruptionError.make({
|
|
138
|
+
table: "effect_agent_checkpoints",
|
|
139
|
+
rowKey: `${checkpoint.thread_id}/${checkpoint.through_sequence}`,
|
|
140
|
+
message: error.message
|
|
141
|
+
}))));
|
|
142
|
+
const batchesByThread = groupByKey(batches, ({ row }) => row.thread_id);
|
|
143
|
+
const recordsByThread = groupByKey(records, ({ row }) => row.thread_id);
|
|
144
|
+
const checkpointsByThread = groupByKey(checkpoints, ({ row }) => row.thread_id);
|
|
145
|
+
const materializedIds = new Set(stored.threads.map((thread) => thread.thread_id));
|
|
146
|
+
for (const thread of stored.threads) {
|
|
147
|
+
const threadBatches = batchesByThread.get(thread.thread_id) ?? [];
|
|
148
|
+
const threadRecords = recordsByThread.get(thread.thread_id) ?? [];
|
|
149
|
+
const threadCheckpoints = checkpointsByThread.get(thread.thread_id) ?? [];
|
|
150
|
+
const recordsByBatch = groupByKey(threadRecords, ({ row }) => row.batch_id);
|
|
151
|
+
let previousDigest = EMPTY_TAIL_DIGEST;
|
|
152
|
+
let expectedSequence = 1;
|
|
153
|
+
const tailDigests = /* @__PURE__ */ new Map([[0, EMPTY_TAIL_DIGEST]]);
|
|
154
|
+
for (const { decoded: canonicalBatch, row: batchRow } of threadBatches) {
|
|
155
|
+
const key = `${batchRow.thread_id}/${batchRow.batch_id}`;
|
|
156
|
+
if (canonicalBatch.batchId !== batchRow.batch_id || batchRow.first_sequence !== expectedSequence || batchRow.last_sequence !== batchRow.first_sequence + canonicalBatch.records.length - 1) return yield* SqliteStorageCorruptionError.make({
|
|
157
|
+
table: "effect_agent_canonical_batches",
|
|
158
|
+
rowKey: key,
|
|
159
|
+
message: "Canonical batch identity, sequence, or record count is inconsistent."
|
|
160
|
+
});
|
|
161
|
+
const digest = yield* digestCanonicalBatch(previousDigest, canonicalBatch).pipe(Effect.provideService(Crypto.Crypto, crypto), Effect.mapError((error) => SqliteStorageCorruptionError.make({
|
|
162
|
+
table: "effect_agent_canonical_batches",
|
|
163
|
+
rowKey: key,
|
|
164
|
+
message: error.message
|
|
165
|
+
})));
|
|
166
|
+
if (batchRow.batch_digest !== digest || batchRow.tail_digest !== digest) return yield* SqliteStorageCorruptionError.make({
|
|
167
|
+
table: "effect_agent_canonical_batches",
|
|
168
|
+
rowKey: key,
|
|
169
|
+
message: "Canonical batch digest does not match its decoded content and prior tail."
|
|
170
|
+
});
|
|
171
|
+
const batchRecords = recordsByBatch.get(batchRow.batch_id) ?? [];
|
|
172
|
+
if (batchRecords.length !== canonicalBatch.records.length) return yield* SqliteStorageCorruptionError.make({
|
|
173
|
+
table: "effect_agent_canonical_records",
|
|
174
|
+
rowKey: key,
|
|
175
|
+
message: "Canonical batch and record-table counts differ."
|
|
176
|
+
});
|
|
177
|
+
for (let index = 0; index < canonicalBatch.records.length; index++) {
|
|
178
|
+
const expectedRecord = canonicalBatch.records[index];
|
|
179
|
+
const storedRecord = batchRecords[index];
|
|
180
|
+
const expectedJson = yield* Schema.encodeEffect(Schema.fromJsonString(CanonicalRecord))(expectedRecord).pipe(Effect.mapError((error) => SqliteStorageCorruptionError.make({
|
|
181
|
+
table: "effect_agent_canonical_batches",
|
|
182
|
+
rowKey: key,
|
|
183
|
+
message: error.message
|
|
184
|
+
})));
|
|
185
|
+
const storedJson = yield* Schema.encodeEffect(Schema.fromJsonString(CanonicalRecord))(storedRecord.decoded).pipe(Effect.mapError((error) => SqliteStorageCorruptionError.make({
|
|
186
|
+
table: "effect_agent_canonical_records",
|
|
187
|
+
rowKey: `${key}/${storedRecord.row.sequence}`,
|
|
188
|
+
message: error.message
|
|
189
|
+
})));
|
|
190
|
+
if (storedRecord.row.sequence !== batchRow.first_sequence + index || storedRecord.row.record_id !== expectedRecord.recordId || expectedJson !== storedJson) return yield* SqliteStorageCorruptionError.make({
|
|
191
|
+
table: "effect_agent_canonical_records",
|
|
192
|
+
rowKey: `${key}/${storedRecord.row.sequence}`,
|
|
193
|
+
message: "Canonical record identity, sequence, or payload differs from its batch."
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
previousDigest = digest;
|
|
197
|
+
expectedSequence = batchRow.last_sequence + 1;
|
|
198
|
+
tailDigests.set(batchRow.last_sequence, digest);
|
|
199
|
+
}
|
|
200
|
+
if (threadRecords.length !== thread.tail_sequence || thread.tail_sequence !== expectedSequence - 1 || thread.tail_digest !== previousDigest) return yield* SqliteStorageCorruptionError.make({
|
|
201
|
+
table: "effect_agent_threads",
|
|
202
|
+
rowKey: thread.thread_id,
|
|
203
|
+
message: "Thread tail does not match its canonical batch chain."
|
|
204
|
+
});
|
|
205
|
+
for (const checkpoint of threadCheckpoints) if (checkpoint.decoded.threadId !== thread.thread_id || checkpoint.decoded.throughSequence !== checkpoint.row.through_sequence || checkpoint.decoded.tailDigest !== checkpoint.row.tail_digest || tailDigests.get(checkpoint.row.through_sequence) !== checkpoint.row.tail_digest) return yield* SqliteStorageCorruptionError.make({
|
|
206
|
+
table: "effect_agent_checkpoints",
|
|
207
|
+
rowKey: `${thread.thread_id}/${checkpoint.row.through_sequence}`,
|
|
208
|
+
message: "Checkpoint identity or digest is not bound to a canonical batch tail."
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (batches.some(({ row }) => !materializedIds.has(row.thread_id)) || records.some(({ row }) => !materializedIds.has(row.thread_id)) || checkpoints.some(({ row }) => !materializedIds.has(row.thread_id))) return yield* SqliteStorageCorruptionError.make({
|
|
212
|
+
table: "effect_agent_threads",
|
|
213
|
+
rowKey: "startup_scan",
|
|
214
|
+
message: "Canonical rows exist without a materialized Thread."
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
const makeServices = Effect.fn("SqliteThreadStore.makeServices")(function* () {
|
|
218
|
+
const config = yield* SqliteStorageConfig;
|
|
219
|
+
const failpoint = yield* SqliteStorageFailpoint;
|
|
220
|
+
const crypto = yield* Crypto.Crypto;
|
|
221
|
+
const journal = yield* initializeSqliteJournal();
|
|
222
|
+
if (config.verifyOnOpen) yield* decodeStartupPayloads(journal, crypto);
|
|
223
|
+
const provideCrypto = (effect) => Effect.provideService(effect, Crypto.Crypto, crypto);
|
|
224
|
+
const hitFailpoint = Effect.fn("SqliteThreadStore.hitFailpoint")((location) => failpoint.hit(location).pipe(Effect.mapError((error) => storeError(`storage failpoint ${location}`, error))));
|
|
225
|
+
const materialize = Effect.fn("SqliteThreadStore.materialize")(function* (request) {
|
|
226
|
+
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ThreadMaterialization))(request).pipe(Effect.mapError((error) => schemaStoreError("validate materialization", error)));
|
|
227
|
+
const now = yield* Clock.currentTimeMillis;
|
|
228
|
+
yield* hitFailpoint("materialize:before");
|
|
229
|
+
yield* journal.materialize(validated.threadId, new Date(now).toISOString(), EMPTY_TAIL_DIGEST, validated.producerEpoch).pipe(Effect.mapError((error) => error._tag === "SqliteFenceRejected" ? mapFence(validated.threadId, error) : storeError("materialize thread", error)));
|
|
230
|
+
yield* hitFailpoint("materialize:after");
|
|
231
|
+
});
|
|
232
|
+
const append = Effect.fn("SqliteThreadStore.append")(function* (request) {
|
|
233
|
+
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(FencedAppendRequest))(request).pipe(Effect.mapError((error) => schemaStoreError("validate canonical append", error)));
|
|
234
|
+
yield* requireThread(journal, validated.threadId);
|
|
235
|
+
const tailDigest = yield* provideCrypto(digestCanonicalBatch(validated.expectedTailDigest, validated.batch)).pipe(Effect.mapError((error) => storeError("digest canonical append", error)));
|
|
236
|
+
const batchJson = yield* encodeCanonicalBatch(validated.batch);
|
|
237
|
+
const rawRecords = yield* Effect.forEach(validated.batch.records, (record) => encodeCanonicalRecord(record).pipe(Effect.map((recordJson) => ({
|
|
238
|
+
recordId: record.recordId,
|
|
239
|
+
recordJson
|
|
240
|
+
}))));
|
|
241
|
+
const rawRequest = yield* Schema.decodeUnknownEffect(RawAppendRequest)({
|
|
242
|
+
threadId: validated.threadId,
|
|
243
|
+
batchId: validated.batch.batchId,
|
|
244
|
+
batchDigest: tailDigest,
|
|
245
|
+
batchJson,
|
|
246
|
+
expectedTailSequence: validated.expectedTailSequence,
|
|
247
|
+
expectedTailDigest: validated.expectedTailDigest,
|
|
248
|
+
producerEpoch: validated.producerEpoch,
|
|
249
|
+
records: rawRecords,
|
|
250
|
+
tailDigest
|
|
251
|
+
}).pipe(Effect.mapError((error) => schemaStoreError("encode canonical append", error)));
|
|
252
|
+
yield* hitFailpoint("append:before");
|
|
253
|
+
const result = yield* journal.append(rawRequest).pipe(Effect.mapError((error) => {
|
|
254
|
+
if (isSqliteFenceRejected(error)) return mapFence(validated.threadId, error);
|
|
255
|
+
if (isSqliteAppendConflict(error)) return error.actualTailSequence !== void 0 && isDigest(error.actualTailDigest) ? AppendConflict.make({
|
|
256
|
+
threadId: validated.threadId,
|
|
257
|
+
batchId: validated.batch.batchId,
|
|
258
|
+
reason: error.reason,
|
|
259
|
+
actualTailSequence: error.actualTailSequence,
|
|
260
|
+
actualTailDigest: error.actualTailDigest
|
|
261
|
+
}) : AppendConflict.make({
|
|
262
|
+
threadId: validated.threadId,
|
|
263
|
+
batchId: validated.batch.batchId,
|
|
264
|
+
reason: error.reason
|
|
265
|
+
});
|
|
266
|
+
return storeError("append canonical batch", error);
|
|
267
|
+
}), Effect.flatMap((result) => Schema.decodeUnknownEffect(AppendResult)(result).pipe(Effect.mapError((error) => schemaStoreError("decode append result", error)))));
|
|
268
|
+
yield* hitFailpoint("append:after");
|
|
269
|
+
return result;
|
|
270
|
+
});
|
|
271
|
+
const loadRecords = Effect.fn("SqliteThreadStore.loadRecords")(function* (request) {
|
|
272
|
+
const rows = yield* journal.read(request).pipe(Effect.mapError((error) => storeError("read canonical records", error)));
|
|
273
|
+
return yield* Effect.forEach(rows, decodeEnvelope);
|
|
274
|
+
});
|
|
275
|
+
const readEffect = Effect.fn("SqliteThreadStore.read")(function* (request) {
|
|
276
|
+
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ThreadRead))(request).pipe(Effect.mapError((error) => schemaStoreError("validate thread read", error)));
|
|
277
|
+
yield* requireThread(journal, validated.threadId);
|
|
278
|
+
const records = yield* loadRecords(RawReadRequest.make({
|
|
279
|
+
threadId: validated.threadId,
|
|
280
|
+
fromSequenceExclusive: validated.afterSequence ?? ZERO_CANONICAL_SEQUENCE,
|
|
281
|
+
limit: validated.limit
|
|
282
|
+
}));
|
|
283
|
+
return Stream.fromIterable(records);
|
|
284
|
+
});
|
|
285
|
+
const read = (request) => Stream.unwrap(readEffect(request));
|
|
286
|
+
const observeEffect = Effect.fn("SqliteThreadStore.observe")(function* (request) {
|
|
287
|
+
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ThreadObservation))(request).pipe(Effect.mapError((error) => schemaStoreError("validate thread observation", error)));
|
|
288
|
+
yield* requireThread(journal, validated.threadId);
|
|
289
|
+
const initialSequence = yield* parseOffset(validated.threadId, validated.afterOffset);
|
|
290
|
+
const cursor = yield* Ref.make(initialSequence);
|
|
291
|
+
const poll = Effect.fn("SqliteThreadStore.observePoll")(function* () {
|
|
292
|
+
const fromSequenceExclusive = yield* Ref.get(cursor);
|
|
293
|
+
const records = yield* loadRecords(RawReadRequest.make({
|
|
294
|
+
threadId: validated.threadId,
|
|
295
|
+
fromSequenceExclusive,
|
|
296
|
+
limit: 1024
|
|
297
|
+
}));
|
|
298
|
+
if (records.length === 0) {
|
|
299
|
+
yield* Effect.sleep(config.observationPollInterval);
|
|
300
|
+
return [];
|
|
301
|
+
}
|
|
302
|
+
yield* Ref.set(cursor, records[records.length - 1].sequence);
|
|
303
|
+
return records;
|
|
304
|
+
});
|
|
305
|
+
return Stream.fromIterableEffectRepeat(poll());
|
|
306
|
+
});
|
|
307
|
+
const observe = (request) => Stream.unwrap(observeEffect(request));
|
|
308
|
+
const exportThread = Effect.fn("SqliteThreadStore.export")(function* (request) {
|
|
309
|
+
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ThreadExportRequest))(request).pipe(Effect.mapError((error) => schemaStoreError("validate thread export", error)));
|
|
310
|
+
yield* requireThread(journal, validated.threadId);
|
|
311
|
+
const exported = yield* journal.exportThread(validated.threadId).pipe(Effect.mapError((error) => storeError("export thread", error)));
|
|
312
|
+
const records = yield* Effect.forEach(exported.records, decodeEnvelope);
|
|
313
|
+
if (records.length > 65536) return yield* ThreadStoreError.make({
|
|
314
|
+
operation: "decode thread export",
|
|
315
|
+
message: "The thread exceeds the current export record limit."
|
|
316
|
+
});
|
|
317
|
+
const tailDigest = yield* Schema.decodeUnknownEffect(Digest)(exported.thread.tail_digest).pipe(Effect.mapError((error) => schemaStoreError("decode export tail digest", error)));
|
|
318
|
+
return ThreadExport.make({
|
|
319
|
+
format: "effect-agent/thread@1",
|
|
320
|
+
threadId: validated.threadId,
|
|
321
|
+
tailSequence: exported.thread.tail_sequence,
|
|
322
|
+
tailDigest,
|
|
323
|
+
records
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
const inspectTail = Effect.fn("SqliteThreadStore.inspectTail")(function* (request) {
|
|
327
|
+
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(ThreadTailRequest))(request).pipe(Effect.mapError((error) => schemaStoreError("validate tail inspection", error)));
|
|
328
|
+
const thread = yield* requireThread(journal, validated.threadId);
|
|
329
|
+
const tailDigest = yield* Schema.decodeUnknownEffect(Digest)(thread.tail_digest).pipe(Effect.mapError((error) => schemaStoreError("decode tail digest", error)));
|
|
330
|
+
return ThreadTail.make({
|
|
331
|
+
threadId: validated.threadId,
|
|
332
|
+
tailSequence: thread.tail_sequence,
|
|
333
|
+
tailDigest,
|
|
334
|
+
producerEpoch: thread.producer_epoch
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
const saveCheckpoint = Effect.fn("SqliteThreadStore.saveCheckpoint")(function* (request) {
|
|
338
|
+
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(SaveCheckpointRequest))(request).pipe(Effect.mapError((error) => schemaStoreError("validate checkpoint", error)));
|
|
339
|
+
const thread = yield* requireThread(journal, validated.checkpoint.threadId);
|
|
340
|
+
if (validated.checkpoint.throughSequence > thread.tail_sequence) return yield* CheckpointRejected.make({
|
|
341
|
+
threadId: validated.checkpoint.threadId,
|
|
342
|
+
reason: "ahead-of-tail"
|
|
343
|
+
});
|
|
344
|
+
if ((yield* tailDigestAt(journal, validated.checkpoint.threadId, validated.checkpoint.throughSequence)) !== validated.checkpoint.tailDigest) return yield* CheckpointRejected.make({
|
|
345
|
+
threadId: validated.checkpoint.threadId,
|
|
346
|
+
reason: "digest-mismatch"
|
|
347
|
+
});
|
|
348
|
+
const checkpointJson = yield* encodeCheckpoint(validated.checkpoint);
|
|
349
|
+
const raw = RawCheckpoint.make({
|
|
350
|
+
threadId: validated.checkpoint.threadId,
|
|
351
|
+
throughSequence: validated.checkpoint.throughSequence,
|
|
352
|
+
tailDigest: validated.checkpoint.tailDigest,
|
|
353
|
+
checkpointJson
|
|
354
|
+
});
|
|
355
|
+
yield* hitFailpoint("save-checkpoint:before");
|
|
356
|
+
yield* journal.saveCheckpoint(raw).pipe(Effect.mapError((error) => isSqliteCheckpointConflict(error) ? CheckpointRejected.make({
|
|
357
|
+
threadId: validated.checkpoint.threadId,
|
|
358
|
+
reason: "digest-mismatch"
|
|
359
|
+
}) : storeError("save checkpoint", error)));
|
|
360
|
+
yield* hitFailpoint("save-checkpoint:after");
|
|
361
|
+
});
|
|
362
|
+
const loadCheckpoint = Effect.fn("SqliteThreadStore.loadCheckpoint")(function* (request) {
|
|
363
|
+
const validated = yield* Schema.decodeUnknownEffect(Schema.toType(LoadCheckpointRequest))(request).pipe(Effect.mapError((error) => schemaStoreError("validate checkpoint lookup", error)));
|
|
364
|
+
const thread = yield* requireThread(journal, validated.threadId);
|
|
365
|
+
const rows = yield* journal.loadCheckpoint(validated.threadId, validated.atOrBeforeSequence ?? thread.tail_sequence).pipe(Effect.mapError((error) => storeError("load checkpoint", error)));
|
|
366
|
+
if (rows.length === 0) return Option.none();
|
|
367
|
+
if (rows.length !== 1) return yield* ThreadStoreError.make({
|
|
368
|
+
operation: "load checkpoint",
|
|
369
|
+
message: `Expected at most one checkpoint row but found ${rows.length}.`
|
|
370
|
+
});
|
|
371
|
+
const checkpoint = yield* decodeCheckpoint(rows[0].checkpoint_json);
|
|
372
|
+
if ((yield* tailDigestAt(journal, checkpoint.threadId, checkpoint.throughSequence)) !== checkpoint.tailDigest) return yield* CheckpointRejected.make({
|
|
373
|
+
threadId: checkpoint.threadId,
|
|
374
|
+
reason: "digest-mismatch"
|
|
375
|
+
});
|
|
376
|
+
return Option.some(checkpoint);
|
|
377
|
+
});
|
|
378
|
+
const threadStore = ThreadStore.of({
|
|
379
|
+
append,
|
|
380
|
+
export: exportThread,
|
|
381
|
+
inspectTail,
|
|
382
|
+
materialize,
|
|
383
|
+
observe,
|
|
384
|
+
read,
|
|
385
|
+
checkpoints: {
|
|
386
|
+
save: saveCheckpoint,
|
|
387
|
+
load: loadCheckpoint
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
return Context.make(ThreadStore, threadStore);
|
|
391
|
+
});
|
|
392
|
+
/**
|
|
393
|
+
* SQLite Thread Store implementation with configuration, failpoint, SQL, and Crypto
|
|
394
|
+
* authority kept visible in its input channel.
|
|
395
|
+
*/
|
|
396
|
+
const threadStoreLayer = Layer.effectContext(makeServices());
|
|
397
|
+
/**
|
|
398
|
+
* Validated SQLite storage configuration Layer with the documented defaults applied. Shared
|
|
399
|
+
* by the ThreadStore and SubmissionLedger convenience layers so their defaults cannot
|
|
400
|
+
* drift.
|
|
401
|
+
*/
|
|
402
|
+
const storageConfigLayer = (options) => Layer.effect(SqliteStorageConfig)(Schema.decodeUnknownEffect(SqliteStorageConfigValue)({
|
|
403
|
+
observationPollInterval: options.observationPollInterval ?? 25,
|
|
404
|
+
busyTimeout: options.busyTimeout ?? 5e3,
|
|
405
|
+
ownershipLeaseDuration: options.ownershipLeaseDuration ?? Duration.toMillis(DEFAULT_OWNERSHIP_LEASE_DURATION),
|
|
406
|
+
verifyOnOpen: options.verifyOnOpen ?? false
|
|
407
|
+
}).pipe(Effect.mapError((error) => SqliteStorageError.make({
|
|
408
|
+
cause: error,
|
|
409
|
+
operation: "configure SQLite storage",
|
|
410
|
+
message: error.message
|
|
411
|
+
}))));
|
|
412
|
+
/** The failpoint Layer selected by convenience options: explicit handler or the no-op default. */
|
|
413
|
+
const storageFailpointLayer = (options) => options.failpoint === void 0 ? SqliteStorageFailpoint.layer : Layer.succeed(SqliteStorageFailpoint)({ hit: options.failpoint });
|
|
414
|
+
/**
|
|
415
|
+
* A composition-root convenience Layer for canonical Threads. Durable accepted work is
|
|
416
|
+
* served by the separate SubmissionLedger port.
|
|
417
|
+
*/
|
|
418
|
+
const layer = (options) => Layer.unwrap(Effect.map(SqliteStorageConfig, (config) => threadStoreLayer.pipe(Layer.provide(Layer.mergeAll(Layer.succeed(SqliteStorageConfig)(config), storageFailpointLayer(options), SqliteClient.layer({ filename: options.filename }), NodeCrypto.layer))))).pipe(Layer.provide(storageConfigLayer(options)));
|
|
419
|
+
//#endregion
|
|
420
|
+
export { layer, storageConfigLayer, storageFailpointLayer, SqliteThreadStore_exports as t, threadStoreLayer };
|
|
421
|
+
|
|
422
|
+
//# sourceMappingURL=SqliteThreadStore.mjs.map
|