@farukada/aws-langgraph-dynamodb-ts 0.4.0 → 0.7.0
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/README.md +15 -4
- package/dist/checkpointer/actions/put-writes.d.ts +4 -4
- package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
- package/dist/checkpointer/actions/put-writes.js +22 -25
- package/dist/checkpointer/actions/put-writes.js.map +1 -1
- package/dist/checkpointer/actions/put.d.ts.map +1 -1
- package/dist/checkpointer/actions/put.js +2 -0
- package/dist/checkpointer/actions/put.js.map +1 -1
- package/dist/checkpointer/internal/item-writer.d.ts +18 -9
- package/dist/checkpointer/internal/item-writer.d.ts.map +1 -1
- package/dist/checkpointer/internal/item-writer.js +28 -11
- package/dist/checkpointer/internal/item-writer.js.map +1 -1
- package/dist/checkpointer/internal/special-write-cleanup.d.ts +18 -0
- package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +1 -0
- package/dist/checkpointer/internal/special-write-cleanup.js +102 -0
- package/dist/checkpointer/internal/special-write-cleanup.js.map +1 -0
- package/dist/factory/factory.d.ts +2 -0
- package/dist/factory/factory.d.ts.map +1 -1
- package/dist/factory/factory.js.map +1 -1
- package/dist/history/actions/clear.js +1 -1
- package/dist/history/actions/clear.js.map +1 -1
- package/dist/history/internal/append-saga.js +24 -21
- package/dist/history/internal/append-saga.js.map +1 -1
- package/dist/history/internal/keys.d.ts +1 -1
- package/dist/history/internal/keys.d.ts.map +1 -1
- package/dist/history/internal/keys.js +12 -2
- package/dist/history/internal/keys.js.map +1 -1
- package/dist/history/internal/message-transaction.d.ts +11 -6
- package/dist/history/internal/message-transaction.d.ts.map +1 -1
- package/dist/history/internal/message-transaction.js +40 -13
- package/dist/history/internal/message-transaction.js.map +1 -1
- package/dist/history/internal/query.d.ts +5 -1
- package/dist/history/internal/query.d.ts.map +1 -1
- package/dist/history/internal/query.js +5 -2
- package/dist/history/internal/query.js.map +1 -1
- package/dist/history/internal/session-count.d.ts +20 -0
- package/dist/history/internal/session-count.d.ts.map +1 -0
- package/dist/history/internal/session-count.js +58 -0
- package/dist/history/internal/session-count.js.map +1 -0
- package/dist/history/internal/session-update.d.ts +4 -1
- package/dist/history/internal/session-update.d.ts.map +1 -1
- package/dist/history/internal/session-update.js +22 -2
- package/dist/history/internal/session-update.js.map +1 -1
- package/dist/shared/codec/s3/offloader.js +1 -1
- package/dist/shared/codec/s3/offloader.js.map +1 -1
- package/dist/shared/dynamodb/batch-write.d.ts +11 -1
- package/dist/shared/dynamodb/batch-write.d.ts.map +1 -1
- package/dist/shared/dynamodb/batch-write.js +24 -3
- package/dist/shared/dynamodb/batch-write.js.map +1 -1
- package/dist/shared/dynamodb/cancellation.d.ts +7 -0
- package/dist/shared/dynamodb/cancellation.d.ts.map +1 -0
- package/dist/shared/dynamodb/cancellation.js +8 -0
- package/dist/shared/dynamodb/cancellation.js.map +1 -0
- package/dist/shared/dynamodb/drain-unprocessed.d.ts +5 -1
- package/dist/shared/dynamodb/drain-unprocessed.d.ts.map +1 -1
- package/dist/shared/dynamodb/drain-unprocessed.js +20 -11
- package/dist/shared/dynamodb/drain-unprocessed.js.map +1 -1
- package/dist/shared/dynamodb/retry-classifier.d.ts.map +1 -1
- package/dist/shared/dynamodb/retry-classifier.js +2 -1
- package/dist/shared/dynamodb/retry-classifier.js.map +1 -1
- package/dist/shared/errors/errors.d.ts +11 -3
- package/dist/shared/errors/errors.d.ts.map +1 -1
- package/dist/shared/errors/errors.js +14 -5
- package/dist/shared/errors/errors.js.map +1 -1
- package/dist/shared/logging/redaction.d.ts +9 -2
- package/dist/shared/logging/redaction.d.ts.map +1 -1
- package/dist/shared/logging/redaction.js +15 -3
- package/dist/shared/logging/redaction.js.map +1 -1
- package/dist/store/actions/list-namespaces.d.ts.map +1 -1
- package/dist/store/actions/list-namespaces.js +10 -2
- package/dist/store/actions/list-namespaces.js.map +1 -1
- package/dist/store/actions/put.d.ts +5 -7
- package/dist/store/actions/put.d.ts.map +1 -1
- package/dist/store/actions/put.js +7 -28
- package/dist/store/actions/put.js.map +1 -1
- package/dist/store/internal/filter.d.ts.map +1 -1
- package/dist/store/internal/filter.js +6 -2
- package/dist/store/internal/filter.js.map +1 -1
- package/dist/store/internal/index-reconcile.d.ts.map +1 -1
- package/dist/store/internal/index-reconcile.js +1 -0
- package/dist/store/internal/index-reconcile.js.map +1 -1
- package/dist/store/internal/persist.d.ts +11 -0
- package/dist/store/internal/persist.d.ts.map +1 -0
- package/dist/store/internal/persist.js +38 -0
- package/dist/store/internal/persist.js.map +1 -0
- package/dist/store/internal/write-verify.d.ts +12 -0
- package/dist/store/internal/write-verify.d.ts.map +1 -0
- package/dist/store/internal/write-verify.js +28 -0
- package/dist/store/internal/write-verify.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,29 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.appendChunks = appendChunks;
|
|
4
|
-
const node_crypto_1 = require("node:crypto");
|
|
5
4
|
const descriptor_keys_1 = require("../../shared/codec/descriptor-keys");
|
|
6
5
|
const orphans_1 = require("../../shared/codec/s3/orphans");
|
|
7
6
|
const batch_write_1 = require("../../shared/dynamodb/batch-write");
|
|
8
|
-
const retry_1 = require("../../shared/dynamodb/retry");
|
|
9
7
|
const errors_1 = require("../../shared/errors/errors");
|
|
10
8
|
const wrap_error_1 = require("../../shared/errors/wrap-error");
|
|
11
|
-
const keys_1 = require("./keys");
|
|
12
9
|
const message_transaction_1 = require("./message-transaction");
|
|
13
|
-
|
|
14
|
-
async function revertSessionCount(context, sessionId, delta) {
|
|
15
|
-
if (delta === 0)
|
|
16
|
-
return;
|
|
17
|
-
const update = {
|
|
18
|
-
TableName: context.tableName,
|
|
19
|
-
Key: { PK: (0, keys_1.sessionPartition)(sessionId), SK: keys_1.SESSION_SORT_KEY },
|
|
20
|
-
UpdateExpression: 'ADD #count :neg',
|
|
21
|
-
ExpressionAttributeNames: { '#count': 'messageCount' },
|
|
22
|
-
ExpressionAttributeValues: { ':neg': -delta },
|
|
23
|
-
};
|
|
24
|
-
const input = { TransactItems: [{ Update: update }], ClientRequestToken: (0, node_crypto_1.randomUUID)() };
|
|
25
|
-
await (0, retry_1.withDynamoDBRetry)(() => context.client.transactWrite(input));
|
|
26
|
-
}
|
|
10
|
+
const session_count_1 = require("./session-count");
|
|
27
11
|
/**
|
|
28
12
|
* Best-effort delete the offloaded S3 objects of the `chunks` slice given.
|
|
29
13
|
* {@link compensate} calls it once per commit status, never for the whole
|
|
@@ -35,14 +19,33 @@ async function cleanBatchS3(context, chunks) {
|
|
|
35
19
|
const descriptors = chunks.flat().map((item) => item.message);
|
|
36
20
|
await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(descriptors), 'history.addMessages', context.logger);
|
|
37
21
|
}
|
|
38
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Delete every committed chunk's items, then revert the counted total by
|
|
24
|
+
* however many rows the delete actually persisted before failing (not
|
|
25
|
+
* necessarily every committed row — see the catch branch below).
|
|
26
|
+
*/
|
|
39
27
|
async function rollbackCommitted(context, sessionId, committed) {
|
|
40
28
|
const keys = committed.flatMap((chunk) => chunk.keys);
|
|
41
|
-
|
|
29
|
+
const total = committed.reduce((sum, chunk) => sum + chunk.count, 0);
|
|
30
|
+
if (keys.length === 0) {
|
|
31
|
+
await (0, session_count_1.revertSessionCount)(context, sessionId, total);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
42
35
|
await (0, batch_write_1.batchWriteAll)(context.client, context.tableName, keys.map((Key) => ({ DeleteRequest: { Key } })));
|
|
43
36
|
}
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
catch (error) {
|
|
38
|
+
/**
|
|
39
|
+
* batchWriteAll has exactly one throw site and it is always a
|
|
40
|
+
* BatchWriteAllIncompleteError (see Task 9) — asserted, not instanceof-
|
|
41
|
+
* checked, since the false case is unreachable and this project
|
|
42
|
+
* enforces 100% branch coverage with no exceptions.
|
|
43
|
+
*/
|
|
44
|
+
const deleted = error.succeededCount;
|
|
45
|
+
await (0, session_count_1.revertSessionCount)(context, sessionId, deleted);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
await (0, session_count_1.revertSessionCount)(context, sessionId, total);
|
|
46
49
|
}
|
|
47
50
|
/**
|
|
48
51
|
* Undo a failed batch. Always throws. S3 cleanup is split by commit status so
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"append-saga.js","sourceRoot":"","sources":["../../../src/history/internal/append-saga.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"append-saga.js","sourceRoot":"","sources":["../../../src/history/internal/append-saga.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Fixed sort key for the per-session metadata item. */
|
|
2
|
-
export declare const SESSION_SORT_KEY = "SESSION";
|
|
2
|
+
export declare const SESSION_SORT_KEY = "HISTORY#SESSION";
|
|
3
3
|
/** Partition key for a chat session: the session id itself. */
|
|
4
4
|
export declare function sessionPartition(sessionId: string): string;
|
|
5
5
|
/** Sort key for a single message item, ordered by its monotonic ULID. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/history/internal/keys.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/history/internal/keys.ts"],"names":[],"mappings":"AAWA,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,oBAA6B,CAAC;AAI3D,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,sEAAsE;AACtE,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C"}
|
|
@@ -4,9 +4,19 @@ exports.SESSION_SORT_KEY = void 0;
|
|
|
4
4
|
exports.sessionPartition = sessionPartition;
|
|
5
5
|
exports.messageSortKey = messageSortKey;
|
|
6
6
|
exports.messageSortKeyPrefix = messageSortKeyPrefix;
|
|
7
|
+
/**
|
|
8
|
+
* Item-kind tag distinguishing this adapter's sort keys from another
|
|
9
|
+
* adapter's on a table shared via `DynamoDBFactory.createAll()` — matches the
|
|
10
|
+
* pattern the checkpointer module already uses for its own META#/PAYLOAD#/
|
|
11
|
+
* WRITE# keys. Without it, `SESSION_SORT_KEY` alone was a bare, common-word
|
|
12
|
+
* literal a store caller could produce by accident (e.g.
|
|
13
|
+
* `store.put([sessionId], 'SESSION', ...)`, since `sortKey` collapses a
|
|
14
|
+
* single-element namespace down to just the key).
|
|
15
|
+
*/
|
|
16
|
+
const ADAPTER_PREFIX = 'HISTORY#';
|
|
7
17
|
/** Fixed sort key for the per-session metadata item. */
|
|
8
|
-
exports.SESSION_SORT_KEY =
|
|
9
|
-
const MESSAGE_PREFIX =
|
|
18
|
+
exports.SESSION_SORT_KEY = `${ADAPTER_PREFIX}SESSION`;
|
|
19
|
+
const MESSAGE_PREFIX = `${ADAPTER_PREFIX}MSG#`;
|
|
10
20
|
/** Partition key for a chat session: the session id itself. */
|
|
11
21
|
function sessionPartition(sessionId) {
|
|
12
22
|
return sessionId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/history/internal/keys.ts"],"names":[],"mappings":";;;;;;AAAA,wDAAwD;AAC3C,QAAA,gBAAgB,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/history/internal/keys.ts"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;GAQG;AACH,MAAM,cAAc,GAAG,UAAU,CAAC;AAElC,wDAAwD;AAC3C,QAAA,gBAAgB,GAAG,GAAG,cAAc,SAAS,CAAC;AAE3D,MAAM,cAAc,GAAG,GAAG,cAAc,MAAM,CAAC;AAE/C,+DAA+D;AAC/D,0BAAiC,SAAiB;IAChD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,yEAAyE;AACzE,wBAA+B,IAAY;IACzC,OAAO,GAAG,cAAc,GAAG,IAAI,EAAE,CAAC;AACpC,CAAC;AAED,sEAAsE;AACtE;IACE,OAAO,cAAc,CAAC;AACxB,CAAC"}
|
|
@@ -10,12 +10,17 @@ export interface ChunkRetryOptions {
|
|
|
10
10
|
* Atomically write a chunk of message items together with the session-metadata
|
|
11
11
|
* count update in one {@link https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html | TransactWriteItems}
|
|
12
12
|
* call, so `messageCount` can never disagree with the messages that landed. A
|
|
13
|
-
* single `ClientRequestToken` is
|
|
14
|
-
* after a lost response) is idempotent and never double-applies the count
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
13
|
+
* single `ClientRequestToken` is used per attempt so a re-sent commit (e.g.
|
|
14
|
+
* after a lost response) is idempotent and never double-applies the count
|
|
15
|
+
* `ADD`. When `fields.forceTtlRefresh` is set, the session update carries a
|
|
16
|
+
* monotonic ConditionExpression (see session-update.ts); if — and only if —
|
|
17
|
+
* that specific condition loses a race against a concurrent caller who just
|
|
18
|
+
* healed the same anchor, this retries the identical chunk once with
|
|
19
|
+
* `forceTtlRefresh: false` (safe: `if_not_exists` then converges to whatever
|
|
20
|
+
* already won) rather than losing the message writes to a benign ttl race. A
|
|
21
|
+
* cancellation caused by any other item (a genuine message-row conflict) is
|
|
22
|
+
* not retried here — it propagates for the normal transient-conflict retry
|
|
23
|
+
* budget inside `withDynamoDBRetry` to handle, or to the caller otherwise.
|
|
19
24
|
*/
|
|
20
25
|
export declare function writeMessageChunk(context: HistoryContext, items: ChatMessageItem[], fields: SessionUpdateFields, retry?: ChunkRetryOptions): Promise<void>;
|
|
21
26
|
//# sourceMappingURL=message-transaction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-transaction.d.ts","sourceRoot":"","sources":["../../../src/history/internal/message-transaction.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"message-transaction.d.ts","sourceRoot":"","sources":["../../../src/history/internal/message-transaction.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAuCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,eAAe,EAAE,EACxB,MAAM,EAAE,mBAAmB,EAC3B,KAAK,GAAE,iBAAsB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAUf"}
|
|
@@ -3,31 +3,58 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.writeMessageChunk = writeMessageChunk;
|
|
4
4
|
const node_crypto_1 = require("node:crypto");
|
|
5
5
|
const constants_1 = require("../../shared/constants");
|
|
6
|
+
const cancellation_1 = require("../../shared/dynamodb/cancellation");
|
|
6
7
|
const retry_1 = require("../../shared/dynamodb/retry");
|
|
7
8
|
const session_update_1 = require("./session-update");
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* {@link MESSAGE_APPEND_RETRY_MAX_ATTEMPTS} rather than the default budget so a
|
|
17
|
-
* burst of concurrent callers can drain via backoff instead of erroring.
|
|
18
|
-
*/
|
|
19
|
-
async function writeMessageChunk(context, items, fields, retry = {}) {
|
|
20
|
-
const input = {
|
|
9
|
+
/** True when a TransactWriteItems cancellation was caused solely by the SESSION update's ttl condition (always TransactItems index 0 — see buildInput below), not by any message item. */
|
|
10
|
+
function isTtlConditionLoss(error) {
|
|
11
|
+
const reasons = (0, cancellation_1.getCancellationReasons)(error);
|
|
12
|
+
return (reasons?.[0]?.Code === 'ConditionalCheckFailed' &&
|
|
13
|
+
reasons.slice(1).every((reason) => reason.Code === 'None'));
|
|
14
|
+
}
|
|
15
|
+
function buildInput(context, items, fields) {
|
|
16
|
+
return {
|
|
21
17
|
TransactItems: [
|
|
22
18
|
(0, session_update_1.buildSessionUpdateItem)(context.tableName, fields),
|
|
23
19
|
...items.map((item) => ({ Put: { TableName: context.tableName, Item: item } })),
|
|
24
20
|
],
|
|
25
21
|
ClientRequestToken: (0, node_crypto_1.randomUUID)(),
|
|
26
22
|
};
|
|
23
|
+
}
|
|
24
|
+
async function attempt(context, items, fields, retry) {
|
|
25
|
+
const input = buildInput(context, items, fields);
|
|
27
26
|
await (0, retry_1.withDynamoDBRetry)(() => context.client.transactWrite(input), {
|
|
28
27
|
maxAttempts: constants_1.MESSAGE_APPEND_RETRY_MAX_ATTEMPTS,
|
|
29
28
|
rng: retry.rng,
|
|
30
29
|
signal: retry.signal,
|
|
31
30
|
});
|
|
32
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Atomically write a chunk of message items together with the session-metadata
|
|
34
|
+
* count update in one {@link https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html | TransactWriteItems}
|
|
35
|
+
* call, so `messageCount` can never disagree with the messages that landed. A
|
|
36
|
+
* single `ClientRequestToken` is used per attempt so a re-sent commit (e.g.
|
|
37
|
+
* after a lost response) is idempotent and never double-applies the count
|
|
38
|
+
* `ADD`. When `fields.forceTtlRefresh` is set, the session update carries a
|
|
39
|
+
* monotonic ConditionExpression (see session-update.ts); if — and only if —
|
|
40
|
+
* that specific condition loses a race against a concurrent caller who just
|
|
41
|
+
* healed the same anchor, this retries the identical chunk once with
|
|
42
|
+
* `forceTtlRefresh: false` (safe: `if_not_exists` then converges to whatever
|
|
43
|
+
* already won) rather than losing the message writes to a benign ttl race. A
|
|
44
|
+
* cancellation caused by any other item (a genuine message-row conflict) is
|
|
45
|
+
* not retried here — it propagates for the normal transient-conflict retry
|
|
46
|
+
* budget inside `withDynamoDBRetry` to handle, or to the caller otherwise.
|
|
47
|
+
*/
|
|
48
|
+
async function writeMessageChunk(context, items, fields, retry = {}) {
|
|
49
|
+
try {
|
|
50
|
+
await attempt(context, items, fields, retry);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (fields.forceTtlRefresh && isTtlConditionLoss(error)) {
|
|
54
|
+
await attempt(context, items, { ...fields, forceTtlRefresh: false }, retry);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
33
60
|
//# sourceMappingURL=message-transaction.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-transaction.js","sourceRoot":"","sources":["../../../src/history/internal/message-transaction.ts"],"names":[],"mappings":";;;AAAA,6CAAyC;AAEzC,sDAA2E;AAC3E,uDAAgE;AAEhE,
|
|
1
|
+
{"version":3,"file":"message-transaction.js","sourceRoot":"","sources":["../../../src/history/internal/message-transaction.ts"],"names":[],"mappings":";;;AAAA,6CAAyC;AAEzC,sDAA2E;AAC3E,qEAA4E;AAC5E,uDAAgE;AAEhE,qDAI0B;AAS1B,0LAA0L;AAC1L,SAAS,kBAAkB,CAAC,KAAY;IACtC,MAAM,OAAO,GAAG,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,CACL,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,wBAAwB;QAC/C,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAC3D,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CACjB,OAAuB,EACvB,KAAwB,EACxB,MAA2B;IAE3B,OAAO;QACL,aAAa,EAAE;YACb,IAAA,uCAAsB,EAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;YACjD,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;SAChF;QACD,kBAAkB,EAAE,IAAA,wBAAU,GAAE;KACjC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,OAAuB,EACvB,KAAwB,EACxB,MAA2B,EAC3B,KAAwB;IAExB,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE;QACjE,WAAW,EAAE,6CAAiC;QAC9C,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACI,KAAK,4BACV,OAAuB,EACvB,KAAwB,EACxB,MAA2B,EAC3B,KAAK,GAAsB,EAAE;IAE7B,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,MAAM,CAAC,eAAe,IAAI,kBAAkB,CAAC,KAAc,CAAC,EAAE,CAAC;YACjE,MAAM,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { QueryCommandInput } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
/** Options for {@link sessionItemsQuery}. */
|
|
3
|
+
export interface SessionItemsQueryOptions {
|
|
4
|
+
consistent?: boolean;
|
|
5
|
+
}
|
|
2
6
|
/** Query input selecting every item in a session's partition (messages + metadata). */
|
|
3
|
-
export declare function sessionItemsQuery(tableName: string, sessionId: string): QueryCommandInput;
|
|
7
|
+
export declare function sessionItemsQuery(tableName: string, sessionId: string, options?: SessionItemsQueryOptions): QueryCommandInput;
|
|
4
8
|
/** Query input selecting a session's message items in chronological order. */
|
|
5
9
|
export declare function messageQuery(tableName: string, sessionId: string): QueryCommandInput;
|
|
6
10
|
//# sourceMappingURL=query.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/history/internal/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAI/D,uFAAuF;AACvF,wBAAgB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/history/internal/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAI/D,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,uFAAuF;AACvF,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,wBAA6B,GACrC,iBAAiB,CASnB;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAWpF"}
|
|
@@ -4,13 +4,16 @@ exports.sessionItemsQuery = sessionItemsQuery;
|
|
|
4
4
|
exports.messageQuery = messageQuery;
|
|
5
5
|
const keys_1 = require("./keys");
|
|
6
6
|
/** Query input selecting every item in a session's partition (messages + metadata). */
|
|
7
|
-
function sessionItemsQuery(tableName, sessionId) {
|
|
8
|
-
|
|
7
|
+
function sessionItemsQuery(tableName, sessionId, options = {}) {
|
|
8
|
+
const params = {
|
|
9
9
|
TableName: tableName,
|
|
10
10
|
KeyConditionExpression: '#pk = :pk',
|
|
11
11
|
ExpressionAttributeNames: { '#pk': 'PK' },
|
|
12
12
|
ExpressionAttributeValues: { ':pk': (0, keys_1.sessionPartition)(sessionId) },
|
|
13
13
|
};
|
|
14
|
+
if (options.consistent)
|
|
15
|
+
params.ConsistentRead = true;
|
|
16
|
+
return params;
|
|
14
17
|
}
|
|
15
18
|
/** Query input selecting a session's message items in chronological order. */
|
|
16
19
|
function messageQuery(tableName, sessionId) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../../src/history/internal/query.ts"],"names":[],"mappings":";;;;AAEA,iCAAgE;
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../../src/history/internal/query.ts"],"names":[],"mappings":";;;;AAEA,iCAAgE;AAOhE,uFAAuF;AACvF,2BACE,SAAiB,EACjB,SAAiB,EACjB,OAAO,GAA6B,EAAE;IAEtC,MAAM,MAAM,GAAsB;QAChC,SAAS,EAAE,SAAS;QACpB,sBAAsB,EAAE,WAAW;QACnC,wBAAwB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACzC,yBAAyB,EAAE,EAAE,KAAK,EAAE,IAAA,uBAAgB,EAAC,SAAS,CAAC,EAAE;KAClE,CAAC;IACF,IAAI,OAAO,CAAC,UAAU;QAAE,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC;IACrD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,8EAA8E;AAC9E,sBAA6B,SAAiB,EAAE,SAAiB;IAC/D,OAAO;QACL,SAAS,EAAE,SAAS;QACpB,sBAAsB,EAAE,sCAAsC;QAC9D,wBAAwB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;QACtD,yBAAyB,EAAE;YACzB,KAAK,EAAE,IAAA,uBAAgB,EAAC,SAAS,CAAC;YAClC,MAAM,EAAE,IAAA,2BAAoB,GAAE;SAC/B;QACD,gBAAgB,EAAE,IAAI;KACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HistoryContext } from './setup';
|
|
2
|
+
/**
|
|
3
|
+
* Subtract a previously-added count from the session, leaving it consistent.
|
|
4
|
+
* Guarded so a concurrently-deleted SESSION row is never resurrected as a
|
|
5
|
+
* permanent, ttl-less junk row: if the row is already gone there is nothing
|
|
6
|
+
* to revert, so that specific condition failure is swallowed rather than
|
|
7
|
+
* surfaced — this runs only from an already-in-progress rollback, where a
|
|
8
|
+
* spurious error for a no-op would misrepresent what happened.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately does not revert a `forceTtlRefresh`-driven ttl SET from an
|
|
11
|
+
* earlier committed chunk: the healed anchor is never shorter than what
|
|
12
|
+
* was there before, so leaving it in place after a rollback only means the
|
|
13
|
+
* session's metadata row outlives its content a bit longer than ideal —
|
|
14
|
+
* self-healing (the next successful append, or DynamoDB's own TTL sweep,
|
|
15
|
+
* resolves it), unlike reverting, which would need to re-check for a
|
|
16
|
+
* concurrent legitimate extension to avoid regressing it. See README.md's
|
|
17
|
+
* "TTL expiry" section.
|
|
18
|
+
*/
|
|
19
|
+
export declare function revertSessionCount(context: HistoryContext, sessionId: string, delta: number): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=session-count.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-count.d.ts","sourceRoot":"","sources":["../../../src/history/internal/session-count.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAe9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAiBf"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.revertSessionCount = revertSessionCount;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const cancellation_1 = require("../../shared/dynamodb/cancellation");
|
|
6
|
+
const retry_1 = require("../../shared/dynamodb/retry");
|
|
7
|
+
const keys_1 = require("./keys");
|
|
8
|
+
/**
|
|
9
|
+
* True when a TransactWriteItems cancellation was caused by a
|
|
10
|
+
* ConditionalCheckFailed reason. Named distinctly from
|
|
11
|
+
* checkpointer/actions/put-writes.ts's isConditionalCheckFailed, which
|
|
12
|
+
* checks a different thing entirely (a raw PutItem exception name, not a
|
|
13
|
+
* transaction cancellation reason) — this function had that same name
|
|
14
|
+
* until now, a real trap for whoever read one assuming it was the other.
|
|
15
|
+
*/
|
|
16
|
+
function isCancelledByCondition(error) {
|
|
17
|
+
const reasons = (0, cancellation_1.getCancellationReasons)(error);
|
|
18
|
+
return reasons?.[0]?.Code === 'ConditionalCheckFailed';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Subtract a previously-added count from the session, leaving it consistent.
|
|
22
|
+
* Guarded so a concurrently-deleted SESSION row is never resurrected as a
|
|
23
|
+
* permanent, ttl-less junk row: if the row is already gone there is nothing
|
|
24
|
+
* to revert, so that specific condition failure is swallowed rather than
|
|
25
|
+
* surfaced — this runs only from an already-in-progress rollback, where a
|
|
26
|
+
* spurious error for a no-op would misrepresent what happened.
|
|
27
|
+
*
|
|
28
|
+
* Deliberately does not revert a `forceTtlRefresh`-driven ttl SET from an
|
|
29
|
+
* earlier committed chunk: the healed anchor is never shorter than what
|
|
30
|
+
* was there before, so leaving it in place after a rollback only means the
|
|
31
|
+
* session's metadata row outlives its content a bit longer than ideal —
|
|
32
|
+
* self-healing (the next successful append, or DynamoDB's own TTL sweep,
|
|
33
|
+
* resolves it), unlike reverting, which would need to re-check for a
|
|
34
|
+
* concurrent legitimate extension to avoid regressing it. See README.md's
|
|
35
|
+
* "TTL expiry" section.
|
|
36
|
+
*/
|
|
37
|
+
async function revertSessionCount(context, sessionId, delta) {
|
|
38
|
+
if (delta === 0)
|
|
39
|
+
return;
|
|
40
|
+
const update = {
|
|
41
|
+
TableName: context.tableName,
|
|
42
|
+
Key: { PK: (0, keys_1.sessionPartition)(sessionId), SK: keys_1.SESSION_SORT_KEY },
|
|
43
|
+
UpdateExpression: 'ADD #count :neg',
|
|
44
|
+
ConditionExpression: 'attribute_exists(PK)',
|
|
45
|
+
ExpressionAttributeNames: { '#count': 'messageCount' },
|
|
46
|
+
ExpressionAttributeValues: { ':neg': -delta },
|
|
47
|
+
};
|
|
48
|
+
const input = { TransactItems: [{ Update: update }], ClientRequestToken: (0, node_crypto_1.randomUUID)() };
|
|
49
|
+
try {
|
|
50
|
+
await (0, retry_1.withDynamoDBRetry)(() => context.client.transactWrite(input));
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (isCancelledByCondition(error))
|
|
54
|
+
return;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=session-count.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-count.js","sourceRoot":"","sources":["../../../src/history/internal/session-count.ts"],"names":[],"mappings":";;;AAAA,6CAAyC;AAEzC,qEAA4E;AAC5E,uDAAgE;AAChE,iCAA4D;AAG5D;;;;;;;GAOG;AACH,SAAS,sBAAsB,CAAC,KAAY;IAC1C,MAAM,OAAO,GAAG,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,wBAAwB,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACI,KAAK,6BACV,OAAuB,EACvB,SAAiB,EACjB,KAAa;IAEb,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO;IACxB,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,GAAG,EAAE,EAAE,EAAE,EAAE,IAAA,uBAAgB,EAAC,SAAS,CAAC,EAAE,EAAE,EAAE,uBAAgB,EAAE;QAC9D,gBAAgB,EAAE,iBAAiB;QACnC,mBAAmB,EAAE,sBAAsB;QAC3C,wBAAwB,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE;QACtD,yBAAyB,EAAE,EAAE,MAAM,EAAE,CAAC,KAAK,EAAE;KAC9C,CAAC;IACF,MAAM,KAAK,GAAG,EAAE,aAAa,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,kBAAkB,EAAE,IAAA,wBAAU,GAAE,EAAE,CAAC;IACxF,IAAI,CAAC;QACH,MAAM,IAAA,yBAAiB,EAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,sBAAsB,CAAC,KAAc,CAAC;YAAE,OAAO;QACnD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -19,7 +19,10 @@ export interface SessionUpdateFields {
|
|
|
19
19
|
* `forceTtlRefresh` is set (because {@link resolveTtlAnchor} found the persisted
|
|
20
20
|
* anchor missing or already expired), the `ttl` clause instead does a plain
|
|
21
21
|
* `SET`, so the SESSION row's own stale attribute actually heals instead of
|
|
22
|
-
* being permanently blocked by `if_not_exists`.
|
|
22
|
+
* being permanently blocked by `if_not_exists`. When forceTtlRefresh is set,
|
|
23
|
+
* the SET is additionally guarded by a ConditionExpression so a concurrent
|
|
24
|
+
* caller's already-healed anchor can never be regressed backward — see
|
|
25
|
+
* message-transaction.ts for how a lost race is retried without forcing.
|
|
23
26
|
*/
|
|
24
27
|
export declare function buildSessionUpdateItem(tableName: string, fields: SessionUpdateFields): HistoryTransactItem;
|
|
25
28
|
//# sourceMappingURL=session-update.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-update.d.ts","sourceRoot":"","sources":["../../../src/history/internal/session-update.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwB,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAI7F,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC,yBAAyB,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAElG,oFAAoF;AACpF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED
|
|
1
|
+
{"version":3,"file":"session-update.d.ts","sourceRoot":"","sources":["../../../src/history/internal/session-update.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwB,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAI7F,8EAA8E;AAC9E,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC,yBAAyB,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAElG,oFAAoF;AACpF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,mBAAmB,GAC1B,mBAAmB,CAiDrB"}
|
|
@@ -11,7 +11,10 @@ const keys_1 = require("./keys");
|
|
|
11
11
|
* `forceTtlRefresh` is set (because {@link resolveTtlAnchor} found the persisted
|
|
12
12
|
* anchor missing or already expired), the `ttl` clause instead does a plain
|
|
13
13
|
* `SET`, so the SESSION row's own stale attribute actually heals instead of
|
|
14
|
-
* being permanently blocked by `if_not_exists`.
|
|
14
|
+
* being permanently blocked by `if_not_exists`. When forceTtlRefresh is set,
|
|
15
|
+
* the SET is additionally guarded by a ConditionExpression so a concurrent
|
|
16
|
+
* caller's already-healed anchor can never be regressed backward — see
|
|
17
|
+
* message-transaction.ts for how a lost race is retried without forcing.
|
|
15
18
|
*/
|
|
16
19
|
function buildSessionUpdateItem(tableName, fields) {
|
|
17
20
|
const names = {
|
|
@@ -32,16 +35,33 @@ function buildSessionUpdateItem(tableName, fields) {
|
|
|
32
35
|
values[':title'] = fields.title;
|
|
33
36
|
sets.push('#title = if_not_exists(#title, :title)');
|
|
34
37
|
}
|
|
38
|
+
let conditionExpression;
|
|
35
39
|
if (fields.ttlTimestamp !== undefined) {
|
|
36
40
|
names['#ttl'] = 'ttl';
|
|
37
41
|
values[':ttl'] = fields.ttlTimestamp;
|
|
38
|
-
|
|
42
|
+
if (fields.forceTtlRefresh) {
|
|
43
|
+
sets.push('#ttl = :ttl');
|
|
44
|
+
/**
|
|
45
|
+
* Guards the force-overwrite so a concurrent caller's already-healed,
|
|
46
|
+
* equal-or-later anchor can never be regressed backward by this one.
|
|
47
|
+
* `<=` (not `<`): two concurrent healers of the same stale anchor
|
|
48
|
+
* typically compute the identical target timestamp, and `<=` lets
|
|
49
|
+
* the second one succeed by re-applying the same value instead of
|
|
50
|
+
* failing the condition and paying a full transaction retry for a
|
|
51
|
+
* write that was never actually a regression.
|
|
52
|
+
*/
|
|
53
|
+
conditionExpression = 'attribute_not_exists(#ttl) OR #ttl <= :ttl';
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
sets.push('#ttl = if_not_exists(#ttl, :ttl)');
|
|
57
|
+
}
|
|
39
58
|
}
|
|
40
59
|
return {
|
|
41
60
|
Update: {
|
|
42
61
|
TableName: tableName,
|
|
43
62
|
Key: { PK: (0, keys_1.sessionPartition)(fields.sessionId), SK: keys_1.SESSION_SORT_KEY },
|
|
44
63
|
UpdateExpression: `ADD #count :n SET ${sets.join(', ')}`,
|
|
64
|
+
...(conditionExpression ? { ConditionExpression: conditionExpression } : {}),
|
|
45
65
|
ExpressionAttributeNames: names,
|
|
46
66
|
ExpressionAttributeValues: values,
|
|
47
67
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-update.js","sourceRoot":"","sources":["../../../src/history/internal/session-update.ts"],"names":[],"mappings":";;;AAEA,iCAA4D;AAe5D
|
|
1
|
+
{"version":3,"file":"session-update.js","sourceRoot":"","sources":["../../../src/history/internal/session-update.ts"],"names":[],"mappings":";;;AAEA,iCAA4D;AAe5D;;;;;;;;;;;;;GAaG;AACH,gCACE,SAAiB,EACjB,MAA2B;IAE3B,MAAM,KAAK,GAA2B;QACpC,QAAQ,EAAE,cAAc;QACxB,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,WAAW;KACpB,CAAC;IACF,MAAM,MAAM,GAAyC;QACnD,IAAI,EAAE,MAAM,CAAC,KAAK;QAClB,IAAI,EAAE,MAAM,CAAC,GAAG;QAChB,IAAI,EAAE,MAAM,CAAC,GAAG;QAChB,MAAM,EAAE,MAAM,CAAC,SAAS;KACzB,CAAC;IACF,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,4BAA4B,EAAE,kCAAkC,CAAC,CAAC;IAC3F,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,KAAK,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC;QAC1B,MAAM,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,mBAAuC,CAAC;IAC5C,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACtC,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;QACtB,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC;QACrC,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;YAC3B,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACzB;;;;;;;;eAQG;YACH,mBAAmB,GAAG,4CAA4C,CAAC;QACrE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IACD,OAAO;QACL,MAAM,EAAE;YACN,SAAS,EAAE,SAAS;YACpB,GAAG,EAAE,EAAE,EAAE,EAAE,IAAA,uBAAgB,EAAC,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,uBAAgB,EAAE;YACrE,gBAAgB,EAAE,qBAAqB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YACxD,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,wBAAwB,EAAE,KAAK;YAC/B,yBAAyB,EAAE,MAAM;SAClC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -32,7 +32,7 @@ class S3Offloader {
|
|
|
32
32
|
if (!this.clientPromise) {
|
|
33
33
|
const cfg = this.config.clientConfig ?? {};
|
|
34
34
|
this.clientPromise = (this.config.createS3Client
|
|
35
|
-
? Promise.resolve(this.config.createS3Client(cfg))
|
|
35
|
+
? Promise.resolve(this.config.createS3Client({ maxAttempts: 1, ...cfg }))
|
|
36
36
|
: (0, client_1.createDefaultS3Client)(cfg)).then((client) => {
|
|
37
37
|
this.resolvedClient = client;
|
|
38
38
|
return client;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"offloader.js","sourceRoot":"","sources":["../../../../src/shared/codec/s3/offloader.ts"],"names":[],"mappings":";;;AAEA,+CAAoG;AACpG,qCAAiD;AACjD,qCAAuD;AACvD,qCAAyC;AACzC,2CAAkD;AAClD,6CAA4D;AAE5D;;;GAGG;AACH;IACU,aAAa,CAAgC;IAC7C,cAAc,CAAuB;IAC5B,UAAU,CAAS;IACnB,SAAS,CAAS;IAClB,cAAc,CAAS;IACvB,GAAG,CAAS;IACZ,WAAW,CAAU;IACrB,MAAM,CAAkB;IAEzC,YAAY,MAAuB;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QACpC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,iCAAqB,CAAC;QAC3D,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,sCAA0B,CAAC;QAC1E,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,oBAAoB,IAAI,0BAAc,CAAC;QACzD,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;IACxC,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC;YAC3C,IAAI,CAAC,aAAa,GAAG,CACnB,IAAI,CAAC,MAAM,CAAC,cAAc;gBACxB,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"offloader.js","sourceRoot":"","sources":["../../../../src/shared/codec/s3/offloader.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,17 @@ import type { WriteRequest } from './types';
|
|
|
7
7
|
* chunk's failure — order-independent writes (deletes/puts) should never
|
|
8
8
|
* lose "later" chunks just because an earlier one failed. If any chunk fails,
|
|
9
9
|
* throws {@link BatchWriteAllIncompleteError} reporting exactly how many
|
|
10
|
-
* chunks succeeded vs. failed
|
|
10
|
+
* chunks succeeded vs. failed, and exactly how many individual writes
|
|
11
|
+
* persisted, once every chunk has been attempted.
|
|
12
|
+
*
|
|
13
|
+
* This is this function's ONLY throw site. Two callers —
|
|
14
|
+
* checkpointer/internal/special-write-cleanup.ts and
|
|
15
|
+
* history/internal/append-saga.ts — type-assert a caught error straight to
|
|
16
|
+
* {@link BatchWriteAllIncompleteError} on that guarantee (not `instanceof`,
|
|
17
|
+
* banned repo-wide) instead of narrowing it, since this project enforces
|
|
18
|
+
* 100% branch coverage and a defensive else-branch here would be
|
|
19
|
+
* unreachable, hence untestable. Adding another throw path to this function
|
|
20
|
+
* requires updating both call sites.
|
|
11
21
|
*/
|
|
12
22
|
export declare function batchWriteAll(client: DynamoDBDocument, tableName: string, requests: WriteRequest[], options?: DrainOptions): Promise<void>;
|
|
13
23
|
//# sourceMappingURL=batch-write.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch-write.d.ts","sourceRoot":"","sources":["../../../src/shared/dynamodb/batch-write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAI9D,OAAO,EAAE,YAAY,EAA0B,MAAM,qBAAqB,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C
|
|
1
|
+
{"version":3,"file":"batch-write.d.ts","sourceRoot":"","sources":["../../../src/shared/dynamodb/batch-write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAI9D,OAAO,EAAE,YAAY,EAA0B,MAAM,qBAAqB,CAAC;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAgCf"}
|
|
@@ -10,24 +10,45 @@ const drain_unprocessed_1 = require("./drain-unprocessed");
|
|
|
10
10
|
* chunk's failure — order-independent writes (deletes/puts) should never
|
|
11
11
|
* lose "later" chunks just because an earlier one failed. If any chunk fails,
|
|
12
12
|
* throws {@link BatchWriteAllIncompleteError} reporting exactly how many
|
|
13
|
-
* chunks succeeded vs. failed
|
|
13
|
+
* chunks succeeded vs. failed, and exactly how many individual writes
|
|
14
|
+
* persisted, once every chunk has been attempted.
|
|
15
|
+
*
|
|
16
|
+
* This is this function's ONLY throw site. Two callers —
|
|
17
|
+
* checkpointer/internal/special-write-cleanup.ts and
|
|
18
|
+
* history/internal/append-saga.ts — type-assert a caught error straight to
|
|
19
|
+
* {@link BatchWriteAllIncompleteError} on that guarantee (not `instanceof`,
|
|
20
|
+
* banned repo-wide) instead of narrowing it, since this project enforces
|
|
21
|
+
* 100% branch coverage and a defensive else-branch here would be
|
|
22
|
+
* unreachable, hence untestable. Adding another throw path to this function
|
|
23
|
+
* requires updating both call sites.
|
|
14
24
|
*/
|
|
15
25
|
async function batchWriteAll(client, tableName, requests, options = {}) {
|
|
16
26
|
const totalChunks = Math.ceil(requests.length / constants_1.BATCH_WRITE_MAX);
|
|
17
27
|
let succeededChunks = 0;
|
|
28
|
+
let succeededCount = 0;
|
|
18
29
|
const failedChunks = [];
|
|
19
30
|
for (let offset = 0; offset < requests.length; offset += constants_1.BATCH_WRITE_MAX) {
|
|
20
31
|
const chunk = requests.slice(offset, offset + constants_1.BATCH_WRITE_MAX);
|
|
21
32
|
try {
|
|
22
33
|
await (0, drain_unprocessed_1.drainUnprocessedWrites)(client, tableName, chunk, options);
|
|
23
34
|
succeededChunks += 1;
|
|
35
|
+
succeededCount += chunk.length;
|
|
24
36
|
}
|
|
25
37
|
catch (error) {
|
|
26
|
-
|
|
38
|
+
/**
|
|
39
|
+
* drainUnprocessedWrites has exactly three throw sites and every one
|
|
40
|
+
* constructs a BatchWriteIncompleteError with an accurate
|
|
41
|
+
* succeededCount — asserted, not name-checked, since the false case is
|
|
42
|
+
* unreachable and this project enforces 100% branch coverage with no
|
|
43
|
+
* exceptions (see drain-unprocessed.ts).
|
|
44
|
+
*/
|
|
45
|
+
const err = error;
|
|
46
|
+
failedChunks.push(err);
|
|
47
|
+
succeededCount += err.succeededCount;
|
|
27
48
|
}
|
|
28
49
|
}
|
|
29
50
|
if (failedChunks.length > 0) {
|
|
30
|
-
throw new errors_1.BatchWriteAllIncompleteError(succeededChunks, totalChunks, failedChunks);
|
|
51
|
+
throw new errors_1.BatchWriteAllIncompleteError(succeededChunks, totalChunks, failedChunks, succeededCount);
|
|
31
52
|
}
|
|
32
53
|
}
|
|
33
54
|
//# sourceMappingURL=batch-write.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch-write.js","sourceRoot":"","sources":["../../../src/shared/dynamodb/batch-write.ts"],"names":[],"mappings":";;;AAEA,4CAA+C;AAC/C,
|
|
1
|
+
{"version":3,"file":"batch-write.js","sourceRoot":"","sources":["../../../src/shared/dynamodb/batch-write.ts"],"names":[],"mappings":";;;AAEA,4CAA+C;AAC/C,6CAA2F;AAC3F,2DAA2E;AAG3E;;;;;;;;;;;;;;;;;GAiBG;AACI,KAAK,wBACV,MAAwB,EACxB,SAAiB,EACjB,QAAwB,EACxB,OAAO,GAAiB,EAAE;IAE1B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,2BAAe,CAAC,CAAC;IACjE,IAAI,eAAe,GAAG,CAAC,CAAC;IACxB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,MAAM,YAAY,GAAY,EAAE,CAAC;IACjC,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,2BAAe,EAAE,CAAC;QACzE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,2BAAe,CAAC,CAAC;QAC/D,IAAI,CAAC;YACH,MAAM,IAAA,0CAAsB,EAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAChE,eAAe,IAAI,CAAC,CAAC;YACrB,cAAc,IAAI,KAAK,CAAC,MAAM,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf;;;;;;eAMG;YACH,MAAM,GAAG,GAAG,KAAkC,CAAC;YAC/C,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvB,cAAc,IAAI,GAAG,CAAC,cAAc,CAAC;QACvC,CAAC;IACH,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,qCAA4B,CACpC,eAAe,EACf,WAAW,EACX,YAAY,EACZ,cAAc,CACf,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** One entry of a TransactWriteItems/TransactionCanceledException's CancellationReasons. */
|
|
2
|
+
export interface CancellationReason {
|
|
3
|
+
Code?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Extract `CancellationReasons` from a transaction-cancellation error, if present. */
|
|
6
|
+
export declare function getCancellationReasons(error: Error): CancellationReason[] | undefined;
|
|
7
|
+
//# sourceMappingURL=cancellation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cancellation.d.ts","sourceRoot":"","sources":["../../../src/shared/dynamodb/cancellation.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,uFAAuF;AACvF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,kBAAkB,EAAE,GAAG,SAAS,CAErF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCancellationReasons = getCancellationReasons;
|
|
4
|
+
/** Extract `CancellationReasons` from a transaction-cancellation error, if present. */
|
|
5
|
+
function getCancellationReasons(error) {
|
|
6
|
+
return error.CancellationReasons;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=cancellation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cancellation.js","sourceRoot":"","sources":["../../../src/shared/dynamodb/cancellation.ts"],"names":[],"mappings":";;;AAKA,uFAAuF;AACvF,gCAAuC,KAAY;IACjD,OAAQ,KAAwD,CAAC,mBAAmB,CAAC;AACvF,CAAC"}
|
|
@@ -9,7 +9,11 @@ export interface DrainOptions {
|
|
|
9
9
|
/**
|
|
10
10
|
* Submit `requests` via BatchWriteItem and re-submit UnprocessedItems with
|
|
11
11
|
* full-jitter backoff until the batch drains. Throws
|
|
12
|
-
* {@link BatchWriteIncompleteError} if it cannot drain within the retry budget
|
|
12
|
+
* {@link BatchWriteIncompleteError} if it cannot drain within the retry budget,
|
|
13
|
+
* or if a round's write call or backoff wait throws outright (SDK failure, or
|
|
14
|
+
* the caller's signal aborting) — in every case `succeededCount` reflects
|
|
15
|
+
* every earlier round's confirmed persists, and the triggering error is
|
|
16
|
+
* attached as `cause`.
|
|
13
17
|
*/
|
|
14
18
|
export declare function drainUnprocessedWrites(client: DynamoDBDocument, tableName: string, requests: WriteRequest[], options?: DrainOptions): Promise<void>;
|
|
15
19
|
//# sourceMappingURL=drain-unprocessed.d.ts.map
|