@effect-agent/storage-memory 0.1.0-beta.8 → 0.1.0-beta.80
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/MemoryMessageDeliveryStore.d.mts +15 -0
- package/dist/MemoryMessageDeliveryStore.mjs +144 -0
- package/dist/MemoryMessageDeliveryStore.mjs.map +1 -0
- package/dist/MemoryScheduleStore.d.mts +10 -0
- package/dist/MemoryScheduleStore.mjs +169 -0
- package/dist/MemoryScheduleStore.mjs.map +1 -0
- package/dist/MemorySemanticIndex.d.mts +21 -0
- package/dist/MemorySemanticIndex.mjs +222 -0
- package/dist/MemorySemanticIndex.mjs.map +1 -0
- package/dist/MemorySubmissionLedger.d.mts +24 -0
- package/dist/MemorySubmissionLedger.mjs +1058 -0
- package/dist/MemorySubmissionLedger.mjs.map +1 -0
- package/dist/MemorySubscriptionStore.d.mts +9 -0
- package/dist/MemorySubscriptionStore.mjs +849 -0
- package/dist/MemorySubscriptionStore.mjs.map +1 -0
- package/dist/MemoryThreadStore.d.mts +17 -0
- package/dist/MemoryThreadStore.mjs +377 -0
- package/dist/MemoryThreadStore.mjs.map +1 -0
- package/dist/index.d.mts +7 -30
- package/dist/index.mjs +7 -1298
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/package.json +1 -45
- package/src/MemoryMessageDeliveryStore.ts +285 -0
- package/src/MemoryScheduleStore.ts +328 -0
- package/src/MemorySemanticIndex.ts +357 -0
- package/src/{memory-ledger.ts → MemorySubmissionLedger.ts} +478 -86
- package/src/MemorySubscriptionStore.ts +1549 -0
- package/src/MemoryThreadStore.ts +766 -0
- package/src/index.ts +6 -2
- package/dist/index.mjs.map +0 -1
- package/dist/testing.d.mts +0 -2
- package/dist/testing.mjs +0 -2
- package/src/memory-storage.ts +0 -614
- package/src/testing.ts +0 -10
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemorySubscriptionStore.mjs","names":[],"sources":["../src/MemorySubscriptionStore.ts"],"sourcesContent":["import { Digest } from \"@effect-agent/thread/Records\";\nimport { compareScheduleNames } from \"@effect-agent/thread/ScheduleTransition\";\nimport {\n AcceptedEvent,\n DeliveryChange,\n SubscriptionChange,\n SourcePartition,\n SubscriptionDelivery,\n SubscriptionDeliveryKey,\n SubscriptionError,\n SubscriptionFailpoint,\n SubscriptionKey,\n SubscriptionLimits,\n SubscriptionRetentionPolicy,\n SubscriptionName,\n SubscriptionRecord,\n SubscriptionScanCursors,\n SubscriptionStore,\n subscriptionDeliveryKeyString,\n subscriptionKeyString,\n} from \"@effect-agent/thread/Subscription\";\nimport {\n sameAcceptedEventIdentity,\n applySubscriptionDeliveryChange,\n applySubscriptionChange,\n validateEventRetention,\n subscriptionCanSelect,\n subscriptionDeliveryCanSelect,\n} from \"@effect-agent/thread/SubscriptionTransition\";\nimport { Clock, Effect, Layer, Ref, Result, Schema } from \"effect\";\n\ninterface MemorySubscriptionState {\n readonly sequence: number;\n readonly retentionDeadline: number | null;\n readonly tombstoneCount: number;\n readonly eventKeys: ReadonlyArray<string>;\n readonly deliveryKeys: ReadonlyArray<string>;\n readonly maintenanceEvents: string;\n readonly maintenanceDeliveries: string;\n readonly recoveryCounts: ReadonlyMap<string, number>;\n readonly eventDeliveryCounts: ReadonlyMap<string, number>;\n readonly retentionHorizon: number | null;\n readonly registrations: ReadonlyMap<string, string>;\n readonly events: ReadonlyMap<string, string>;\n readonly deliveries: ReadonlyMap<string, string>;\n readonly registrationIndex: ReadonlyMap<string, RegistrationIndex>;\n readonly candidateIndex: ReadonlyMap<string, ReadonlyArray<string>>;\n readonly ownerRegistrationCounts: ReadonlyMap<string, number>;\n readonly eventIndex: ReadonlyMap<string, EventIndex>;\n readonly deliveryIndex: ReadonlyMap<string, DeliveryIndex>;\n readonly ownerDeliveryCounts: ReadonlyMap<string, number>;\n readonly scanCursors: SubscriptionScanCursors;\n}\n\ninterface RegistrationIndex {\n readonly key: SubscriptionKey;\n readonly ordinal: number;\n readonly state: SubscriptionRecord[\"state\"];\n readonly recoveryKey: string | null;\n readonly recoveryAt: number | null;\n}\ninterface EventIndex {\n readonly routingComplete: boolean;\n readonly nextAttemptAtMillis: number;\n}\ninterface DeliveryIndex {\n readonly key: SubscriptionDeliveryKey;\n readonly state: SubscriptionDelivery[\"state\"];\n readonly parked?: boolean;\n readonly observeSettlement?: boolean;\n readonly nextAttemptAtMillis: number;\n}\n\nconst error = (reason: SubscriptionError[\"reason\"], code: string) =>\n SubscriptionError.make({ reason, code });\n\nconst samePartition = (left: SourcePartition, right: SourcePartition): boolean =>\n left.tenantId === right.tenantId && left.address === right.address;\n\nconst sameSource = (left: AcceptedEvent[\"source\"], right: AcceptedEvent[\"source\"]): boolean =>\n left.name === right.name && left.version === right.version;\n\nconst encode = <A, I>(\n schema: Schema.Codec<A, I>,\n value: A,\n code: string,\n): Result.Result<string, SubscriptionError> =>\n Result.try({\n try: () => Schema.encodeSync(Schema.fromJsonString(schema))(value),\n catch: () => error(\"corrupt\", code),\n });\n\nconst decode = <A, I>(\n schema: Schema.Codec<A, I>,\n value: string,\n code: string,\n): Result.Result<A, SubscriptionError> =>\n Result.try({\n try: () => Schema.decodeSync(Schema.fromJsonString(schema))(value),\n catch: () => error(\"corrupt\", code),\n });\n\nconst decodeEffect = <A, I>(schema: Schema.Codec<A, I>, value: string, code: string) =>\n Effect.fromResult(decode(schema, value, code));\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 jsonBytes = (value: unknown): number =>\n new TextEncoder().encode(JSON.stringify(value)).byteLength;\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 candidateIndexKey = (record: SubscriptionRecord): string =>\n JSON.stringify([\n record.configuration.source.name,\n record.configuration.source.version,\n record.configuration.matchingKey,\n ]);\n\nconst eventCandidateIndexKey = (event: AcceptedEvent): string =>\n JSON.stringify([event.source.name, event.source.version, event.matchingKey]);\n\nconst sameEventIdentity = sameAcceptedEventIdentity;\n\nconst deliveryBelongsTo = (\n delivery: SubscriptionDelivery,\n record: SubscriptionRecord,\n event: AcceptedEvent,\n): boolean =>\n subscriptionKeyString(delivery.key.subscription) === subscriptionKeyString(record.key) &&\n delivery.key.eventId === event.eventId &&\n sameSource(delivery.source, event.source);\n\n// Ordered in-memory indexes allow bounded maintenance pages without scanning retained values.\nconst removeKeys = (\n keys: ReadonlyArray<string>,\n removed: ReadonlySet<string>,\n): ReadonlyArray<string> => {\n if (removed.size === 0) return keys;\n const result = [...keys];\n\n for (const key of removed) {\n const index = upperBound(result, key) - 1;\n\n if (result[index] === key) result.splice(index, 1);\n }\n\n return result;\n};\n\nconst upperBound = (keys: ReadonlyArray<string>, after: string): number => {\n let low = 0;\n let high = keys.length;\n\n while (low < high) {\n const middle = Math.floor((low + high) / 2);\n const key = keys[middle];\n\n if (key !== undefined && compareScheduleNames(key, after) <= 0) low = middle + 1;\n else high = middle;\n }\n\n return low;\n};\n\nconst insertKey = (keys: ReadonlyArray<string>, key: string): ReadonlyArray<string> => {\n const at = upperBound(keys, key);\n\n if (at > 0 && keys[at - 1] === key) return keys;\n\n return [...keys.slice(0, at), key, ...keys.slice(at)];\n};\n\nconst recoveryCountsAfter = (\n current: MemorySubscriptionState,\n next: ReadonlyMap<string, RegistrationIndex>,\n keys: ReadonlyArray<string>,\n): ReadonlyMap<string, number> => {\n const counts = new Map(current.recoveryCounts);\n\n for (const key of keys) {\n const before = current.registrationIndex.get(key)?.recoveryKey;\n const after = next.get(key)?.recoveryKey;\n\n if (before === after) continue;\n if (before !== null && before !== undefined) counts.set(before, (counts.get(before) ?? 0) - 1);\n if (after !== null && after !== undefined) counts.set(after, (counts.get(after) ?? 0) + 1);\n }\n\n return counts;\n};\n\nconst makeMemorySubscriptionStore = Effect.fn(\"makeMemorySubscriptionStore\")(function* (\n ownedPartition: SourcePartition,\n) {\n const partition = yield* validate(SourcePartition, ownedPartition, \"partition\");\n\n const state = yield* Ref.make<MemorySubscriptionState>({\n sequence: 0,\n retentionDeadline: null,\n tombstoneCount: 0,\n eventKeys: [],\n deliveryKeys: [],\n maintenanceEvents: \"\",\n maintenanceDeliveries: \"\",\n recoveryCounts: new Map(),\n eventDeliveryCounts: new Map(),\n retentionHorizon: null,\n registrations: new Map(),\n events: new Map(),\n deliveries: new Map(),\n registrationIndex: new Map(),\n candidateIndex: new Map(),\n ownerRegistrationCounts: new Map(),\n eventIndex: new Map(),\n deliveryIndex: new Map(),\n ownerDeliveryCounts: new Map(),\n scanCursors: { events: \"\", deliveries: \"\", recovery: 0 },\n });\n\n const failpoint = yield* SubscriptionFailpoint;\n\n const requirePartition = <A extends { readonly partition: SourcePartition }>(\n value: A,\n code: string,\n ) =>\n samePartition(value.partition, partition)\n ? Effect.succeed(value)\n : Effect.fail(error(\"validation\", code));\n\n const requireKey = (key: SubscriptionKey, code: string) =>\n validate(SubscriptionKey, key, code).pipe(\n Effect.flatMap((decoded) => requirePartition(decoded, code)),\n );\n\n const register: SubscriptionStore[\"Service\"][\"register\"] = Effect.fn(\n \"MemorySubscriptionStore.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, \"register-partition\");\n yield* failpoint.hit(\"subscription:register:before\");\n\n const result = yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (\n current,\n ): readonly [\n Result.Result<SubscriptionRecord, SubscriptionError>,\n MemorySubscriptionState,\n ] => {\n const key = subscriptionKeyString(record.key);\n const existingText = current.registrations.get(key);\n\n if (existingText !== undefined) {\n const existing = decode(SubscriptionRecord, existingText, \"register-existing\");\n\n if (Result.isFailure(existing)) return [Result.fail(existing.failure), current];\n\n return existing.success.creationFingerprint === record.creationFingerprint\n ? [Result.succeed(existing.success), current]\n : [Result.fail(error(\"conflict\", \"registration-identity\")), current];\n }\n if (jsonBytes(record.configuration.context) > limits.maxContextBytes)\n return [Result.fail(error(\"capacity\", \"context-bytes\")), current];\n if (jsonBytes(record.configuration.parameters) > limits.maxPayloadBytes)\n return [Result.fail(error(\"capacity\", \"parameters-bytes\")), current];\n if (\n record.configuration.expiresAtMillis !== null &&\n record.configuration.expiresAtMillis - record.createdAtMillis > limits.maxLifetimeMillis\n )\n return [Result.fail(error(\"capacity\", \"lifetime\")), current];\n if (current.registrations.size >= limits.maxRegistrations)\n return [Result.fail(error(\"capacity\", \"registrations\")), current];\n const ownerCount = current.ownerRegistrationCounts.get(record.key.ownerId) ?? 0;\n\n if (ownerCount >= limits.maxRegistrationsPerOwner)\n return [Result.fail(error(\"capacity\", \"owner-registrations\")), current];\n const assigned = { ...record, ordinal: current.sequence + 1 };\n const encoded = encode(SubscriptionRecord, assigned, \"register-encode\");\n\n if (Result.isFailure(encoded)) return [Result.fail(encoded.failure), current];\n const registrations = new Map(current.registrations);\n\n registrations.set(key, encoded.success);\n const registrationIndex = new Map(current.registrationIndex);\n\n registrationIndex.set(key, {\n key: assigned.key,\n ordinal: assigned.ordinal,\n state: assigned.state,\n recoveryKey: assigned.recovery === null ? null : candidateIndexKey(assigned),\n recoveryAt: assigned.recovery?.nextAttemptAtMillis ?? null,\n });\n const candidateIndex = new Map(current.candidateIndex);\n const candidateKey = candidateIndexKey(assigned);\n\n candidateIndex.set(candidateKey, [...(candidateIndex.get(candidateKey) ?? []), key]);\n const ownerRegistrationCounts = new Map(current.ownerRegistrationCounts);\n\n ownerRegistrationCounts.set(assigned.key.ownerId, ownerCount + 1);\n\n return [\n Result.succeed(assigned),\n {\n ...current,\n sequence: assigned.ordinal,\n registrations,\n registrationIndex,\n recoveryCounts: recoveryCountsAfter(current, registrationIndex, [key]),\n candidateIndex,\n ownerRegistrationCounts,\n },\n ];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n\n yield* failpoint.hit(\"subscription:register:after\");\n\n return result;\n });\n\n const get: SubscriptionStore[\"Service\"][\"get\"] = Effect.fn(\"MemorySubscriptionStore.get\")(\n function* (input) {\n const key = yield* requireKey(input, \"get-key\");\n const text = (yield* Ref.get(state)).registrations.get(subscriptionKeyString(key));\n\n return text === undefined\n ? null\n : yield* decodeEffect(SubscriptionRecord, text, \"get-record\");\n },\n );\n\n const list: SubscriptionStore[\"Service\"][\"list\"] = Effect.fn(\"MemorySubscriptionStore.list\")(\n function* (ownerId, after, limit) {\n if (!Number.isSafeInteger(after) || after < 0 || !Number.isSafeInteger(limit) || limit <= 0)\n return yield* error(\"validation\", \"list-page\");\n const current = yield* Ref.get(state);\n const records: Array<SubscriptionRecord> = [];\n\n for (const [storageKey, indexed] of current.registrationIndex) {\n if (indexed.key.ownerId !== ownerId || indexed.ordinal <= after) continue;\n const text = current.registrations.get(storageKey);\n\n if (text === undefined) return yield* error(\"corrupt\", \"list-index\");\n records.push(yield* decodeEffect(SubscriptionRecord, text, \"list-record\"));\n }\n records.sort((a, b) => a.ordinal - b.ordinal);\n\n return records.slice(0, limit);\n },\n );\n\n const change: SubscriptionStore[\"Service\"][\"change\"] = Effect.fn(\n \"MemorySubscriptionStore.change\",\n )(function* (input, expectedRevision, inputChange) {\n const key = yield* requireKey(input, \"change-key\");\n const change = yield* validate(SubscriptionChange, inputChange, \"change\");\n\n yield* validate(Schema.Int.check(Schema.isGreaterThan(0)), expectedRevision, \"revision\");\n yield* failpoint.hit(\"subscription:change:before\");\n\n const updated = yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (\n current,\n ): readonly [\n Result.Result<SubscriptionRecord, SubscriptionError>,\n MemorySubscriptionState,\n ] => {\n const storageKey = subscriptionKeyString(key);\n const text = current.registrations.get(storageKey);\n\n if (text === undefined) return [Result.fail(error(\"not-found\", \"subscription\")), current];\n const existing = decode(SubscriptionRecord, text, \"change-record\");\n\n if (Result.isFailure(existing)) return [existing, current];\n const updated = applySubscriptionChange(existing.success, expectedRevision, change);\n\n if (Result.isFailure(updated)) return [updated, current];\n const revised = { ...updated.success, ordinal: current.sequence + 1 };\n const encoded = encode(SubscriptionRecord, revised, \"change-encode\");\n\n if (Result.isFailure(encoded)) return [Result.fail(encoded.failure), current];\n const registrations = new Map(current.registrations);\n\n registrations.set(storageKey, encoded.success);\n const registrationIndex = new Map(current.registrationIndex);\n\n registrationIndex.set(storageKey, {\n key,\n ordinal: revised.ordinal,\n state: revised.state,\n recoveryKey: revised.recovery === null ? null : candidateIndexKey(revised),\n recoveryAt: revised.recovery?.nextAttemptAtMillis ?? null,\n });\n const candidateIndex = new Map(current.candidateIndex);\n const oldKey = candidateIndexKey(existing.success);\n const nextKey = candidateIndexKey(revised);\n\n {\n candidateIndex.set(\n oldKey,\n (candidateIndex.get(oldKey) ?? []).filter((key) => key !== storageKey),\n );\n candidateIndex.set(\n nextKey,\n [...(candidateIndex.get(nextKey) ?? []), storageKey].sort(\n (a, b) =>\n (registrationIndex.get(a)?.ordinal ?? 0) -\n (registrationIndex.get(b)?.ordinal ?? 0),\n ),\n );\n }\n\n return [\n Result.succeed(revised),\n {\n ...current,\n sequence: revised.ordinal,\n registrations,\n registrationIndex,\n candidateIndex,\n recoveryCounts: recoveryCountsAfter(current, registrationIndex, [storageKey]),\n },\n ];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n\n yield* failpoint.hit(\"subscription:change:after\");\n\n return updated;\n });\n\n const cancel: SubscriptionStore[\"Service\"][\"cancel\"] = Effect.fn(\n \"MemorySubscriptionStore.cancel\",\n )(function* (input, expectedRevision) {\n const key = yield* requireKey(input, \"cancel-key\");\n\n yield* failpoint.hit(\"subscription:cancel:before\");\n\n const result = yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (\n current,\n ): readonly [\n Result.Result<SubscriptionRecord, SubscriptionError>,\n MemorySubscriptionState,\n ] => {\n const storageKey = subscriptionKeyString(key);\n const text = current.registrations.get(storageKey);\n\n if (text === undefined) return [Result.fail(error(\"not-found\", \"subscription\")), current];\n const decoded = decode(SubscriptionRecord, text, \"cancel-record\");\n\n if (Result.isFailure(decoded)) return [decoded, current];\n if (\n expectedRevision !== undefined &&\n expectedRevision !== decoded.success.configurationRevision &&\n !(\n decoded.success.state === \"cancelled\" &&\n expectedRevision + 1 === decoded.success.configurationRevision\n )\n )\n return [\n Result.fail(\n SubscriptionError.make({\n reason: \"conflict\",\n code: \"configuration-revision\",\n currentRevision: decoded.success.configurationRevision,\n currentState: decoded.success.state,\n }),\n ),\n current,\n ];\n if (decoded.success.state === \"cancelled\")\n return [Result.succeed(decoded.success), current];\n\n const cancelled = {\n ...decoded.success,\n configurationRevision: decoded.success.configurationRevision + 1,\n state: \"cancelled\" as const,\n recovery: null,\n };\n\n const encoded = encode(SubscriptionRecord, cancelled, \"cancel-encode\");\n\n if (Result.isFailure(encoded)) return [Result.fail(encoded.failure), current];\n const registrations = new Map(current.registrations);\n\n registrations.set(storageKey, encoded.success);\n const registrationIndex = new Map(current.registrationIndex);\n const indexed = registrationIndex.get(storageKey);\n\n if (indexed === undefined)\n return [Result.fail(error(\"corrupt\", \"cancel-index\")), current];\n registrationIndex.set(storageKey, {\n ...indexed,\n state: \"cancelled\",\n recoveryAt: null,\n recoveryKey: null,\n });\n\n return [\n Result.succeed(cancelled),\n {\n ...current,\n registrations,\n registrationIndex,\n recoveryCounts: recoveryCountsAfter(current, registrationIndex, [storageKey]),\n },\n ];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n\n yield* failpoint.hit(\"subscription:cancel:after\");\n\n return result;\n });\n\n const accept: SubscriptionStore[\"Service\"][\"accept\"] = Effect.fn(\n \"MemorySubscriptionStore.accept\",\n )(function* (input, inputLimits) {\n const event = yield* validate(AcceptedEvent, input, \"accept-event\");\n const currentTimeMillis = yield* Clock.currentTimeMillis;\n const limits = yield* validate(SubscriptionLimits, inputLimits, \"accept-limits\");\n\n yield* requirePartition(event, \"accept-partition\");\n yield* failpoint.hit(\"subscription:accept:before\");\n\n const result = yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (\n current,\n ): readonly [Result.Result<AcceptedEvent, SubscriptionError>, MemorySubscriptionState] => {\n const existingText = current.events.get(event.eventId);\n\n if (existingText !== undefined) {\n const existing = decode(AcceptedEvent, existingText, \"accept-existing\");\n\n if (Result.isFailure(existing)) return [Result.fail(existing.failure), current];\n\n return sameEventIdentity(existing.success, event)\n ? [Result.succeed(existing.success), current]\n : [Result.fail(error(\"conflict\", \"event-identity\")), current];\n }\n if (\n current.retentionHorizon !== null &&\n current.retentionHorizon !== limits.retention?.replayHorizonMillis\n )\n return [Result.fail(error(\"conflict\", \"retention-horizon\")), current];\n const horizon = validateEventRetention(event, limits, currentTimeMillis);\n\n if (Result.isFailure(horizon)) return [Result.fail(horizon.failure), current];\n if (jsonBytes(event.payload) > limits.maxPayloadBytes)\n return [Result.fail(error(\"capacity\", \"payload-bytes\")), current];\n if (current.events.size - current.tombstoneCount >= limits.maxEvents)\n return [Result.fail(error(\"capacity\", \"events\")), current];\n\n const accepted: AcceptedEvent = {\n ...event,\n cutoff: current.sequence + 1,\n cursor: 0,\n routingComplete: false,\n routingFailure: null,\n };\n\n const encoded = encode(AcceptedEvent, accepted, \"accept-encode\");\n\n if (Result.isFailure(encoded)) return [Result.fail(encoded.failure), current];\n const events = new Map(current.events);\n\n events.set(accepted.eventId, encoded.success);\n const eventIndex = new Map(current.eventIndex);\n\n eventIndex.set(accepted.eventId, {\n routingComplete: false,\n nextAttemptAtMillis: accepted.nextAttemptAtMillis,\n });\n\n return [\n Result.succeed(accepted),\n {\n ...current,\n sequence: accepted.cutoff,\n events,\n eventIndex,\n eventKeys: insertKey(current.eventKeys, accepted.eventId),\n retentionHorizon: limits.retention?.replayHorizonMillis ?? current.retentionHorizon,\n retentionDeadline:\n limits.retention === undefined\n ? current.retentionDeadline\n : accepted.acceptedAtMillis,\n },\n ];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n\n yield* failpoint.hit(\"subscription:accept:after\");\n\n return result;\n });\n\n const event: SubscriptionStore[\"Service\"][\"event\"] = Effect.fn(\"MemorySubscriptionStore.event\")(\n function* (eventId) {\n const text = (yield* Ref.get(state)).events.get(eventId);\n\n return text === undefined ? null : yield* decodeEffect(AcceptedEvent, text, \"event-record\");\n },\n );\n\n const pendingEvents: SubscriptionStore[\"Service\"][\"pendingEvents\"] = Effect.fn(\n \"MemorySubscriptionStore.pendingEvents\",\n )(function* (nowMillis, after, limit) {\n const events: Array<string> = [];\n\n for (const [eventId, indexed] of (yield* Ref.get(state)).eventIndex) {\n if (\n !indexed.routingComplete &&\n indexed.nextAttemptAtMillis <= nowMillis &&\n compareScheduleNames(eventId, after) > 0\n )\n events.push(eventId);\n }\n events.sort(compareScheduleNames);\n\n return events.slice(0, limit);\n });\n\n const candidates: SubscriptionStore[\"Service\"][\"candidates\"] = Effect.fn(\n \"MemorySubscriptionStore.candidates\",\n )(function* (input, limit) {\n const accepted = yield* validate(AcceptedEvent, input, \"candidates-event\");\n\n yield* requirePartition(accepted, \"candidates-partition\");\n const stored = yield* event(accepted.eventId);\n\n if (stored === null || !sameEventIdentity(stored, accepted))\n return yield* error(stored === null ? \"not-found\" : \"conflict\", \"event\");\n const current = yield* Ref.get(state);\n const records: Array<SubscriptionRecord> = [];\n\n for (const storageKey of current.candidateIndex.get(eventCandidateIndexKey(stored)) ?? []) {\n const indexed = current.registrationIndex.get(storageKey);\n\n if (indexed === undefined) return yield* error(\"corrupt\", \"candidate-index\");\n if (indexed.ordinal <= stored.cursor || indexed.ordinal > stored.cutoff) continue;\n const text = current.registrations.get(storageKey);\n\n if (text === undefined) return yield* error(\"corrupt\", \"candidate-record\");\n records.push(yield* decodeEffect(SubscriptionRecord, text, \"candidate-record\"));\n }\n records.sort((a, b) => a.ordinal - b.ordinal);\n\n return records.slice(0, limit);\n });\n\n const select: SubscriptionStore[\"Service\"][\"select\"] = Effect.fn(\n \"MemorySubscriptionStore.select\",\n )(function* (inputEvent, inputDeliveries, cursor, complete, nowMillis, inputLimits) {\n const suppliedEvent = 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(suppliedEvent, \"select-partition\");\n yield* failpoint.hit(\"subscription:select:before\");\n const effectiveNowMillis = Math.max(nowMillis, yield* Clock.currentTimeMillis);\n\n yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (current): readonly [Result.Result<void, SubscriptionError>, MemorySubscriptionState] => {\n const eventText = current.events.get(suppliedEvent.eventId);\n\n if (eventText === undefined) return [Result.fail(error(\"not-found\", \"event\")), current];\n const decodedEvent = decode(AcceptedEvent, eventText, \"select-event-record\");\n\n if (Result.isFailure(decodedEvent)) return [Result.fail(decodedEvent.failure), current];\n const accepted = decodedEvent.success;\n\n if (\n !sameEventIdentity(accepted, suppliedEvent) ||\n suppliedEvent.cursor !== accepted.cursor\n )\n return [Result.fail(error(\"conflict\", \"event-cursor\")), current];\n if (accepted.routingComplete)\n return [\n complete && cursor === accepted.cursor\n ? Result.void\n : Result.fail(error(\"conflict\", \"routing-complete\")),\n current,\n ];\n if (!Number.isSafeInteger(cursor) || cursor < accepted.cursor || cursor > accepted.cutoff)\n return [Result.fail(error(\"validation\", \"cursor\")), current];\n\n const additions: Array<readonly [string, string]> = [];\n const updates: Array<readonly [string, string]> = [];\n const additionIndex: Array<readonly [string, DeliveryIndex, string]> = [];\n const registrationUpdates: Array<readonly [string, RegistrationIndex]> = [];\n const owners = new Map(current.ownerDeliveryCounts);\n\n for (const delivery of deliveries) {\n const recordText = current.registrations.get(\n subscriptionKeyString(delivery.key.subscription),\n );\n\n if (recordText === undefined)\n return [Result.fail(error(\"not-found\", \"subscription\")), current];\n const record = decode(SubscriptionRecord, recordText, \"select-registration\");\n\n if (Result.isFailure(record)) return [Result.fail(record.failure), current];\n if (\n !deliveryBelongsTo(delivery, record.success, accepted) ||\n !subscriptionDeliveryCanSelect(delivery, record.success, accepted) ||\n record.success.ordinal <= accepted.cursor ||\n record.success.ordinal > cursor\n )\n return [Result.fail(error(\"conflict\", \"selection\")), current];\n if (!subscriptionCanSelect(record.success, accepted, effectiveNowMillis, false))\n continue;\n const deliveryKey = subscriptionDeliveryKeyString(delivery.key);\n const existingText = current.deliveries.get(deliveryKey);\n\n if (existingText !== undefined) {\n const existing = decode(SubscriptionDelivery, existingText, \"select-existing\");\n\n if (Result.isFailure(existing)) return [Result.fail(existing.failure), current];\n if (!sameDeliveryIdentity(existing.success, delivery))\n return [Result.fail(error(\"conflict\", \"delivery-identity\")), current];\n continue;\n }\n\n const encodedDelivery = encode(\n SubscriptionDelivery,\n delivery,\n \"select-delivery-encode\",\n );\n\n if (Result.isFailure(encodedDelivery))\n return [Result.fail(encodedDelivery.failure), current];\n additions.push([deliveryKey, encodedDelivery.success]);\n additionIndex.push([\n deliveryKey,\n {\n key: delivery.key,\n state: delivery.state,\n nextAttemptAtMillis: delivery.retry.nextAttemptAtMillis,\n },\n record.success.key.ownerId,\n ]);\n const ownerId = record.success.key.ownerId;\n\n owners.set(ownerId, (owners.get(ownerId) ?? 0) + 1);\n if ((owners.get(ownerId) ?? 0) > limits.maxDeliveriesPerOwner)\n return [Result.fail(error(\"capacity\", \"owner-deliveries\")), current];\n if (record.success.configuration.mode === \"once\") {\n const consumed = { ...record.success, state: \"consumed\" as const, recovery: null };\n\n const encodedRecord = encode(\n SubscriptionRecord,\n consumed,\n \"select-registration-encode\",\n );\n\n if (Result.isFailure(encodedRecord))\n return [Result.fail(encodedRecord.failure), current];\n const consumedKey = subscriptionKeyString(consumed.key);\n\n updates.push([consumedKey, encodedRecord.success]);\n const indexed = current.registrationIndex.get(consumedKey);\n\n if (indexed === undefined)\n return [Result.fail(error(\"corrupt\", \"selection-index\")), current];\n registrationUpdates.push([\n consumedKey,\n { ...indexed, state: \"consumed\", recoveryAt: null, recoveryKey: null },\n ]);\n }\n }\n if (current.deliveries.size + additions.length > limits.maxDeliveries)\n return [Result.fail(error(\"capacity\", \"deliveries\")), current];\n const registrations = new Map(current.registrations);\n\n for (const [key, value] of updates) registrations.set(key, value);\n const nextDeliveries = new Map(current.deliveries);\n\n for (const [key, value] of additions) nextDeliveries.set(key, value);\n const deliveryIndex = new Map(current.deliveryIndex);\n\n for (const [key, value] of additionIndex) deliveryIndex.set(key, value);\n const registrationIndex = new Map(current.registrationIndex);\n\n for (const [key, value] of registrationUpdates) registrationIndex.set(key, value);\n\n const eventDeliveryCounts = new Map(current.eventDeliveryCounts);\n\n eventDeliveryCounts.set(\n accepted.eventId,\n (eventDeliveryCounts.get(accepted.eventId) ?? 0) + additions.length,\n );\n\n const nextEvent: AcceptedEvent = {\n ...accepted,\n cursor,\n routingComplete: complete,\n routingFailure: null,\n };\n\n const encodedEvent = encode(AcceptedEvent, nextEvent, \"select-event-encode\");\n\n if (Result.isFailure(encodedEvent)) return [Result.fail(encodedEvent.failure), current];\n const events = new Map(current.events);\n\n events.set(nextEvent.eventId, encodedEvent.success);\n const eventIndex = new Map(current.eventIndex);\n\n eventIndex.set(nextEvent.eventId, {\n routingComplete: nextEvent.routingComplete,\n nextAttemptAtMillis: nextEvent.nextAttemptAtMillis,\n });\n\n return [\n Result.void,\n {\n ...current,\n registrations,\n registrationIndex,\n recoveryCounts: recoveryCountsAfter(\n current,\n registrationIndex,\n registrationUpdates.map(([key]) => key),\n ),\n eventDeliveryCounts,\n events,\n eventIndex,\n deliveries: nextDeliveries,\n deliveryKeys: additions.reduce(\n (keys, [key]) => insertKey(keys, key),\n current.deliveryKeys,\n ),\n deliveryIndex,\n ownerDeliveryCounts: owners,\n },\n ];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n yield* failpoint.hit(\"subscription:select:after\");\n });\n\n const catchUp: SubscriptionStore[\"Service\"][\"catchUp\"] = Effect.fn(\n \"MemorySubscriptionStore.catchUp\",\n )(function* (inputEvent, inputDelivery, nowMillis, inputLimits) {\n const suppliedEvent = 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(suppliedEvent, \"catch-up-partition\");\n yield* failpoint.hit(\"subscription:catch-up:before\");\n const effectiveNowMillis = Math.max(nowMillis, yield* Clock.currentTimeMillis);\n\n yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (current): readonly [Result.Result<void, SubscriptionError>, MemorySubscriptionState] => {\n const eventText = current.events.get(suppliedEvent.eventId);\n\n const recordText = current.registrations.get(\n subscriptionKeyString(delivery.key.subscription),\n );\n\n if (eventText === undefined || recordText === undefined)\n return [\n Result.fail(error(\"not-found\", eventText === undefined ? \"event\" : \"subscription\")),\n current,\n ];\n const accepted = decode(AcceptedEvent, eventText, \"catch-up-event-record\");\n\n if (Result.isFailure(accepted)) return [Result.fail(accepted.failure), current];\n const record = decode(SubscriptionRecord, recordText, \"catch-up-registration\");\n\n if (Result.isFailure(record)) return [Result.fail(record.failure), current];\n if (\n !sameEventIdentity(accepted.success, suppliedEvent) ||\n !deliveryBelongsTo(delivery, record.success, accepted.success) ||\n !subscriptionDeliveryCanSelect(delivery, record.success, accepted.success) ||\n record.success.configuration.mode !== \"once\"\n )\n return [Result.fail(error(\"conflict\", \"catch-up-identity\")), current];\n const key = subscriptionDeliveryKeyString(delivery.key);\n const existingText = current.deliveries.get(key);\n\n if (existingText !== undefined) {\n const existing = decode(SubscriptionDelivery, existingText, \"catch-up-existing\");\n\n if (Result.isFailure(existing)) return [Result.fail(existing.failure), current];\n\n return sameDeliveryIdentity(existing.success, delivery)\n ? [Result.void, current]\n : [Result.fail(error(\"conflict\", \"delivery-identity\")), current];\n }\n if (!subscriptionCanSelect(record.success, accepted.success, effectiveNowMillis, true))\n return [Result.fail(error(\"conflict\", \"catch-up-eligibility\")), current];\n if (current.deliveries.size >= limits.maxDeliveries)\n return [Result.fail(error(\"capacity\", \"deliveries\")), current];\n const ownerCount = current.ownerDeliveryCounts.get(record.success.key.ownerId) ?? 0;\n\n if (ownerCount >= limits.maxDeliveriesPerOwner)\n return [Result.fail(error(\"capacity\", \"owner-deliveries\")), current];\n\n const encodedDelivery = encode(\n SubscriptionDelivery,\n delivery,\n \"catch-up-delivery-encode\",\n );\n\n if (Result.isFailure(encodedDelivery))\n return [Result.fail(encodedDelivery.failure), current];\n const consumed = { ...record.success, state: \"consumed\" as const, recovery: null };\n\n const encodedRecord = encode(\n SubscriptionRecord,\n consumed,\n \"catch-up-registration-encode\",\n );\n\n if (Result.isFailure(encodedRecord)) return [Result.fail(encodedRecord.failure), current];\n const deliveries = new Map(current.deliveries);\n\n deliveries.set(key, encodedDelivery.success);\n const registrations = new Map(current.registrations);\n const consumedKey = subscriptionKeyString(consumed.key);\n\n registrations.set(consumedKey, encodedRecord.success);\n const registrationIndex = new Map(current.registrationIndex);\n const indexed = registrationIndex.get(consumedKey);\n\n if (indexed === undefined)\n return [Result.fail(error(\"corrupt\", \"catch-up-index\")), current];\n registrationIndex.set(consumedKey, {\n ...indexed,\n state: \"consumed\",\n recoveryAt: null,\n recoveryKey: null,\n });\n const deliveryIndex = new Map(current.deliveryIndex);\n\n deliveryIndex.set(key, {\n key: delivery.key,\n state: delivery.state,\n nextAttemptAtMillis: delivery.retry.nextAttemptAtMillis,\n });\n const ownerDeliveryCounts = new Map(current.ownerDeliveryCounts);\n const eventDeliveryCounts = new Map(current.eventDeliveryCounts);\n\n eventDeliveryCounts.set(\n accepted.success.eventId,\n (eventDeliveryCounts.get(accepted.success.eventId) ?? 0) + 1,\n );\n\n ownerDeliveryCounts.set(consumed.key.ownerId, ownerCount + 1);\n\n return [\n Result.void,\n {\n ...current,\n deliveries,\n deliveryIndex,\n deliveryKeys: insertKey(current.deliveryKeys, key),\n ownerDeliveryCounts,\n registrations,\n registrationIndex,\n recoveryCounts: recoveryCountsAfter(current, registrationIndex, [consumedKey]),\n eventDeliveryCounts,\n },\n ];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n yield* failpoint.hit(\"subscription:catch-up:after\");\n });\n\n const deferEvent: SubscriptionStore[\"Service\"][\"deferEvent\"] = Effect.fn(\n \"MemorySubscriptionStore.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* failpoint.hit(\"subscription:defer-event:before\");\n yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (current): readonly [Result.Result<void, SubscriptionError>, MemorySubscriptionState] => {\n const text = current.events.get(eventId);\n\n if (text === undefined) return [Result.fail(error(\"not-found\", \"event\")), current];\n const accepted = decode(AcceptedEvent, text, \"defer-event-record\");\n\n if (Result.isFailure(accepted)) return [Result.fail(accepted.failure), current];\n const updated = { ...accepted.success, nextAttemptAtMillis, routingFailure };\n const encoded = encode(AcceptedEvent, updated, \"defer-event-encode\");\n\n if (Result.isFailure(encoded)) return [Result.fail(encoded.failure), current];\n const events = new Map(current.events);\n\n events.set(eventId, encoded.success);\n const eventIndex = new Map(current.eventIndex);\n const indexed = eventIndex.get(eventId);\n\n if (indexed === undefined)\n return [Result.fail(error(\"corrupt\", \"defer-event-index\")), current];\n eventIndex.set(eventId, { ...indexed, nextAttemptAtMillis });\n\n return [Result.void, { ...current, events, eventIndex }];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n yield* failpoint.hit(\"subscription:defer-event:after\");\n });\n\n const delivery: SubscriptionStore[\"Service\"][\"delivery\"] = Effect.fn(\n \"MemorySubscriptionStore.delivery\",\n )(function* (input) {\n const key = yield* validate(SubscriptionDeliveryKey, input, \"delivery-key\");\n\n yield* requirePartition(key.subscription, \"delivery-partition\");\n const text = (yield* Ref.get(state)).deliveries.get(subscriptionDeliveryKeyString(key));\n\n return text === undefined\n ? null\n : yield* decodeEffect(SubscriptionDelivery, text, \"delivery-record\");\n });\n\n const pendingDeliveries: SubscriptionStore[\"Service\"][\"pendingDeliveries\"] = Effect.fn(\n \"MemorySubscriptionStore.pendingDeliveries\",\n )(function* (nowMillis, after, limit) {\n const items: Array<SubscriptionDeliveryKey> = [];\n\n for (const [storageKey, item] of (yield* Ref.get(state)).deliveryIndex) {\n if (\n ((item.state !== \"delivered\" && item.state !== \"refused\" && item.parked !== true) ||\n (item.state === \"delivered\" && item.observeSettlement === true)) &&\n item.nextAttemptAtMillis <= nowMillis &&\n compareScheduleNames(storageKey, after) > 0\n )\n items.push(item.key);\n }\n items.sort((a, b) =>\n compareScheduleNames(subscriptionDeliveryKeyString(a), subscriptionDeliveryKeyString(b)),\n );\n\n return items.slice(0, limit);\n });\n\n const listDeliveries: SubscriptionStore[\"Service\"][\"listDeliveries\"] = Effect.fn(\n \"MemorySubscriptionStore.listDeliveries\",\n )(function* (input, after, limit) {\n const key = yield* requireKey(input, \"list-deliveries-key\");\n const items: Array<SubscriptionDelivery> = [];\n\n for (const text of (yield* Ref.get(state)).deliveries.values()) {\n const item = yield* decodeEffect(SubscriptionDelivery, text, \"list-delivery\");\n const itemKey = subscriptionDeliveryKeyString(item.key);\n\n if (\n subscriptionKeyString(item.key.subscription) === subscriptionKeyString(key) &&\n compareScheduleNames(itemKey, after) > 0\n )\n items.push(item);\n }\n items.sort((a, b) =>\n compareScheduleNames(\n subscriptionDeliveryKeyString(a.key),\n subscriptionDeliveryKeyString(b.key),\n ),\n );\n\n return items.slice(0, limit);\n });\n\n const changeDelivery: SubscriptionStore[\"Service\"][\"changeDelivery\"] = Effect.fn(\n \"MemorySubscriptionStore.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, \"change-delivery-partition\");\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 result = yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (\n current,\n ): readonly [\n Result.Result<SubscriptionDelivery, SubscriptionError>,\n MemorySubscriptionState,\n ] => {\n const storageKey = subscriptionDeliveryKeyString(key);\n const text = current.deliveries.get(storageKey);\n\n if (text === undefined) return [Result.fail(error(\"not-found\", \"delivery\")), current];\n const decoded = decode(SubscriptionDelivery, text, \"change-delivery-record\");\n\n if (Result.isFailure(decoded)) return [decoded, current];\n\n const registrationText = current.registrations.get(\n subscriptionKeyString(key.subscription),\n );\n\n if (registrationText === undefined)\n return [Result.fail(error(\"corrupt\", \"delivery-registration\")), current];\n\n const registration = decode(\n SubscriptionRecord,\n registrationText,\n \"delivery-registration\",\n );\n\n if (Result.isFailure(registration)) return [Result.fail(registration.failure), current];\n\n const transition = applySubscriptionDeliveryChange(\n decoded.success,\n registration.success,\n deliveryId,\n effectiveChange,\n );\n\n if (Result.isFailure(transition)) return [Result.fail(transition.failure), current];\n if (transition.success === decoded.success)\n return [Result.succeed(decoded.success), current];\n const updated = transition.success;\n const encoded = encode(SubscriptionDelivery, updated, \"change-delivery-encode\");\n\n if (Result.isFailure(encoded)) return [Result.fail(encoded.failure), current];\n const deliveries = new Map(current.deliveries);\n\n deliveries.set(storageKey, encoded.success);\n const deliveryIndex = new Map(current.deliveryIndex);\n const indexed = deliveryIndex.get(storageKey);\n\n if (indexed === undefined)\n return [Result.fail(error(\"corrupt\", \"delivery-index\")), current];\n deliveryIndex.set(storageKey, {\n ...indexed,\n state: updated.state,\n parked: updated.retry.parked ?? false,\n observeSettlement: updated.observeSettlement ?? false,\n nextAttemptAtMillis: updated.retry.nextAttemptAtMillis,\n });\n\n return [Result.succeed(updated), { ...current, deliveries, deliveryIndex }];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n\n yield* failpoint.hit(`subscription:delivery-${change._tag.toLowerCase()}:after`);\n\n return result;\n });\n\n const recovering: SubscriptionStore[\"Service\"][\"recovering\"] = Effect.fn(\n \"MemorySubscriptionStore.recovering\",\n )(function* (nowMillis, after, limit) {\n const records: Array<{ readonly key: SubscriptionKey; readonly ordinal: number }> = [];\n\n for (const item of (yield* Ref.get(state)).registrationIndex.values()) {\n if (\n item.ordinal > after &&\n item.state === \"active\" &&\n item.recoveryAt !== null &&\n item.recoveryAt <= nowMillis\n )\n records.push({ key: item.key, ordinal: item.ordinal });\n }\n records.sort((a, b) => a.ordinal - b.ordinal);\n\n return records.slice(0, limit);\n });\n\n const deferRecovery: SubscriptionStore[\"Service\"][\"deferRecovery\"] = Effect.fn(\n \"MemorySubscriptionStore.deferRecovery\",\n )(function* (input, expectedRevision, recovery) {\n const key = yield* requireKey(input, \"defer-recovery-key\");\n\n yield* failpoint.hit(\"subscription:defer-recovery:before\");\n yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (current): readonly [Result.Result<void, SubscriptionError>, MemorySubscriptionState] => {\n const storageKey = subscriptionKeyString(key);\n const text = current.registrations.get(storageKey);\n\n if (text === undefined) return [Result.fail(error(\"not-found\", \"subscription\")), current];\n const record = decode(SubscriptionRecord, text, \"defer-recovery-record\");\n\n if (Result.isFailure(record)) return [Result.fail(record.failure), current];\n\n if (record.success.configurationRevision !== expectedRevision)\n return [Result.void, current];\n\n const updated = {\n ...record.success,\n recovery:\n record.success.state === \"active\" || record.success.state === \"paused\"\n ? recovery\n : null,\n };\n\n const encoded = encode(SubscriptionRecord, updated, \"defer-recovery-encode\");\n\n if (Result.isFailure(encoded)) return [Result.fail(encoded.failure), current];\n const registrations = new Map(current.registrations);\n\n registrations.set(storageKey, encoded.success);\n const registrationIndex = new Map(current.registrationIndex);\n const indexed = registrationIndex.get(storageKey);\n\n if (indexed === undefined)\n return [Result.fail(error(\"corrupt\", \"recovery-index\")), current];\n registrationIndex.set(storageKey, {\n ...indexed,\n recoveryKey: updated.recovery === null ? null : candidateIndexKey(updated),\n recoveryAt: updated.recovery?.nextAttemptAtMillis ?? null,\n });\n\n return [\n Result.void,\n {\n ...current,\n registrations,\n registrationIndex,\n recoveryCounts: recoveryCountsAfter(current, registrationIndex, [storageKey]),\n },\n ];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n yield* failpoint.hit(\"subscription:defer-recovery:after\");\n });\n\n const readScanCursors: SubscriptionStore[\"Service\"][\"readScanCursors\"] = Ref.get(state).pipe(\n Effect.map((current) => current.scanCursors),\n );\n\n const advanceScanCursors: SubscriptionStore[\"Service\"][\"advanceScanCursors\"] = Effect.fn(\n \"MemorySubscriptionStore.advanceScanCursors\",\n )(function* (input) {\n const cursors = yield* validate(SubscriptionScanCursors, input, \"scan-cursors\");\n\n yield* failpoint.hit(\"subscription:advance-scan-cursors:before\");\n yield* Effect.uninterruptible(\n Ref.update(state, (current) => ({ ...current, scanCursors: cursors })),\n );\n yield* failpoint.hit(\"subscription:advance-scan-cursors:after\");\n });\n\n const nextDeadline = Effect.gen(function* () {\n let deadline: number | null = null;\n const current = yield* Ref.get(state);\n\n if (\n current.scanCursors.events !== \"\" ||\n current.scanCursors.deliveries !== \"\" ||\n current.scanCursors.recovery !== 0\n )\n return 0;\n\n const consider = (value: number) => {\n if (deadline === null || value < deadline) deadline = value;\n };\n\n if (current.retentionDeadline !== null) consider(current.retentionDeadline);\n for (const accepted of current.eventIndex.values())\n if (!accepted.routingComplete) consider(accepted.nextAttemptAtMillis);\n for (const item of current.deliveryIndex.values())\n if (\n (item.state !== \"delivered\" && item.state !== \"refused\" && item.parked !== true) ||\n (item.state === \"delivered\" && item.observeSettlement === true)\n )\n consider(item.nextAttemptAtMillis);\n for (const record of current.registrationIndex.values())\n if (record.state === \"active\" && record.recoveryAt !== null) consider(record.recoveryAt);\n\n return deadline;\n }).pipe(Effect.withSpan(\"MemorySubscriptionStore.nextDeadline\"));\n\n const compact: SubscriptionStore[\"Service\"][\"compact\"] = Effect.fn(\n \"MemorySubscriptionStore.compact\",\n )(function* (nowMillis, inputPolicy, requestedLimit) {\n nowMillis = Math.min(nowMillis, yield* Clock.currentTimeMillis);\n const policy = yield* validate(SubscriptionRetentionPolicy, inputPolicy, \"retention-policy\");\n\n const limit = yield* validate(\n Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 100 })),\n requestedLimit,\n \"maintenance-limit\",\n );\n\n yield* failpoint.hit(\"subscription:compact:before\");\n let corruptCandidates = 0;\n\n const result = yield* Effect.uninterruptible(\n Ref.modify(\n state,\n (current): readonly [Result.Result<number, SubscriptionError>, MemorySubscriptionState] => {\n if (\n current.retentionHorizon !== null &&\n current.retentionHorizon !== policy.replayHorizonMillis\n )\n return [Result.fail(error(\"conflict\", \"retention-horizon\")), current];\n const events = new Map(current.events);\n const eventIndex = new Map(current.eventIndex);\n const deliveries = new Map(current.deliveries);\n const deliveryIndex = new Map(current.deliveryIndex);\n const eventDeliveryCounts = new Map(current.eventDeliveryCounts);\n const ownerDeliveryCounts = new Map(current.ownerDeliveryCounts);\n\n // Page indexes before decoding. Each pass reads at most 2 * limit event values and\n // limit delivery values; relationship checks use maintained reference counts.\n const deliveryPage = current.deliveryKeys.slice(\n upperBound(current.deliveryKeys, current.maintenanceDeliveries),\n upperBound(current.deliveryKeys, current.maintenanceDeliveries) + limit,\n );\n\n const eventPage = current.eventKeys.slice(\n upperBound(current.eventKeys, current.maintenanceEvents),\n upperBound(current.eventKeys, current.maintenanceEvents) + limit,\n );\n\n const cutoff = nowMillis - policy.completedRetentionMillis;\n let removed = 0;\n const removedEvents = new Set<string>();\n const removedDeliveries = new Set<string>();\n\n for (const key of deliveryPage) {\n const text = deliveries.get(key);\n\n if (text === undefined) continue;\n const decoded = decode(SubscriptionDelivery, text, \"compact-delivery\");\n\n if (Result.isFailure(decoded)) {\n corruptCandidates++;\n continue;\n }\n const delivery = decoded.success;\n\n if (\n subscriptionDeliveryKeyString(delivery.key) !== key ||\n !samePartition(delivery.key.subscription.partition, partition)\n ) {\n corruptCandidates++;\n continue;\n }\n\n if (\n delivery.state !== \"refused\" &&\n (delivery.state !== \"delivered\" || delivery.settledAtMillis === undefined)\n )\n continue;\n if (\n (delivery.settledAtMillis ??\n delivery.completedAtMillis ??\n delivery.selectedAtMillis) > cutoff\n )\n continue;\n const eventText = events.get(delivery.key.eventId);\n\n if (eventText === undefined) continue;\n const accepted = decode(AcceptedEvent, eventText, \"compact-delivery-event\");\n\n if (Result.isFailure(accepted)) {\n corruptCandidates++;\n continue;\n }\n const event = accepted.success;\n\n if (\n event.eventId !== delivery.key.eventId ||\n !samePartition(event.partition, partition)\n ) {\n corruptCandidates++;\n continue;\n }\n\n if (\n !event.routingComplete ||\n event.occurredAtMillis === undefined ||\n event.acceptedAtMillis > cutoff ||\n (current.recoveryCounts.get(eventCandidateIndexKey(event)) ?? 0) > 0\n )\n continue;\n deliveries.delete(key);\n removedDeliveries.add(key);\n deliveryIndex.delete(key);\n eventDeliveryCounts.set(\n event.eventId,\n (eventDeliveryCounts.get(event.eventId) ?? 1) - 1,\n );\n const owner = delivery.key.subscription.ownerId;\n\n ownerDeliveryCounts.set(owner, (ownerDeliveryCounts.get(owner) ?? 1) - 1);\n }\n let tombstones = current.tombstoneCount;\n\n for (const key of eventPage) {\n const text = events.get(key);\n\n if (text === undefined) continue;\n const decoded = decode(AcceptedEvent, text, \"compact-event\");\n\n if (Result.isFailure(decoded)) {\n corruptCandidates++;\n continue;\n }\n const event = decoded.success;\n\n if (event.eventId !== key || !samePartition(event.partition, partition)) {\n corruptCandidates++;\n continue;\n }\n\n if (!event.routingComplete || event.occurredAtMillis === undefined) continue;\n const expired = event.occurredAtMillis <= nowMillis - policy.replayHorizonMillis;\n\n if (event.tombstone === true && !expired) continue;\n if (\n event.acceptedAtMillis > cutoff ||\n (eventDeliveryCounts.get(key) ?? 0) > 0 ||\n (current.recoveryCounts.get(eventCandidateIndexKey(event)) ?? 0) > 0\n )\n continue;\n if (expired) {\n events.delete(key);\n removedEvents.add(key);\n eventIndex.delete(key);\n eventDeliveryCounts.delete(key);\n if (event.tombstone === true) tombstones--;\n } else {\n if (tombstones >= policy.maxTombstones) continue;\n\n const encoded = encode(\n AcceptedEvent,\n { ...event, payload: null, tombstone: true },\n \"compact-tombstone\",\n );\n\n if (Result.isFailure(encoded)) continue;\n events.set(key, encoded.success);\n tombstones++;\n }\n removed++;\n }\n\n return [\n Result.succeed(removed),\n {\n ...current,\n events,\n eventIndex,\n deliveries,\n deliveryIndex,\n eventDeliveryCounts,\n ownerDeliveryCounts,\n eventKeys: removeKeys(current.eventKeys, removedEvents),\n deliveryKeys: removeKeys(current.deliveryKeys, removedDeliveries),\n tombstoneCount: tombstones,\n maintenanceEvents: eventPage.length < limit ? \"\" : (eventPage.at(-1) ?? \"\"),\n maintenanceDeliveries: deliveryPage.length < limit ? \"\" : (deliveryPage.at(-1) ?? \"\"),\n retentionHorizon: policy.replayHorizonMillis,\n retentionDeadline: events.size === 0 ? null : nowMillis + 60_000,\n },\n ];\n },\n ),\n ).pipe(Effect.flatMap(Effect.fromResult));\n\n if (corruptCandidates > 0)\n yield* Effect.logWarning(\"Subscription retention preserved corrupt candidates\", {\n count: corruptCandidates,\n });\n yield* failpoint.hit(\"subscription:compact:after\");\n\n return result;\n });\n\n return SubscriptionStore.of({\n partition,\n compact,\n register,\n get,\n list,\n cancel,\n change,\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 memorySubscriptionStoreLayer = (\n partition: SourcePartition,\n): Layer.Layer<SubscriptionStore, SubscriptionError> =>\n Layer.effect(SubscriptionStore, makeMemorySubscriptionStore(partition));\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Crypto, Layer } from "effect";
|
|
2
|
+
import { ThreadStore } from "@effect-agent/thread/ThreadStore";
|
|
3
|
+
declare namespace MemoryThreadStore_d_exports {
|
|
4
|
+
export { MemoryThreadStoreLive, memoryThreadStoreLayer };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* In-memory canonical Thread persistence. Durable accepted work is served by the separate
|
|
8
|
+
* SubmissionLedger port; this Layer deliberately provides only the ThreadStore.
|
|
9
|
+
*/
|
|
10
|
+
declare const MemoryThreadStoreLive: Layer.Layer<ThreadStore, never, Crypto.Crypto>;
|
|
11
|
+
/** Configure a finite retained Thread capacity. Invalid construction options throw immediately. */
|
|
12
|
+
declare const memoryThreadStoreLayer: (options?: {
|
|
13
|
+
readonly maxThreads?: number;
|
|
14
|
+
}) => Layer.Layer<ThreadStore, never, Crypto.Crypto>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { MemoryThreadStoreLive, memoryThreadStoreLayer, MemoryThreadStore_d_exports as t };
|
|
17
|
+
//# sourceMappingURL=MemoryThreadStore.d.mts.map
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { Context, Crypto, Effect, Encoding, Layer, Option, PubSub, Ref, Schema, Stream } from "effect";
|
|
3
|
+
import { ThreadId } from "@effect-agent/core/Identifiers";
|
|
4
|
+
import { CanonicalRecordEnvelope, CanonicalSequence, ObservationOffset } from "@effect-agent/thread/Records";
|
|
5
|
+
import { EMPTY_TAIL_DIGEST, digestCanonicalBatch } from "@effect-agent/thread/Digest";
|
|
6
|
+
import { AppendConflict, AppendResult, CheckpointRejected, FenceRejected, FencedAppendRequest, LoadCheckpointRequest, MAX_THREAD_EXPORT_RECORDS, SaveCheckpointRequest, SaveRecoveryCheckpointRequest, ThreadExport, ThreadExportRequest, ThreadMaterialization, ThreadNotMaterialized, ThreadObservation, ThreadRead, ThreadStore, ThreadStoreError, ThreadTail, ThreadTailRequest } from "@effect-agent/thread/ThreadStore";
|
|
7
|
+
//#region src/MemoryThreadStore.ts
|
|
8
|
+
var MemoryThreadStore_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
MemoryThreadStoreLive: () => MemoryThreadStoreLive,
|
|
10
|
+
memoryThreadStoreLayer: () => memoryThreadStoreLayer
|
|
11
|
+
});
|
|
12
|
+
const MAX_THREADS = 256;
|
|
13
|
+
const MAX_RECORDS_PER_THREAD = MAX_THREAD_EXPORT_RECORDS;
|
|
14
|
+
const MAX_CHECKPOINTS_PER_THREAD = 1024;
|
|
15
|
+
const ThreadCapacity = Context.Reference("@effect-agent/storage-memory/MemoryThreadStore/ThreadCapacity", { defaultValue: () => MAX_THREADS });
|
|
16
|
+
const storeError = (operation, message, cause) => cause === void 0 ? ThreadStoreError.make({
|
|
17
|
+
operation,
|
|
18
|
+
message
|
|
19
|
+
}) : ThreadStoreError.make({
|
|
20
|
+
operation,
|
|
21
|
+
message,
|
|
22
|
+
cause
|
|
23
|
+
});
|
|
24
|
+
const validate = Effect.fn("MemoryThreadStore.validate")((schema, operation, value) => Schema.encodeUnknownEffect(schema)(value).pipe(Effect.flatMap(Schema.decodeUnknownEffect(schema)), Effect.mapError((error) => storeError(operation, `Invalid ${operation} request`, error))));
|
|
25
|
+
const decodeCanonicalSequence = Schema.decodeSync(CanonicalSequence);
|
|
26
|
+
const ZERO_CANONICAL_SEQUENCE = decodeCanonicalSequence(0);
|
|
27
|
+
const offsetSequence = Effect.fn("MemoryThreadStore.offsetSequence")((threadId, offset) => {
|
|
28
|
+
if (offset === void 0) return Effect.succeed(ZERO_CANONICAL_SEQUENCE);
|
|
29
|
+
const prefix = `memory:v1:${Encoding.encodeBase64(threadId)}:`;
|
|
30
|
+
const encodedSequence = offset.startsWith(prefix) ? offset.slice(prefix.length) : "";
|
|
31
|
+
if (!/^\d+$/.test(encodedSequence)) return Effect.fail(storeError("observe", "Malformed observation offset"));
|
|
32
|
+
const sequence = Number(encodedSequence);
|
|
33
|
+
return Number.isSafeInteger(sequence) ? Schema.decodeUnknownEffect(CanonicalSequence)(sequence).pipe(Effect.mapError(() => storeError("observe", "Malformed observation offset"))) : Effect.fail(storeError("observe", "Malformed observation offset"));
|
|
34
|
+
});
|
|
35
|
+
const observationOffset = (threadId, sequence) => Schema.decodeSync(ObservationOffset)(`memory:v1:${Encoding.encodeBase64(threadId)}:${sequence}`);
|
|
36
|
+
const findThread = Effect.fn("MemoryThreadStore.findThread")((state, threadId) => {
|
|
37
|
+
const thread = state.threads.get(threadId);
|
|
38
|
+
return thread === void 0 ? Effect.fail(ThreadNotMaterialized.make({ threadId })) : Effect.succeed(thread);
|
|
39
|
+
});
|
|
40
|
+
const CheckpointVersionEnvelope = Schema.Struct({ checkpoint: Schema.Struct({
|
|
41
|
+
threadId: ThreadId,
|
|
42
|
+
schemaVersion: Schema.Natural
|
|
43
|
+
}) });
|
|
44
|
+
const validateCheckpointVersion = Effect.fn("MemoryThreadStore.validateCheckpointVersion")(function* (value) {
|
|
45
|
+
const envelope = yield* Schema.decodeUnknownEffect(CheckpointVersionEnvelope)(value).pipe(Effect.mapError(() => storeError("saveCheckpoint", "Invalid saveCheckpoint request")));
|
|
46
|
+
if (envelope.checkpoint.schemaVersion !== 1) return yield* CheckpointRejected.make({
|
|
47
|
+
threadId: envelope.checkpoint.threadId,
|
|
48
|
+
reason: "unsupported-version"
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
const makeThreadStore = Effect.gen(function* () {
|
|
52
|
+
const maxThreads = yield* ThreadCapacity;
|
|
53
|
+
const crypto = yield* Crypto.Crypto;
|
|
54
|
+
const state = yield* Ref.make({ threads: /* @__PURE__ */ new Map() });
|
|
55
|
+
const updates = yield* PubSub.sliding(1);
|
|
56
|
+
yield* Effect.addFinalizer(() => PubSub.shutdown(updates));
|
|
57
|
+
const materialize = Effect.fn("MemoryThreadStore.materialize")((unvalidated) => Effect.gen(function* () {
|
|
58
|
+
const request = yield* validate(ThreadMaterialization, "materialize", unvalidated);
|
|
59
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
60
|
+
const existing = current.threads.get(request.threadId);
|
|
61
|
+
if (existing !== void 0) {
|
|
62
|
+
if (request.producerEpoch < existing.producerEpoch) return [{
|
|
63
|
+
_tag: "failure",
|
|
64
|
+
error: FenceRejected.make({
|
|
65
|
+
threadId: request.threadId,
|
|
66
|
+
actualEpoch: existing.producerEpoch,
|
|
67
|
+
attemptedEpoch: request.producerEpoch
|
|
68
|
+
})
|
|
69
|
+
}, current];
|
|
70
|
+
if (request.producerEpoch === existing.producerEpoch) return [{ _tag: "success" }, current];
|
|
71
|
+
const threads = new Map(current.threads);
|
|
72
|
+
threads.set(request.threadId, {
|
|
73
|
+
...existing,
|
|
74
|
+
producerEpoch: request.producerEpoch
|
|
75
|
+
});
|
|
76
|
+
return [{ _tag: "success" }, { threads }];
|
|
77
|
+
}
|
|
78
|
+
if (current.threads.size >= maxThreads) return [{
|
|
79
|
+
_tag: "failure",
|
|
80
|
+
error: storeError("materialize", `In-memory thread limit ${maxThreads} exceeded`)
|
|
81
|
+
}, current];
|
|
82
|
+
const threads = new Map(current.threads);
|
|
83
|
+
threads.set(request.threadId, {
|
|
84
|
+
producerEpoch: request.producerEpoch,
|
|
85
|
+
tailSequence: ZERO_CANONICAL_SEQUENCE,
|
|
86
|
+
tailDigest: EMPTY_TAIL_DIGEST,
|
|
87
|
+
records: [],
|
|
88
|
+
recordIds: /* @__PURE__ */ new Set(),
|
|
89
|
+
batches: /* @__PURE__ */ new Map(),
|
|
90
|
+
tailDigests: /* @__PURE__ */ new Map([[ZERO_CANONICAL_SEQUENCE, EMPTY_TAIL_DIGEST]]),
|
|
91
|
+
checkpoints: /* @__PURE__ */ new Map()
|
|
92
|
+
});
|
|
93
|
+
return [{ _tag: "success" }, { threads }];
|
|
94
|
+
});
|
|
95
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
96
|
+
}));
|
|
97
|
+
const append = Effect.fn("MemoryThreadStore.append")((unvalidated) => Effect.gen(function* () {
|
|
98
|
+
const request = yield* validate(FencedAppendRequest, "append", unvalidated);
|
|
99
|
+
const digest = yield* digestCanonicalBatch(request.expectedTailDigest, request.batch).pipe(Effect.provideService(Crypto.Crypto, crypto), Effect.mapError((error) => storeError("append", error.message, error)));
|
|
100
|
+
const decision = yield* Effect.uninterruptible(Ref.modify(state, (current) => {
|
|
101
|
+
const thread = current.threads.get(request.threadId);
|
|
102
|
+
if (thread === void 0) return [{
|
|
103
|
+
_tag: "failure",
|
|
104
|
+
error: ThreadNotMaterialized.make({ threadId: request.threadId })
|
|
105
|
+
}, current];
|
|
106
|
+
if (request.producerEpoch !== thread.producerEpoch) return [{
|
|
107
|
+
_tag: "failure",
|
|
108
|
+
error: FenceRejected.make({
|
|
109
|
+
threadId: request.threadId,
|
|
110
|
+
actualEpoch: thread.producerEpoch,
|
|
111
|
+
attemptedEpoch: request.producerEpoch
|
|
112
|
+
})
|
|
113
|
+
}, current];
|
|
114
|
+
const previous = thread.batches.get(request.batch.batchId);
|
|
115
|
+
if (previous !== void 0) {
|
|
116
|
+
if (previous.digest !== digest) return [{
|
|
117
|
+
_tag: "failure",
|
|
118
|
+
error: AppendConflict.make({
|
|
119
|
+
threadId: request.threadId,
|
|
120
|
+
batchId: request.batch.batchId,
|
|
121
|
+
reason: "batch-digest"
|
|
122
|
+
})
|
|
123
|
+
}, current];
|
|
124
|
+
return [{
|
|
125
|
+
_tag: "success",
|
|
126
|
+
result: AppendResult.make({
|
|
127
|
+
firstSequence: previous.result.firstSequence,
|
|
128
|
+
lastSequence: previous.result.lastSequence,
|
|
129
|
+
tailDigest: previous.result.tailDigest,
|
|
130
|
+
replayed: true
|
|
131
|
+
}),
|
|
132
|
+
records: []
|
|
133
|
+
}, current];
|
|
134
|
+
}
|
|
135
|
+
if (request.expectedTailSequence !== thread.tailSequence || request.expectedTailDigest !== thread.tailDigest) return [{
|
|
136
|
+
_tag: "failure",
|
|
137
|
+
error: AppendConflict.make({
|
|
138
|
+
threadId: request.threadId,
|
|
139
|
+
batchId: request.batch.batchId,
|
|
140
|
+
reason: "tail",
|
|
141
|
+
actualTailSequence: thread.tailSequence,
|
|
142
|
+
actualTailDigest: thread.tailDigest
|
|
143
|
+
})
|
|
144
|
+
}, current];
|
|
145
|
+
if (thread.records.length + request.batch.records.length > MAX_RECORDS_PER_THREAD) return [{
|
|
146
|
+
_tag: "failure",
|
|
147
|
+
error: storeError("append", `In-memory record limit ${MAX_RECORDS_PER_THREAD} exceeded`)
|
|
148
|
+
}, current];
|
|
149
|
+
const batchRecordIds = /* @__PURE__ */ new Set();
|
|
150
|
+
for (const record of request.batch.records) {
|
|
151
|
+
if (thread.recordIds.has(record.recordId) || batchRecordIds.has(record.recordId)) return [{
|
|
152
|
+
_tag: "failure",
|
|
153
|
+
error: AppendConflict.make({
|
|
154
|
+
threadId: request.threadId,
|
|
155
|
+
batchId: request.batch.batchId,
|
|
156
|
+
reason: "record-identity"
|
|
157
|
+
})
|
|
158
|
+
}, current];
|
|
159
|
+
batchRecordIds.add(record.recordId);
|
|
160
|
+
}
|
|
161
|
+
const records = request.batch.records.map((record, index) => {
|
|
162
|
+
const sequence = decodeCanonicalSequence(thread.tailSequence + index + 1);
|
|
163
|
+
return CanonicalRecordEnvelope.make({
|
|
164
|
+
threadId: request.threadId,
|
|
165
|
+
batchId: request.batch.batchId,
|
|
166
|
+
sequence,
|
|
167
|
+
offset: observationOffset(request.threadId, sequence),
|
|
168
|
+
record
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
const lastSequence = decodeCanonicalSequence(thread.tailSequence + records.length);
|
|
172
|
+
const result = AppendResult.make({
|
|
173
|
+
firstSequence: decodeCanonicalSequence(thread.tailSequence + 1),
|
|
174
|
+
lastSequence,
|
|
175
|
+
tailDigest: digest,
|
|
176
|
+
replayed: false
|
|
177
|
+
});
|
|
178
|
+
const batches = new Map(thread.batches);
|
|
179
|
+
batches.set(request.batch.batchId, {
|
|
180
|
+
digest,
|
|
181
|
+
result
|
|
182
|
+
});
|
|
183
|
+
const recordIds = new Set(thread.recordIds);
|
|
184
|
+
for (const recordId of batchRecordIds) recordIds.add(recordId);
|
|
185
|
+
const tailDigests = new Map(thread.tailDigests);
|
|
186
|
+
tailDigests.set(lastSequence, digest);
|
|
187
|
+
const threads = new Map(current.threads);
|
|
188
|
+
threads.set(request.threadId, {
|
|
189
|
+
...thread,
|
|
190
|
+
tailSequence: lastSequence,
|
|
191
|
+
tailDigest: digest,
|
|
192
|
+
records: [...thread.records, ...records],
|
|
193
|
+
recordIds,
|
|
194
|
+
batches,
|
|
195
|
+
tailDigests
|
|
196
|
+
});
|
|
197
|
+
return [{
|
|
198
|
+
_tag: "success",
|
|
199
|
+
result,
|
|
200
|
+
records
|
|
201
|
+
}, { threads }];
|
|
202
|
+
}).pipe(Effect.tap((decision) => decision._tag === "success" && decision.records.length > 0 ? PubSub.publish(updates, void 0) : Effect.void)));
|
|
203
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
204
|
+
return decision.result;
|
|
205
|
+
}));
|
|
206
|
+
const readSnapshot = Effect.fn("MemoryThreadStore.readSnapshot")((threadId, afterSequence, limit) => Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, threadId)), Effect.map((thread) => {
|
|
207
|
+
const start = afterSequence ?? ZERO_CANONICAL_SEQUENCE;
|
|
208
|
+
return thread.records.slice(start, start + limit);
|
|
209
|
+
})));
|
|
210
|
+
const read = (unvalidated) => Stream.unwrap(Effect.gen(function* () {
|
|
211
|
+
const request = yield* validate(ThreadRead, "read", unvalidated);
|
|
212
|
+
const records = yield* readSnapshot(request.threadId, request.afterSequence, request.limit);
|
|
213
|
+
return Stream.fromIterable(records);
|
|
214
|
+
}));
|
|
215
|
+
const observe = (unvalidated) => Stream.unwrap(Effect.gen(function* () {
|
|
216
|
+
const request = yield* validate(ThreadObservation, "observe", unvalidated);
|
|
217
|
+
const afterSequence = yield* offsetSequence(request.threadId, request.afterOffset);
|
|
218
|
+
return Stream.unwrap(Effect.gen(function* () {
|
|
219
|
+
const subscription = yield* PubSub.subscribe(updates);
|
|
220
|
+
const initial = yield* readSnapshot(request.threadId, afterSequence, MAX_RECORDS_PER_THREAD);
|
|
221
|
+
const highWater = initial.length === 0 ? afterSequence : initial.at(-1)?.sequence ?? afterSequence;
|
|
222
|
+
const live = Stream.fromEffectRepeat(PubSub.take(subscription)).pipe(Stream.mapAccumEffect(() => highWater, (lastSequence) => readSnapshot(request.threadId, lastSequence, MAX_RECORDS_PER_THREAD).pipe(Effect.map((records) => [records.at(-1)?.sequence ?? lastSequence, records]))));
|
|
223
|
+
return Stream.fromIterable(initial).pipe(Stream.concat(live));
|
|
224
|
+
}));
|
|
225
|
+
}));
|
|
226
|
+
const exportThread = Effect.fn("MemoryThreadStore.export")((unvalidated) => Effect.gen(function* () {
|
|
227
|
+
const request = yield* validate(ThreadExportRequest, "export", unvalidated);
|
|
228
|
+
const thread = yield* Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, request.threadId)));
|
|
229
|
+
return ThreadExport.make({
|
|
230
|
+
format: "effect-agent/thread@1",
|
|
231
|
+
threadId: request.threadId,
|
|
232
|
+
tailSequence: thread.tailSequence,
|
|
233
|
+
tailDigest: thread.tailDigest,
|
|
234
|
+
records: thread.records
|
|
235
|
+
});
|
|
236
|
+
}));
|
|
237
|
+
const inspectTail = Effect.fn("MemoryThreadStore.inspectTail")((unvalidated) => Effect.gen(function* () {
|
|
238
|
+
const request = yield* validate(ThreadTailRequest, "inspectTail", unvalidated);
|
|
239
|
+
const thread = yield* Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, request.threadId)));
|
|
240
|
+
return ThreadTail.make({
|
|
241
|
+
threadId: request.threadId,
|
|
242
|
+
tailSequence: thread.tailSequence,
|
|
243
|
+
tailDigest: thread.tailDigest,
|
|
244
|
+
producerEpoch: thread.producerEpoch
|
|
245
|
+
});
|
|
246
|
+
}));
|
|
247
|
+
const saveCheckpoint = Effect.fn("MemoryThreadStore.saveCheckpoint")((unvalidated) => Effect.gen(function* () {
|
|
248
|
+
yield* validateCheckpointVersion(unvalidated);
|
|
249
|
+
const request = yield* validate(SaveCheckpointRequest, "saveCheckpoint", unvalidated);
|
|
250
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
251
|
+
const checkpoint = request.checkpoint;
|
|
252
|
+
const thread = current.threads.get(checkpoint.threadId);
|
|
253
|
+
if (thread === void 0) return [{
|
|
254
|
+
_tag: "failure",
|
|
255
|
+
error: ThreadNotMaterialized.make({ threadId: checkpoint.threadId })
|
|
256
|
+
}, current];
|
|
257
|
+
if (checkpoint.throughSequence > thread.tailSequence) return [{
|
|
258
|
+
_tag: "failure",
|
|
259
|
+
error: CheckpointRejected.make({
|
|
260
|
+
threadId: checkpoint.threadId,
|
|
261
|
+
reason: "ahead-of-tail"
|
|
262
|
+
})
|
|
263
|
+
}, current];
|
|
264
|
+
if (thread.tailDigests.get(checkpoint.throughSequence) !== checkpoint.tailDigest) return [{
|
|
265
|
+
_tag: "failure",
|
|
266
|
+
error: CheckpointRejected.make({
|
|
267
|
+
threadId: checkpoint.threadId,
|
|
268
|
+
reason: "digest-mismatch"
|
|
269
|
+
})
|
|
270
|
+
}, current];
|
|
271
|
+
if (!thread.checkpoints.has(checkpoint.throughSequence) && thread.checkpoints.size >= MAX_CHECKPOINTS_PER_THREAD) return [{
|
|
272
|
+
_tag: "failure",
|
|
273
|
+
error: storeError("saveCheckpoint", `In-memory checkpoint limit ${MAX_CHECKPOINTS_PER_THREAD} exceeded`)
|
|
274
|
+
}, current];
|
|
275
|
+
const checkpoints = new Map(thread.checkpoints);
|
|
276
|
+
checkpoints.set(checkpoint.throughSequence, checkpoint);
|
|
277
|
+
const threads = new Map(current.threads);
|
|
278
|
+
threads.set(checkpoint.threadId, {
|
|
279
|
+
...thread,
|
|
280
|
+
checkpoints
|
|
281
|
+
});
|
|
282
|
+
return [{ _tag: "success" }, { threads }];
|
|
283
|
+
});
|
|
284
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
285
|
+
}));
|
|
286
|
+
const loadCheckpoint = Effect.fn("MemoryThreadStore.loadCheckpoint")((unvalidated) => Effect.gen(function* () {
|
|
287
|
+
const request = yield* validate(LoadCheckpointRequest, "loadCheckpoint", unvalidated);
|
|
288
|
+
const thread = yield* Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, request.threadId)));
|
|
289
|
+
const maximum = request.atOrBeforeSequence ?? thread.tailSequence;
|
|
290
|
+
let selected;
|
|
291
|
+
for (const [sequence, checkpoint] of thread.checkpoints) if (sequence <= maximum && (selected === void 0 || sequence > selected.throughSequence)) selected = checkpoint;
|
|
292
|
+
if (selected !== void 0 && thread.tailDigests.get(selected.throughSequence) !== selected.tailDigest) return yield* CheckpointRejected.make({
|
|
293
|
+
threadId: request.threadId,
|
|
294
|
+
reason: "digest-mismatch"
|
|
295
|
+
});
|
|
296
|
+
return Option.fromNullishOr(selected);
|
|
297
|
+
}));
|
|
298
|
+
const saveRecoveryCheckpoint = Effect.fn("MemoryThreadStore.saveRecoveryCheckpoint")(function* (unvalidated) {
|
|
299
|
+
const request = yield* validate(SaveRecoveryCheckpointRequest, "saveRecoveryCheckpoint", unvalidated);
|
|
300
|
+
const decision = yield* Ref.modify(state, (current) => {
|
|
301
|
+
const checkpoint = request.checkpoint;
|
|
302
|
+
const thread = current.threads.get(checkpoint.threadId);
|
|
303
|
+
if (thread === void 0) return [{
|
|
304
|
+
_tag: "failure",
|
|
305
|
+
error: ThreadNotMaterialized.make({ threadId: checkpoint.threadId })
|
|
306
|
+
}, current];
|
|
307
|
+
if (thread.producerEpoch !== request.producerEpoch) return [{
|
|
308
|
+
_tag: "failure",
|
|
309
|
+
error: FenceRejected.make({
|
|
310
|
+
threadId: checkpoint.threadId,
|
|
311
|
+
actualEpoch: thread.producerEpoch,
|
|
312
|
+
attemptedEpoch: request.producerEpoch
|
|
313
|
+
})
|
|
314
|
+
}, current];
|
|
315
|
+
if (checkpoint.throughSequence > thread.tailSequence) return [{
|
|
316
|
+
_tag: "failure",
|
|
317
|
+
error: CheckpointRejected.make({
|
|
318
|
+
threadId: checkpoint.threadId,
|
|
319
|
+
reason: "ahead-of-tail"
|
|
320
|
+
})
|
|
321
|
+
}, current];
|
|
322
|
+
if (thread.tailDigests.get(checkpoint.throughSequence) !== checkpoint.tailDigest) return [{
|
|
323
|
+
_tag: "failure",
|
|
324
|
+
error: CheckpointRejected.make({
|
|
325
|
+
threadId: checkpoint.threadId,
|
|
326
|
+
reason: "digest-mismatch"
|
|
327
|
+
})
|
|
328
|
+
}, current];
|
|
329
|
+
if ((thread.recoveryCheckpoint?.throughSequence ?? -1) > checkpoint.throughSequence) return [{ _tag: "success" }, current];
|
|
330
|
+
const threads = new Map(current.threads);
|
|
331
|
+
threads.set(checkpoint.threadId, {
|
|
332
|
+
...thread,
|
|
333
|
+
recoveryCheckpoint: checkpoint
|
|
334
|
+
});
|
|
335
|
+
return [{ _tag: "success" }, { threads }];
|
|
336
|
+
});
|
|
337
|
+
if (decision._tag === "failure") return yield* decision.error;
|
|
338
|
+
});
|
|
339
|
+
const loadRecoveryCheckpoint = Effect.fn("MemoryThreadStore.loadRecoveryCheckpoint")(function* (unvalidated) {
|
|
340
|
+
const request = yield* validate(LoadCheckpointRequest, "loadRecoveryCheckpoint", unvalidated);
|
|
341
|
+
const thread = yield* Ref.get(state).pipe(Effect.flatMap((current) => findThread(current, request.threadId)));
|
|
342
|
+
const checkpoint = thread.recoveryCheckpoint;
|
|
343
|
+
if (checkpoint === void 0 || checkpoint.throughSequence > (request.atOrBeforeSequence ?? thread.tailSequence)) return Option.none();
|
|
344
|
+
if (thread.tailDigests.get(checkpoint.throughSequence) !== checkpoint.tailDigest) return yield* CheckpointRejected.make({
|
|
345
|
+
threadId: request.threadId,
|
|
346
|
+
reason: "digest-mismatch"
|
|
347
|
+
});
|
|
348
|
+
return Option.some(checkpoint);
|
|
349
|
+
});
|
|
350
|
+
return ThreadStore.of({
|
|
351
|
+
materialize,
|
|
352
|
+
append,
|
|
353
|
+
read,
|
|
354
|
+
observe,
|
|
355
|
+
export: exportThread,
|
|
356
|
+
inspectTail,
|
|
357
|
+
checkpoints: {
|
|
358
|
+
save: saveCheckpoint,
|
|
359
|
+
load: loadCheckpoint
|
|
360
|
+
},
|
|
361
|
+
recoveryCheckpoints: {
|
|
362
|
+
save: saveRecoveryCheckpoint,
|
|
363
|
+
load: loadRecoveryCheckpoint
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
/**
|
|
368
|
+
* In-memory canonical Thread persistence. Durable accepted work is served by the separate
|
|
369
|
+
* SubmissionLedger port; this Layer deliberately provides only the ThreadStore.
|
|
370
|
+
*/
|
|
371
|
+
const MemoryThreadStoreLive = Layer.effect(ThreadStore, makeThreadStore);
|
|
372
|
+
/** Configure a finite retained Thread capacity. Invalid construction options throw immediately. */
|
|
373
|
+
const memoryThreadStoreLayer = (options = {}) => MemoryThreadStoreLive.pipe(Layer.provide(Layer.succeed(ThreadCapacity)(Schema.decodeSync(Schema.Int.check(Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(65536)))(options.maxThreads ?? MAX_THREADS))));
|
|
374
|
+
//#endregion
|
|
375
|
+
export { MemoryThreadStoreLive, memoryThreadStoreLayer, MemoryThreadStore_exports as t };
|
|
376
|
+
|
|
377
|
+
//# sourceMappingURL=MemoryThreadStore.mjs.map
|