@farukada/aws-langgraph-dynamodb-ts 0.1.0 → 0.2.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.
Files changed (153) hide show
  1. package/README.md +131 -5
  2. package/dist/checkpointer/actions/delete-thread.d.ts.map +1 -1
  3. package/dist/checkpointer/actions/delete-thread.js +17 -9
  4. package/dist/checkpointer/actions/delete-thread.js.map +1 -1
  5. package/dist/checkpointer/actions/get-tuple.d.ts.map +1 -1
  6. package/dist/checkpointer/actions/get-tuple.js +70 -32
  7. package/dist/checkpointer/actions/get-tuple.js.map +1 -1
  8. package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
  9. package/dist/checkpointer/actions/put-writes.js +36 -19
  10. package/dist/checkpointer/actions/put-writes.js.map +1 -1
  11. package/dist/checkpointer/actions/put.d.ts.map +1 -1
  12. package/dist/checkpointer/actions/put.js +75 -18
  13. package/dist/checkpointer/actions/put.js.map +1 -1
  14. package/dist/checkpointer/actions/writer.d.ts +6 -4
  15. package/dist/checkpointer/actions/writer.d.ts.map +1 -1
  16. package/dist/checkpointer/actions/writer.js +12 -12
  17. package/dist/checkpointer/actions/writer.js.map +1 -1
  18. package/dist/checkpointer/index.d.ts +11 -4
  19. package/dist/checkpointer/index.d.ts.map +1 -1
  20. package/dist/checkpointer/index.js +97 -72
  21. package/dist/checkpointer/index.js.map +1 -1
  22. package/dist/checkpointer/types/index.d.ts +8 -0
  23. package/dist/checkpointer/types/index.d.ts.map +1 -1
  24. package/dist/checkpointer/utils/deserialization.d.ts.map +1 -1
  25. package/dist/checkpointer/utils/deserialization.js +28 -4
  26. package/dist/checkpointer/utils/deserialization.js.map +1 -1
  27. package/dist/checkpointer/utils/validation.d.ts +8 -8
  28. package/dist/checkpointer/utils/validation.d.ts.map +1 -1
  29. package/dist/checkpointer/utils/validation.js +45 -1
  30. package/dist/checkpointer/utils/validation.js.map +1 -1
  31. package/dist/factory.d.ts +14 -0
  32. package/dist/factory.d.ts.map +1 -1
  33. package/dist/factory.js +39 -20
  34. package/dist/factory.js.map +1 -1
  35. package/dist/history/actions/add-message.d.ts +5 -9
  36. package/dist/history/actions/add-message.d.ts.map +1 -1
  37. package/dist/history/actions/add-message.js +14 -42
  38. package/dist/history/actions/add-message.js.map +1 -1
  39. package/dist/history/actions/add-messages.d.ts +6 -10
  40. package/dist/history/actions/add-messages.d.ts.map +1 -1
  41. package/dist/history/actions/add-messages.js +33 -38
  42. package/dist/history/actions/add-messages.js.map +1 -1
  43. package/dist/history/actions/clear.d.ts.map +1 -1
  44. package/dist/history/actions/clear.js +3 -3
  45. package/dist/history/actions/clear.js.map +1 -1
  46. package/dist/history/actions/get-messages.d.ts.map +1 -1
  47. package/dist/history/actions/get-messages.js +2 -2
  48. package/dist/history/actions/get-messages.js.map +1 -1
  49. package/dist/history/actions/list-sessions.d.ts.map +1 -1
  50. package/dist/history/actions/list-sessions.js +6 -3
  51. package/dist/history/actions/list-sessions.js.map +1 -1
  52. package/dist/history/index.d.ts +41 -5
  53. package/dist/history/index.d.ts.map +1 -1
  54. package/dist/history/index.js +52 -18
  55. package/dist/history/index.js.map +1 -1
  56. package/dist/history/session-adapter.d.ts +39 -0
  57. package/dist/history/session-adapter.d.ts.map +1 -0
  58. package/dist/history/session-adapter.js +74 -0
  59. package/dist/history/session-adapter.js.map +1 -0
  60. package/dist/history/types/index.d.ts +32 -1
  61. package/dist/history/types/index.d.ts.map +1 -1
  62. package/dist/history/utils/index.d.ts +1 -0
  63. package/dist/history/utils/index.d.ts.map +1 -1
  64. package/dist/history/utils/index.js +1 -0
  65. package/dist/history/utils/index.js.map +1 -1
  66. package/dist/history/utils/optimistic-retry.d.ts +31 -0
  67. package/dist/history/utils/optimistic-retry.d.ts.map +1 -0
  68. package/dist/history/utils/optimistic-retry.js +74 -0
  69. package/dist/history/utils/optimistic-retry.js.map +1 -0
  70. package/dist/history/utils/title-generator.d.ts.map +1 -1
  71. package/dist/history/utils/title-generator.js +8 -3
  72. package/dist/history/utils/title-generator.js.map +1 -1
  73. package/dist/history/utils/update-expression-builder.d.ts +18 -21
  74. package/dist/history/utils/update-expression-builder.d.ts.map +1 -1
  75. package/dist/history/utils/update-expression-builder.js +31 -46
  76. package/dist/history/utils/update-expression-builder.js.map +1 -1
  77. package/dist/history/utils/validation.d.ts.map +1 -1
  78. package/dist/history/utils/validation.js +23 -3
  79. package/dist/history/utils/validation.js.map +1 -1
  80. package/dist/index.d.ts +6 -2
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +6 -1
  83. package/dist/index.js.map +1 -1
  84. package/dist/shared/utils/batch-write.d.ts +44 -6
  85. package/dist/shared/utils/batch-write.d.ts.map +1 -1
  86. package/dist/shared/utils/batch-write.js +76 -28
  87. package/dist/shared/utils/batch-write.js.map +1 -1
  88. package/dist/shared/utils/client-factory.d.ts +25 -0
  89. package/dist/shared/utils/client-factory.d.ts.map +1 -0
  90. package/dist/shared/utils/client-factory.js +23 -0
  91. package/dist/shared/utils/client-factory.js.map +1 -0
  92. package/dist/shared/utils/compressor.d.ts +21 -3
  93. package/dist/shared/utils/compressor.d.ts.map +1 -1
  94. package/dist/shared/utils/compressor.js +75 -10
  95. package/dist/shared/utils/compressor.js.map +1 -1
  96. package/dist/shared/utils/constants.d.ts +1 -1
  97. package/dist/shared/utils/constants.d.ts.map +1 -1
  98. package/dist/shared/utils/constants.js +5 -1
  99. package/dist/shared/utils/constants.js.map +1 -1
  100. package/dist/shared/utils/index.d.ts +3 -0
  101. package/dist/shared/utils/index.d.ts.map +1 -1
  102. package/dist/shared/utils/index.js +3 -0
  103. package/dist/shared/utils/index.js.map +1 -1
  104. package/dist/shared/utils/logger.d.ts +27 -0
  105. package/dist/shared/utils/logger.d.ts.map +1 -1
  106. package/dist/shared/utils/logger.js +86 -0
  107. package/dist/shared/utils/logger.js.map +1 -1
  108. package/dist/shared/utils/retry.d.ts +7 -0
  109. package/dist/shared/utils/retry.d.ts.map +1 -1
  110. package/dist/shared/utils/retry.js +75 -17
  111. package/dist/shared/utils/retry.js.map +1 -1
  112. package/dist/shared/utils/s3-offloader.d.ts +10 -3
  113. package/dist/shared/utils/s3-offloader.d.ts.map +1 -1
  114. package/dist/shared/utils/s3-offloader.js +19 -9
  115. package/dist/shared/utils/s3-offloader.js.map +1 -1
  116. package/dist/shared/utils/s3-orphans.d.ts +16 -0
  117. package/dist/shared/utils/s3-orphans.d.ts.map +1 -0
  118. package/dist/shared/utils/s3-orphans.js +79 -0
  119. package/dist/shared/utils/s3-orphans.js.map +1 -0
  120. package/dist/shared/utils/sleep.d.ts +48 -0
  121. package/dist/shared/utils/sleep.d.ts.map +1 -0
  122. package/dist/shared/utils/sleep.js +82 -0
  123. package/dist/shared/utils/sleep.js.map +1 -0
  124. package/dist/shared/utils/ttl.d.ts +6 -2
  125. package/dist/shared/utils/ttl.d.ts.map +1 -1
  126. package/dist/shared/utils/ttl.js +17 -2
  127. package/dist/shared/utils/ttl.js.map +1 -1
  128. package/dist/store/actions/get-operation.d.ts.map +1 -1
  129. package/dist/store/actions/get-operation.js +2 -2
  130. package/dist/store/actions/get-operation.js.map +1 -1
  131. package/dist/store/actions/list-namespaces-operation.d.ts.map +1 -1
  132. package/dist/store/actions/list-namespaces-operation.js +44 -29
  133. package/dist/store/actions/list-namespaces-operation.js.map +1 -1
  134. package/dist/store/actions/put-operation.d.ts.map +1 -1
  135. package/dist/store/actions/put-operation.js +19 -5
  136. package/dist/store/actions/put-operation.js.map +1 -1
  137. package/dist/store/actions/search-operation.d.ts.map +1 -1
  138. package/dist/store/actions/search-operation.js +54 -21
  139. package/dist/store/actions/search-operation.js.map +1 -1
  140. package/dist/store/index.d.ts +1 -0
  141. package/dist/store/index.d.ts.map +1 -1
  142. package/dist/store/index.js +24 -20
  143. package/dist/store/index.js.map +1 -1
  144. package/dist/store/types/index.d.ts +33 -1
  145. package/dist/store/types/index.d.ts.map +1 -1
  146. package/dist/store/utils/filter.d.ts.map +1 -1
  147. package/dist/store/utils/filter.js +63 -1
  148. package/dist/store/utils/filter.js.map +1 -1
  149. package/dist/store/utils/validation.d.ts +6 -2
  150. package/dist/store/utils/validation.d.ts.map +1 -1
  151. package/dist/store/utils/validation.js +21 -5
  152. package/dist/store/utils/validation.js.map +1 -1
  153. package/package.json +36 -25
@@ -85,25 +85,82 @@ const putAction = async (params) => {
85
85
  if (ttl !== undefined) {
86
86
  payloadItem.ttl = ttl;
87
87
  }
88
- // Atomic write: both items must succeed or both fail
89
- await (0, shared_1.withDynamoDBRetry)(async () => {
90
- await params.client.transactWrite({
91
- TransactItems: [
92
- {
93
- Put: {
94
- TableName: params.checkpointsTableName,
95
- Item: metadataItem,
96
- },
97
- },
98
- {
99
- Put: {
100
- TableName: params.checkpointsTableName,
101
- Item: payloadItem,
88
+ // Optimistic-concurrency guard on the metadata Put.
89
+ //
90
+ // Two concurrent workers racing on the same (thread_id, checkpoint_id) with
91
+ // *different* parent/type (= divergent lineage) is a bug we want to surface,
92
+ // not silently resolve last-writer-wins. Two writers agreeing on parent+type
93
+ // (= legitimate idempotent retry after a transient error) must still succeed.
94
+ //
95
+ // attribute_not_exists(checkpoint_id) -- first write ever: OK
96
+ // OR (#type = :expected_type AND ( -- OR same content as prior:
97
+ // attribute_not_exists(parent_checkpoint_id) -- initial checkpoint
98
+ // OR parent_checkpoint_id = :expected_parent -- with named parent
99
+ // ))
100
+ //
101
+ // The payload Put is left unconditional: payloads are keyed by PAYLOAD#<id>
102
+ // and contain content-for-content equal data for a given checkpoint, so an
103
+ // overwrite on retry is always safe.
104
+ // Normalize `''` to `undefined` so an empty-string parent and an unset parent
105
+ // are treated as the same "no parent" state. This keeps the ConditionExpression
106
+ // stable across idempotent retries that might represent "initial checkpoint"
107
+ // either way (e.g. a migration or a caller that stringifies nulls).
108
+ const rawExpectedParent = params.config.configurable?.checkpoint_id;
109
+ const expectedParent = typeof rawExpectedParent === 'string' && rawExpectedParent.length > 0
110
+ ? rawExpectedParent
111
+ : undefined;
112
+ const metadataExpressionAttributeNames = { '#type': 'type' };
113
+ const metadataExpressionAttributeValues = {
114
+ ':expected_type': type1,
115
+ };
116
+ const parentClause = expectedParent !== undefined
117
+ ? 'parent_checkpoint_id = :expected_parent'
118
+ : 'attribute_not_exists(parent_checkpoint_id)';
119
+ if (expectedParent !== undefined) {
120
+ metadataExpressionAttributeValues[':expected_parent'] = expectedParent;
121
+ }
122
+ const metadataConditionExpression = 'attribute_not_exists(checkpoint_id) ' + `OR (#type = :expected_type AND (${parentClause}))`;
123
+ // Atomic write: both items must succeed or both fail.
124
+ // On failure, best-effort-clean any S3 objects we uploaded so they don't linger
125
+ // until the lifecycle rule sweeps them. Errors during cleanup are only logged.
126
+ try {
127
+ await (0, shared_1.withDynamoDBRetry)(async () => {
128
+ await params.client.transactWrite({
129
+ TransactItems: [
130
+ {
131
+ Put: {
132
+ TableName: params.checkpointsTableName,
133
+ Item: metadataItem,
134
+ ConditionExpression: metadataConditionExpression,
135
+ ExpressionAttributeNames: metadataExpressionAttributeNames,
136
+ ExpressionAttributeValues: metadataExpressionAttributeValues,
137
+ },
102
138
  },
103
- },
104
- ],
105
- });
106
- });
139
+ { Put: { TableName: params.checkpointsTableName, Item: payloadItem } },
140
+ ],
141
+ });
142
+ }, { signal: params.signal });
143
+ }
144
+ catch (err) {
145
+ // Skip S3 orphan cleanup on the optimistic-lock conflict path. S3 keys are
146
+ // derived deterministically from (thread_id, checkpoint_id), so a divergent
147
+ // put() on the same checkpoint_id uploads to the *same* keys as the
148
+ // canonical write already occupies — cleaning them up after a
149
+ // ConditionalCheckFailed would delete live data still referenced by the
150
+ // winning transaction. The S3 lifecycle rule is the safe backstop for the
151
+ // rare case where an overwrite did occur; data integrity beats a few stale
152
+ // bytes in S3.
153
+ const isConditionalFailure = err &&
154
+ typeof err === 'object' &&
155
+ ('name' in err
156
+ ? err.name === 'ConditionalCheckFailedException' ||
157
+ err.name === 'TransactionCanceledException'
158
+ : false);
159
+ if (params.s3Offloader && !isConditionalFailure) {
160
+ await (0, shared_1.cleanUpS3Orphans)(params.s3Offloader, [s3CheckpointKey, s3MetadataKey], 'put failure');
161
+ }
162
+ throw err;
163
+ }
107
164
  return {
108
165
  configurable: {
109
166
  thread_id: metadataItem.thread_id,
@@ -1 +1 @@
1
- {"version":3,"file":"put.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/put.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"put.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/put.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,9 @@ export declare class Writer {
12
12
  readonly checkpoint_id: string;
13
13
  /** Unique identifier for the task that created this write */
14
14
  readonly task_id: string;
15
- /** Index of the write within the task (0-based) */
15
+ /** Index of the write within the task. Negative values correspond to
16
+ * `WRITES_IDX_MAP` special-channel slots (__error__=-1, __scheduled__=-2,
17
+ * __interrupt__=-3, __resume__=-4); non-negative values are positional. */
16
18
  readonly idx: number;
17
19
  /** Channel name for the write */
18
20
  readonly channel: string;
@@ -42,9 +44,9 @@ export declare class Writer {
42
44
  checkpoint_ns: string;
43
45
  }): string;
44
46
  /**
45
- * Get the separator used for composite keys
46
- * Note: Input validation ensures this separator cannot be in any component
47
+ * Separator used for composite keys.
48
+ * Note: input validation ensures this separator cannot appear in any component.
47
49
  */
48
- static separator(): string;
50
+ static readonly SEPARATOR = ":::";
49
51
  }
50
52
  //# sourceMappingURL=writer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAS1D;;;GAGG;AACH,qBAAa,MAAM;IACjB,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2CAA2C;IAC3C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;gBAEf,EACV,SAAS,EACT,aAAa,EACb,aAAa,EACb,OAAO,EACP,GAAG,EACH,OAAO,EACP,IAAI,EACJ,KAAK,GACN,EAAE,WAAW;IAsBd,cAAc,IAAI,iBAAiB;IAcnC;;;;;;OAMG;IACH,MAAM,CAAC,gBAAgB,CAAC,EACtB,qCAAqC,EACrC,WAAW,EACX,OAAO,EACP,IAAI,EACJ,KAAK,GACN,EAAE,iBAAiB,GAAG,MAAM;IA8B7B;;;;;OAKG;IACH,MAAM,CAAC,eAAe,CAAC,EACrB,SAAS,EACT,aAAa,EACb,aAAa,GACd,EAAE;QACD,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;KACvB,GAAG,MAAM;IAIV;;;OAGG;IACH,MAAM,CAAC,SAAS;CAGjB"}
1
+ {"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAS1D;;;GAGG;AACH,qBAAa,MAAM;IACjB,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2CAA2C;IAC3C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;gFAE4E;IAC5E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;gBAEf,EACV,SAAS,EACT,aAAa,EACb,aAAa,EACb,OAAO,EACP,GAAG,EACH,OAAO,EACP,IAAI,EACJ,KAAK,GACN,EAAE,WAAW;IAsBd,cAAc,IAAI,iBAAiB;IAcnC;;;;;;OAMG;IACH,MAAM,CAAC,gBAAgB,CAAC,EACtB,qCAAqC,EACrC,WAAW,EACX,OAAO,EACP,IAAI,EACJ,KAAK,GACN,EAAE,iBAAiB,GAAG,MAAM;IA8B7B;;;;;OAKG;IACH,MAAM,CAAC,eAAe,CAAC,EACrB,SAAS,EACT,aAAa,EACb,aAAa,GACd,EAAE;QACD,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;KACvB,GAAG,MAAM;IAIV;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS,SAAS;CACnC"}
@@ -15,7 +15,9 @@ class Writer {
15
15
  checkpoint_id;
16
16
  /** Unique identifier for the task that created this write */
17
17
  task_id;
18
- /** Index of the write within the task (0-based) */
18
+ /** Index of the write within the task. Negative values correspond to
19
+ * `WRITES_IDX_MAP` special-channel slots (__error__=-1, __scheduled__=-2,
20
+ * __interrupt__=-3, __resume__=-4); non-negative values are positional. */
19
21
  idx;
20
22
  /** Channel name for the write */
21
23
  channel;
@@ -30,8 +32,8 @@ class Writer {
30
32
  (0, utils_1.validateCheckpointId)(checkpoint_id, true);
31
33
  (0, utils_1.validateTaskId)(task_id);
32
34
  (0, utils_1.validateChannel)(channel);
33
- if (!Number.isInteger(idx) || idx < 0) {
34
- throw new Error('idx must be a non-negative integer');
35
+ if (!Number.isInteger(idx)) {
36
+ throw new Error('idx must be an integer');
35
37
  }
36
38
  this.thread_id = thread_id;
37
39
  this.checkpoint_ns = checkpoint_ns;
@@ -49,7 +51,7 @@ class Writer {
49
51
  checkpoint_id: this.checkpoint_id,
50
52
  checkpoint_ns: this.checkpoint_ns,
51
53
  }),
52
- task_id_idx: [this.task_id, this.idx].join(Writer.separator()),
54
+ task_id_idx: [this.task_id, this.idx].join(Writer.SEPARATOR),
53
55
  channel: this.channel,
54
56
  type: this.type,
55
57
  value: this.value,
@@ -63,12 +65,12 @@ class Writer {
63
65
  * @throws Error if parsing fails or data is invalid
64
66
  */
65
67
  static fromDynamoDBItem({ thread_id_checkpoint_id_checkpoint_ns, task_id_idx, channel, type, value, }) {
66
- const parts = thread_id_checkpoint_id_checkpoint_ns.split(this.separator());
68
+ const parts = thread_id_checkpoint_id_checkpoint_ns.split(this.SEPARATOR);
67
69
  if (parts.length !== 3) {
68
70
  throw new Error(`Invalid partition key format: expected 3 parts, got ${parts.length}`);
69
71
  }
70
72
  const [thread_id, checkpoint_id, checkpoint_ns] = parts;
71
- const idxParts = task_id_idx.split(this.separator());
73
+ const idxParts = task_id_idx.split(this.SEPARATOR);
72
74
  if (idxParts.length !== 2) {
73
75
  throw new Error(`Invalid sort key format: expected 2 parts, got ${idxParts.length}`);
74
76
  }
@@ -95,15 +97,13 @@ class Writer {
95
97
  * @returns Composite partition key
96
98
  */
97
99
  static getPartitionKey({ thread_id, checkpoint_id, checkpoint_ns, }) {
98
- return [thread_id, checkpoint_id, checkpoint_ns].join(this.separator());
100
+ return [thread_id, checkpoint_id, checkpoint_ns].join(this.SEPARATOR);
99
101
  }
100
102
  /**
101
- * Get the separator used for composite keys
102
- * Note: Input validation ensures this separator cannot be in any component
103
+ * Separator used for composite keys.
104
+ * Note: input validation ensures this separator cannot appear in any component.
103
105
  */
104
- static separator() {
105
- return ':::';
106
- }
106
+ static SEPARATOR = ':::';
107
107
  }
108
108
  exports.Writer = Writer;
109
109
  //# sourceMappingURL=writer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/writer.ts"],"names":[],"mappings":"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"}
@@ -49,13 +49,18 @@ export declare class DynamoDBSaver extends BaseCheckpointSaver {
49
49
  * Delete a thread and all its checkpoints and writes
50
50
  *
51
51
  * @param threadId - The thread ID to delete
52
+ * @param options.signal - Optional AbortSignal for cancelling in-flight retries
52
53
  * @throws Error if validation fails or operation fails
53
54
  */
54
- deleteThread(threadId: string): Promise<void>;
55
+ deleteThread(threadId: string, options?: {
56
+ signal?: AbortSignal;
57
+ }): Promise<void>;
55
58
  /**
56
59
  * Get a checkpoint tuple from DynamoDB
57
60
  *
58
- * @param config - Runnable configuration containing thread_id and optional checkpoint_id
61
+ * @param config - Runnable configuration containing thread_id and optional checkpoint_id.
62
+ * `config.signal` is honored as an AbortSignal: in-flight retries short-circuit
63
+ * and the returned promise rejects with the signal's abort reason.
59
64
  * @returns CheckpointTuple if found, undefined otherwise
60
65
  * @throws Error if validation fails or operation fails
61
66
  */
@@ -63,7 +68,8 @@ export declare class DynamoDBSaver extends BaseCheckpointSaver {
63
68
  /**
64
69
  * Save a checkpoint to DynamoDB
65
70
  *
66
- * @param config - Runnable configuration
71
+ * @param config - Runnable configuration. `config.signal` is honored as an
72
+ * AbortSignal and short-circuits retry backoff.
67
73
  * @param checkpoint - Checkpoint to save
68
74
  * @param metadata - Checkpoint metadata
69
75
  * @param newVersions - Channel versions (not used in DynamoDB implementation)
@@ -74,7 +80,8 @@ export declare class DynamoDBSaver extends BaseCheckpointSaver {
74
80
  /**
75
81
  * Save pending writes to DynamoDB
76
82
  *
77
- * @param config - Runnable configuration
83
+ * @param config - Runnable configuration. `config.signal` is honored as an
84
+ * AbortSignal and short-circuits retry backoff.
78
85
  * @param writes - Array of pending writes to save
79
86
  * @param taskId - Task ID for the writes
80
87
  * @throws Error if validation fails or operation fails
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/checkpointer/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,iCAAiC,CAAC;AAUzC,OAAO,KAAK,EAAyC,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAI3F;;;;;;;;GAQG;AACH,qBAAa,aAAc,SAAQ,mBAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IAErC;;;;;;;;;;;;;OAaG;gBACS,OAAO,EAAE,oBAAoB;IAoCzC;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAOf;;;;;OAKG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnD;;;;;;OAMG;IACG,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAY5E;;;;;;;;;OASG;IACG,GAAG,CACP,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,kBAAkB,EAE5B,WAAW,EAAE,eAAe,GAC3B,OAAO,CAAC,cAAc,CAAC;IAe1B;;;;;;;OAOG;IACG,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe9F;;;;;;;OAOG;IACI,IAAI,CACT,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,qBAAqB,GAAG,SAAS,GACzC,cAAc,CAAC,eAAe,CAAC;IAkHlC;;;;;;;OAOG;YACW,4BAA4B;IAsF1C;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;CAStB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/checkpointer/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,iCAAiC,CAAC;AAgBzC,OAAO,KAAK,EAAyC,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAS3F;;;;;;;;GAQG;AACH,qBAAa,aAAc,SAAQ,mBAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IAErC;;;;;;;;;;;;;OAaG;gBACS,OAAO,EAAE,oBAAoB;IAgCzC;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAOf;;;;;;OAMG;IACG,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAW3F;;;;;;;;OAQG;IACG,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAa5E;;;;;;;;;;OAUG;IACG,GAAG,CACP,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,kBAAkB,EAE5B,WAAW,EAAE,eAAe,GAC3B,OAAO,CAAC,cAAc,CAAC;IAgB1B;;;;;;;;OAQG;IACG,SAAS,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB9F;;;;;;;OAOG;IACI,IAAI,CACT,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,qBAAqB,GAAG,SAAS,GACzC,cAAc,CAAC,eAAe,CAAC;IAiIlC;;;;;;;OAOG;YACW,4BAA4B;IAqG1C;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;CAStB"}
@@ -5,8 +5,6 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.DynamoDBSaver = void 0;
8
- const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
9
- const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
10
8
  const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
11
9
  const shared_1 = require("../shared");
12
10
  const actions_1 = require("./actions");
@@ -51,16 +49,11 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
51
49
  this.writesTableName = options.writesTableName;
52
50
  this.ttlDays = options.ttlDays;
53
51
  this.ttlSeconds = options.ttlSeconds;
54
- if (options.client) {
55
- this.client = options.client;
56
- this.ddbClient = undefined;
57
- this.ownsClient = false;
58
- }
59
- else {
60
- this.ddbClient = new client_dynamodb_1.DynamoDBClient(options.clientConfig || {});
61
- this.client = lib_dynamodb_1.DynamoDBDocument.from(this.ddbClient);
62
- this.ownsClient = true;
63
- }
52
+ ({
53
+ ddbClient: this.ddbClient,
54
+ client: this.client,
55
+ ownsClient: this.ownsClient,
56
+ } = (0, shared_1.resolveDynamoDBClient)(options));
64
57
  if (options.compression?.enabled) {
65
58
  this.compressor = new shared_1.Compressor(options.compression);
66
59
  }
@@ -93,21 +86,25 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
93
86
  * Delete a thread and all its checkpoints and writes
94
87
  *
95
88
  * @param threadId - The thread ID to delete
89
+ * @param options.signal - Optional AbortSignal for cancelling in-flight retries
96
90
  * @throws Error if validation fails or operation fails
97
91
  */
98
- async deleteThread(threadId) {
92
+ async deleteThread(threadId, options = {}) {
99
93
  return await (0, actions_1.deleteThreadAction)({
100
94
  client: this.client,
101
95
  checkpointsTableName: this.checkpointsTableName,
102
96
  writesTableName: this.writesTableName,
103
97
  threadId,
104
98
  s3Offloader: this.s3Offloader,
99
+ signal: options.signal,
105
100
  });
106
101
  }
107
102
  /**
108
103
  * Get a checkpoint tuple from DynamoDB
109
104
  *
110
- * @param config - Runnable configuration containing thread_id and optional checkpoint_id
105
+ * @param config - Runnable configuration containing thread_id and optional checkpoint_id.
106
+ * `config.signal` is honored as an AbortSignal: in-flight retries short-circuit
107
+ * and the returned promise rejects with the signal's abort reason.
111
108
  * @returns CheckpointTuple if found, undefined otherwise
112
109
  * @throws Error if validation fails or operation fails
113
110
  */
@@ -120,12 +117,14 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
120
117
  config,
121
118
  compressor: this.compressor,
122
119
  s3Offloader: this.s3Offloader,
120
+ signal: config.signal,
123
121
  });
124
122
  }
125
123
  /**
126
124
  * Save a checkpoint to DynamoDB
127
125
  *
128
- * @param config - Runnable configuration
126
+ * @param config - Runnable configuration. `config.signal` is honored as an
127
+ * AbortSignal and short-circuits retry backoff.
129
128
  * @param checkpoint - Checkpoint to save
130
129
  * @param metadata - Checkpoint metadata
131
130
  * @param newVersions - Channel versions (not used in DynamoDB implementation)
@@ -146,12 +145,14 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
146
145
  ttlSeconds: this.ttlSeconds,
147
146
  compressor: this.compressor,
148
147
  s3Offloader: this.s3Offloader,
148
+ signal: config.signal,
149
149
  });
150
150
  }
151
151
  /**
152
152
  * Save pending writes to DynamoDB
153
153
  *
154
- * @param config - Runnable configuration
154
+ * @param config - Runnable configuration. `config.signal` is honored as an
155
+ * AbortSignal and short-circuits retry backoff.
155
156
  * @param writes - Array of pending writes to save
156
157
  * @param taskId - Task ID for the writes
157
158
  * @throws Error if validation fails or operation fails
@@ -168,6 +169,7 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
168
169
  ttlSeconds: this.ttlSeconds,
169
170
  compressor: this.compressor,
170
171
  s3Offloader: this.s3Offloader,
172
+ signal: config.signal,
171
173
  });
172
174
  }
173
175
  /**
@@ -182,6 +184,10 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
182
184
  const { limit, before, filter } = options ?? {};
183
185
  const thread_id = config.configurable?.thread_id;
184
186
  const checkpoint_ns = config.configurable?.checkpoint_ns;
187
+ // `config.signal` is LangChain's idiomatic AbortSignal field. We propagate
188
+ // it into each retry cycle AND check it before yielding, so a consumer
189
+ // calling `controller.abort()` during iteration stops producing items.
190
+ const signal = config.signal;
185
191
  // Validate thread_id
186
192
  if (typeof thread_id !== 'string') {
187
193
  throw new Error('thread_id must be a string');
@@ -189,74 +195,84 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
189
195
  (0, utils_1.validateThreadId)(thread_id);
190
196
  // Validate limit if provided
191
197
  (0, utils_1.validateListLimit)(limit);
198
+ // Validate `before` checkpoint_id — LangGraph passes this back verbatim from
199
+ // a previous CheckpointTuple.config, but user callers can construct it
200
+ // manually. Without validation a malformed before-id could slip past the
201
+ // KeyCondition bound (payload items would get returned under `list({before})`).
202
+ const beforeCheckpointId = before?.configurable?.checkpoint_id;
203
+ if (beforeCheckpointId !== undefined) {
204
+ if (typeof beforeCheckpointId !== 'string') {
205
+ throw new Error('before.configurable.checkpoint_id must be a string');
206
+ }
207
+ (0, utils_1.validateCheckpointId)(beforeCheckpointId, false);
208
+ }
192
209
  const expressionAttributeValues = {
193
210
  ':thread_id': thread_id,
194
- ':payload_prefix': types_1.PAYLOAD_SK_PREFIX,
195
211
  ...(checkpoint_ns !== undefined && { ':checkpoint_ns': checkpoint_ns }),
196
212
  };
197
- // Filter by namespace when provided — applied after reading (costs RCU for
198
- // non-matching items, but avoids an additional GSI for the uncommon case).
199
- const filterExpression = checkpoint_ns !== undefined ? 'checkpoint_ns = :checkpoint_ns' : undefined;
200
- // Key condition excludes PAYLOAD# items — only metadata items are returned.
201
- // UUID checkpoint IDs (hex 0-9, a-f) sort before 'PAYLOAD#' in ASCII.
202
- let keyConditionExpression = 'thread_id = :thread_id AND checkpoint_id < :payload_prefix';
213
+ // Filter to metadata items only via the non-key `type` attribute (payload
214
+ // items don't carry `type`). We intentionally avoid using checkpoint_id in
215
+ // the KeyCondition to exclude payload items — any lex-bound approach
216
+ // silently drops metadata whose IDs sort above `PAYLOAD#`, which includes
217
+ // the very common case of lowercase-letter-prefixed IDs.
218
+ const filterParts = ['attribute_exists(#type)'];
219
+ if (checkpoint_ns !== undefined)
220
+ filterParts.push('checkpoint_ns = :checkpoint_ns');
221
+ const filterExpression = filterParts.join(' AND ');
222
+ // KeyCondition gates only by thread (and optional `before` sort-key bound).
223
+ let keyConditionExpression = 'thread_id = :thread_id';
203
224
  if (before?.configurable?.checkpoint_id) {
204
225
  keyConditionExpression = 'thread_id = :thread_id AND checkpoint_id < :before_checkpoint_id';
205
226
  expressionAttributeValues[':before_checkpoint_id'] = before.configurable.checkpoint_id;
206
- // No need for :payload_prefix — before_checkpoint_id is always a UUID which is < PAYLOAD#
207
227
  }
208
- // When filter is active, skip DynamoDB-level Limit since metadata filtering
209
- // is a post-filter (metadata is stored as binary and cannot be filtered server-side).
210
- const hasFilter = filter && Object.keys(filter).length > 0;
211
- const queryLimit = hasFilter ? undefined : limit;
212
- const result = await (0, shared_1.withDynamoDBRetry)(async () => {
213
- return await this.client.query({
214
- TableName: this.checkpointsTableName,
215
- KeyConditionExpression: keyConditionExpression,
216
- ExpressionAttributeValues: expressionAttributeValues,
217
- FilterExpression: filterExpression,
218
- Limit: queryLimit,
219
- ScanIndexForward: false, // Descending order
220
- });
221
- });
228
+ // When any server-side OR client-side filter reduces the returned set, we
229
+ // drop the DynamoDB Limit: Limit is applied BEFORE FilterExpression, so a
230
+ // small limit with heavy filtering truncates the result prematurely. The
231
+ // metadata filter (from CheckpointListOptions.filter) and the checkpoint_ns
232
+ // filter both qualify.
233
+ const hasMetadataFilter = filter && Object.keys(filter).length > 0;
234
+ const hasNsFilter = checkpoint_ns !== undefined;
235
+ const hasAnyFilter = hasMetadataFilter || hasNsFilter;
222
236
  let yieldedCount = 0;
223
- if (result.Items) {
224
- const items = result.Items;
225
- const payloadMap = await this.fetchCheckpointPayloadsBatch(items);
226
- for (const item of items) {
227
- const checkpointData = payloadMap.get(item.checkpoint_id) ?? new Uint8Array(0);
228
- const tuple = await (0, utils_1.deserializeCheckpointTuple)(item, checkpointData, this.serde, this.compressor, this.s3Offloader);
229
- // Apply metadata filter: skip checkpoints that don't match all filter entries
230
- if (hasFilter && !this.matchesFilter(tuple.metadata, filter)) {
231
- continue;
232
- }
233
- yield tuple;
234
- yieldedCount++;
235
- if (limit && yieldedCount >= limit)
236
- return;
237
+ let iterationCount = 0;
238
+ let lastEvaluatedKey;
239
+ do {
240
+ // Safety net against pathological filter queries: if a thread has millions
241
+ // of checkpoints and the filter matches almost none, the scan could loop
242
+ // for minutes burning RCU. Abort with a clear error rather than hanging.
243
+ iterationCount++;
244
+ if (iterationCount > shared_1.MAX_LOOP_ITERATIONS) {
245
+ throw new Error(`list() exceeded ${shared_1.MAX_LOOP_ITERATIONS} DynamoDB pages without reaching the requested limit. ` +
246
+ `This usually indicates a filter that matches very few items in a large thread — ` +
247
+ `narrow the filter, add a more selective namespace, or redesign with a GSI.`);
237
248
  }
238
- }
239
- // Paginate through remaining results if LastEvaluatedKey is present
240
- let lastEvaluatedKey = result.LastEvaluatedKey;
241
- while (lastEvaluatedKey) {
242
- const nextResult = await (0, shared_1.withDynamoDBRetry)(async () => {
249
+ const pageLimit = hasAnyFilter
250
+ ? undefined
251
+ : limit
252
+ ? Math.max(1, limit - yieldedCount)
253
+ : undefined;
254
+ signal?.throwIfAborted();
255
+ const result = await (0, shared_1.withDynamoDBRetry)(async () => {
243
256
  return await this.client.query({
244
257
  TableName: this.checkpointsTableName,
245
258
  KeyConditionExpression: keyConditionExpression,
246
259
  ExpressionAttributeValues: expressionAttributeValues,
260
+ ExpressionAttributeNames: { '#type': 'type' },
247
261
  FilterExpression: filterExpression,
248
- Limit: hasFilter ? undefined : limit ? limit - yieldedCount : undefined,
249
- ScanIndexForward: false,
262
+ Limit: pageLimit,
263
+ ScanIndexForward: false, // Descending order
250
264
  ExclusiveStartKey: lastEvaluatedKey,
251
265
  });
252
- });
253
- if (nextResult.Items) {
254
- const items = nextResult.Items;
255
- const payloadMap = await this.fetchCheckpointPayloadsBatch(items);
266
+ }, { signal });
267
+ if (result.Items) {
268
+ const items = result.Items;
269
+ const payloadMap = await this.fetchCheckpointPayloadsBatch(items, signal);
256
270
  for (const item of items) {
271
+ signal?.throwIfAborted();
257
272
  const checkpointData = payloadMap.get(item.checkpoint_id) ?? new Uint8Array(0);
258
273
  const tuple = await (0, utils_1.deserializeCheckpointTuple)(item, checkpointData, this.serde, this.compressor, this.s3Offloader);
259
- if (hasFilter && !this.matchesFilter(tuple.metadata, filter)) {
274
+ // Apply metadata filter: skip checkpoints that don't match all filter entries.
275
+ if (hasMetadataFilter && !this.matchesFilter(tuple.metadata, filter)) {
260
276
  continue;
261
277
  }
262
278
  yield tuple;
@@ -265,8 +281,8 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
265
281
  return;
266
282
  }
267
283
  }
268
- lastEvaluatedKey = nextResult.LastEvaluatedKey;
269
- }
284
+ lastEvaluatedKey = result.LastEvaluatedKey;
285
+ } while (lastEvaluatedKey);
270
286
  }
271
287
  /**
272
288
  * Fetch checkpoint payloads for a batch of metadata items.
@@ -276,7 +292,7 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
276
292
  * @param items - Checkpoint metadata items from a query result
277
293
  * @returns Map from checkpoint_id to raw checkpoint data
278
294
  */
279
- async fetchCheckpointPayloadsBatch(items) {
295
+ async fetchCheckpointPayloadsBatch(items, signal) {
280
296
  const payloadMap = new Map();
281
297
  // Partition items into groups
282
298
  const splitItems = []; // Need PAYLOAD# fetch
@@ -306,7 +322,7 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
306
322
  checkpoint_id: `${types_1.PAYLOAD_SK_PREFIX}${item.checkpoint_id}`,
307
323
  }));
308
324
  // Retry loop for UnprocessedKeys with exponential backoff
309
- let retryDelay = 100;
325
+ let retryDelay = shared_1.INITIAL_BACKOFF_DELAY_MS;
310
326
  let retryCount = 0;
311
327
  while (keys.length > 0) {
312
328
  const batchResult = await (0, shared_1.withDynamoDBRetry)(async () => {
@@ -315,12 +331,19 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
315
331
  [this.checkpointsTableName]: { Keys: keys },
316
332
  },
317
333
  });
318
- });
334
+ }, { signal });
319
335
  // Map results — BatchGetItem returns items in arbitrary order
320
336
  const responses = batchResult.Responses?.[this.checkpointsTableName];
321
337
  if (responses) {
322
338
  for (const response of responses) {
323
- // Extract original checkpoint_id from PAYLOAD#<id> sort key
339
+ // Sanity-check the sort key before trimming it. Without this, a corrupted
340
+ // or migrated row missing the PAYLOAD# prefix would silently produce a
341
+ // garbage `originalId` and ripple into the "payload not found" path below.
342
+ if (!response.checkpoint_id?.startsWith(types_1.PAYLOAD_SK_PREFIX)) {
343
+ throw new Error(`Unexpected payload item sort key in ${this.checkpointsTableName}: ` +
344
+ `expected to start with "${types_1.PAYLOAD_SK_PREFIX}" but got "${response.checkpoint_id}". ` +
345
+ `This indicates table corruption or a stale migration — investigate before retrying.`);
346
+ }
324
347
  const originalId = response.checkpoint_id.substring(types_1.PAYLOAD_SK_PREFIX.length);
325
348
  payloadMap.set(originalId, response.checkpoint);
326
349
  }
@@ -333,8 +356,10 @@ class DynamoDBSaver extends langgraph_checkpoint_1.BaseCheckpointSaver {
333
356
  throw new Error(`Failed to fetch all checkpoint payloads after ${shared_1.MAX_UNPROCESSED_RETRIES} retries. ` +
334
357
  `${unprocessed.length} keys remain unprocessed.`);
335
358
  }
336
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
337
- retryDelay = Math.min(retryDelay * 2, 5000);
359
+ // Full jitter so list() calls across concurrent workers don't synchronize
360
+ // and re-pressure the partition on every retry cycle.
361
+ await (0, shared_1.sleep)((0, shared_1.fullJitter)(retryDelay), signal);
362
+ retryDelay = (0, shared_1.nextBackoffDelay)(retryDelay);
338
363
  keys = unprocessed;
339
364
  }
340
365
  else {