@farukada/aws-langgraph-dynamodb-ts 0.1.1 → 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 +33 -22
package/README.md CHANGED
@@ -171,6 +171,14 @@ await history.addMessages('user-123', 'session-456', [
171
171
  const sessions = await history.listSessions('user-123');
172
172
  ```
173
173
 
174
+ > **TTL semantics.** The session metadata item's TTL is refreshed on every
175
+ > `addMessage`/`addMessages` call, so `listSessions()` keeps reporting the
176
+ > session as live while activity continues. Individual message items get their
177
+ > TTL stamped at write time and expire independently — a long-lived session can
178
+ > develop gaps where older messages drop out while recent ones persist. If that
179
+ > isn't acceptable, set `ttlDays` well above your expected session lifetime or
180
+ > manage deletion explicitly via `clear()`.
181
+
174
182
  ### Factory (One-Liner)
175
183
 
176
184
  ```typescript
@@ -327,13 +335,16 @@ When TTL and S3 offloading are both enabled, the library automatically configure
327
335
  ### Store Filters
328
336
 
329
337
  ```typescript
330
- // JSONPath-based filtering: $eq, $ne, $gt, $gte, $lt, $lte
338
+ // Supported operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin.
339
+ // Filter keys are fields inside the stored `value` — the library wraps each
340
+ // key as `value.<key>` when building the DynamoDB FilterExpression.
331
341
  const [results] = await store.batch([
332
342
  {
333
343
  namespacePrefix: ['products'],
334
344
  filter: {
335
- 'value.price': { $gte: 10, $lte: 100 },
336
- 'value.category': { $eq: 'electronics' },
345
+ price: { $gte: 10, $lte: 100 },
346
+ category: { $in: ['electronics', 'books'] },
347
+ status: { $ne: 'archived' },
337
348
  },
338
349
  limit: 10,
339
350
  },
@@ -349,6 +360,89 @@ const [results] = await store.batch([
349
360
  ['documents', 'category', 'subcategory']
350
361
  ```
351
362
 
363
+ ### LangChain `RunnableWithMessageHistory` integration
364
+
365
+ `DynamoDBChatMessageHistory` is a multi-session store; to hand a single
366
+ `(userId, sessionId)` pair to LangChain's `RunnableWithMessageHistory`, bind it
367
+ with `forSession()` which returns a `BaseListChatMessageHistory` compatible
368
+ instance:
369
+
370
+ ```typescript
371
+ import { RunnableWithMessageHistory } from '@langchain/core/runnables';
372
+ import { DynamoDBChatMessageHistory } from '@farukada/aws-langgraph-dynamodb-ts';
373
+
374
+ const store = new DynamoDBChatMessageHistory({ tableName: 'chat-sessions' });
375
+
376
+ const chain = new RunnableWithMessageHistory({
377
+ runnable,
378
+ getMessageHistory: (sessionId) => store.forSession(userId, sessionId),
379
+ inputMessagesKey: 'input',
380
+ historyMessagesKey: 'history',
381
+ });
382
+ ```
383
+
384
+ ### Atomicity contract
385
+
386
+ | Operation | Atomicity scope | Failure mode |
387
+ |---|---|---|
388
+ | `DynamoDBSaver.put()` | Single `TransactWrite` on the **checkpoints** table. Both the metadata item and the payload item either both persist or neither does. | Throws; any S3 objects already uploaded are best-effort cleaned, otherwise swept by the S3 lifecycle rule. |
389
+ | `DynamoDBSaver.putWrites()` | `BatchWriteItem` against the **writes** table, chunked in groups of 25. Each 25-item batch is independently atomic at the DynamoDB level; batches are *not* transactional with each other. | On retry exhaustion, throws `BatchWriteIncompleteError` with `.succeededCount` and `.unprocessed` so callers can drive reconciliation. Items already acked by DynamoDB remain persisted. |
390
+ | `put()` ↔ `putWrites()` | **Not** atomic across tables. LangGraph calls them as distinct steps; a crash between them leaves a checkpoint without its writes (or vice versa). | LangGraph's node-retry loop will re-invoke both; the idempotency guard on `put()` makes re-submission safe. |
391
+ | `DynamoDBChatMessageHistory.addMessage(s)` | `TransactWriteItems` — up to 99 message items plus the metadata counter update, guarded by an optimistic `messageCount` condition. All-or-nothing per call. | Throws on conflict; the helper retries up to 5× on `ConditionalCheckFailed` sub-reasons. |
392
+ | `DynamoDBStore.batch()` (Put) | Per-item `UpdateItem` / `PutItem`. Batch failure is per-operation. | Each op's error propagates via `Promise.all`. |
393
+
394
+ ### `put()` optimistic-concurrency guard
395
+
396
+ Every `put()` writes metadata with a `ConditionExpression` that rejects the
397
+ transaction when a pre-existing row for the same `(thread_id, checkpoint_id)`
398
+ disagrees on `parent_checkpoint_id` or serializer `type`. Effect:
399
+
400
+ - A fresh checkpoint always succeeds.
401
+ - A retry after a transient error (network blip, throttling) re-issues the same
402
+ `(parent, type)` and succeeds as if idempotent.
403
+ - Two concurrent workers writing the same `checkpoint_id` but with different
404
+ lineage surface a `ConditionalCheckFailedException` instead of silently
405
+ last-writer-wins. Treat it as a signal that your application is accidentally
406
+ racing two graphs on the same thread.
407
+
408
+ ### Limits and safety caps
409
+
410
+ The library enforces a set of client-side caps to produce clean, actionable
411
+ errors before DynamoDB / zlib / memory blow up on pathological inputs. All of
412
+ them are configurable or can be raised if a legitimate workload needs more.
413
+
414
+ | Cap | Default | Raise via | Hit when |
415
+ |---|---|---|---|
416
+ | Decompressed checkpoint size | 50 MiB | `compression.maxDecompressedBytes` | Gzip payload expands beyond the cap — defends against gzip bombs. |
417
+ | Filter expression string | 3.5 KiB | not configurable — simplify the filter | Complex / deeply-nested `Store.batch` searches. DynamoDB's hard limit is 4 KiB. |
418
+ | `$in` / `$nin` array size | 50 values | not configurable — split the query | Store filter with oversized enum sets. |
419
+ | `list()` page iterations | 1000 pages | not configurable — narrow the filter or add a GSI | Filters that match almost nothing across a very large thread. |
420
+ | `deleteThread()` page iterations | 10 000 pages | not configurable — investigate the thread | Effectively "unbounded for real workloads"; only trips on extreme datasets. |
421
+ | Retry `cause`-chain depth | 32 | not configurable | Hostile error with an absurd nesting depth; prevents stack-overflow DoS. |
422
+
423
+ ### S3 encryption
424
+
425
+ Offloaded checkpoint payloads are written with `ServerSideEncryption: AES256`
426
+ by default (matching S3's own default since 2023). If your bucket policy
427
+ enforces a different algorithm (e.g. `aws:kms`), set it explicitly:
428
+
429
+ ```typescript
430
+ s3OffloadConfig: {
431
+ bucketName: 'my-checkpoints-bucket',
432
+ serverSideEncryption: 'aws:kms',
433
+ sseKmsKeyId: 'alias/my-key',
434
+ }
435
+ ```
436
+
437
+ IAM: the `s3:PutObject` permission granted in the minimum policy above is
438
+ sufficient. If your bucket policy denies the `AES256` algorithm, either grant
439
+ the key permissions required for `aws:kms` or set `serverSideEncryption`
440
+ explicitly to match the bucket policy.
441
+
442
+ ### Checkpoint data migration (v<4)
443
+
444
+ LangGraph bumped the `Checkpoint.v` field to **4** in `@langchain/langgraph-checkpoint@1.0`. Reading data written by older versions (v<4) requires synthesizing the `TASKS` channel from parent-checkpoint writes — this library does not migrate on read. If you have pre-1.0 data, run LangGraph's reference migration before switching to this saver, or drop the older checkpoints.
445
+
352
446
  <a id="configuration-reference"></a>
353
447
 
354
448
  ## Configuration Reference
@@ -473,13 +567,45 @@ const [results] = await store.batch([
473
567
 
474
568
  ## Testing
475
569
 
570
+ ### Unit tests
571
+
476
572
  ```bash
477
- npm test # Run all tests
573
+ npm test # Run all tests (672 unit tests, ~28s)
478
574
  npm test -- --coverage # With coverage
479
575
  npm run build # Type-check + compile
480
576
  npm run lint # ESLint
481
577
  ```
482
578
 
579
+ ### Integration tests (DynamoDB Local)
580
+
581
+ A dedicated integration tier verifies the library against a real DynamoDB API
582
+ (catches things `aws-sdk-client-mock` cannot, such as `ValidationException` on
583
+ malformed filter expressions, `ConditionExpression` enforcement, primary-key
584
+ attribute rules, and lex-sort assumptions on sort keys).
585
+
586
+ ```bash
587
+ npm run test:integration:up # docker compose up (amazon/dynamodb-local)
588
+ npm run test:integration # 15 e2e tests against localhost:8000
589
+ npm run test:integration:down # docker compose down
590
+ ```
591
+
592
+ CI runs the same suite via a `DynamoDB Local` GitHub Actions service container
593
+ on every pull request.
594
+
595
+ ### Mutation testing (Stryker)
596
+
597
+ Line coverage tells you what code ran; **mutation coverage** tells you what
598
+ tests would *catch regressions*. Run against a hot spot:
599
+
600
+ ```bash
601
+ npm run test:mutate:quick # retry.ts, compressor.ts, filter.ts (~15 min)
602
+ npm run test:mutate # full src/ (long — use incremental mode)
603
+ ```
604
+
605
+ Stryker runs with `incremental: true`, so re-runs only re-check files that
606
+ changed since the last run. Baseline kill-rate goal: **≥80%** for new code in
607
+ `src/shared/` and `src/checkpointer/`.
608
+
483
609
  <a id="project-structure"></a>
484
610
 
485
611
  ## Project Structure
@@ -526,5 +652,5 @@ MIT © [FarukAda](https://github.com/farukada)
526
652
  <p align="center">
527
653
  Built with <a href="https://langchain-ai.github.io/langgraphjs/">LangGraph</a> · <a href="https://aws.amazon.com/sdk-for-javascript/">AWS SDK v3</a> · <a href="https://github.com/langchain-ai/langchainjs">LangChain</a>
528
654
  <br/>
529
- <a href="https://www.npmjs.com/package/@farukada/aws-langgraph-dynamodb-ts">npm</a> · <a href="https://github.com/farukada/aws-langgraph-dynamodb-ts">GitHub</a> · <a href="https://github.com/farukada/aws-langgraph-dynamodb-ts/issues">Issues</a>
655
+ <a href="https://www.npmjs.com/package/@farukada/aws-langgraph-dynamodb-ts">npm</a> · <a href="https://github.com/FarukAda/aws-langgraph-dynamodb-ts">GitHub</a> · <a href="https://github.com/FarukAda/aws-langgraph-dynamodb-ts/issues">Issues</a>
530
656
  </p>
@@ -1 +1 @@
1
- {"version":3,"file":"delete-thread.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,wBAAwB,EAG9B,MAAM,UAAU,CAAC;AAclB;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAU,QAAQ,wBAAwB,KAAG,OAAO,CAAC,IAAI,CAwIvF,CAAC"}
1
+ {"version":3,"file":"delete-thread.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,wBAAwB,EAG9B,MAAM,UAAU,CAAC;AAclB;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAU,QAAQ,wBAAwB,KAAG,OAAO,CAAC,IAAI,CAsJvF,CAAC"}
@@ -14,16 +14,23 @@ const writer_1 = require("./writer");
14
14
  const deleteThreadAction = async (params) => {
15
15
  // Validate thread ID
16
16
  (0, utils_1.validateThreadId)(params.threadId);
17
- // Query returns BOTH metadata items and PAYLOAD# items for the thread
17
+ // Query returns BOTH metadata items and PAYLOAD# items for the thread.
18
+ // We don't set a per-query Limit — DynamoDB's default 1 MB page is far more
19
+ // efficient than many small 100-item queries. The real safeguard is the
20
+ // MAX_DELETE_BATCH_SIZE total-item cap below.
18
21
  const allItems = [];
19
22
  let lastEvaluatedKey;
20
23
  let iterationCount = 0;
21
- const MAX_ITERATIONS = 10;
22
- // Fetch all items with pagination and safety limits
24
+ // Deliberately higher than `MAX_LOOP_ITERATIONS` used in list()/search: deletes
25
+ // must walk every page of a thread (there's no early-exit when a user limit is
26
+ // satisfied), so the bound is set to "effectively unbounded for practical
27
+ // workloads". The real guard is MAX_DELETE_BATCH_SIZE total items held in memory.
28
+ const MAX_DELETE_PAGES = 10_000;
23
29
  do {
24
30
  iterationCount++;
25
- if (iterationCount > MAX_ITERATIONS) {
26
- throw new Error('Delete operation exceeded maximum iteration limit');
31
+ if (iterationCount > MAX_DELETE_PAGES) {
32
+ throw new Error(`deleteThread scanned more than ${MAX_DELETE_PAGES} DynamoDB pages without completing. ` +
33
+ `The thread has an extreme number of checkpoints — investigate before retrying.`);
27
34
  }
28
35
  const result = await (0, shared_1.withDynamoDBRetry)(async () => {
29
36
  return await params.client.query({
@@ -32,10 +39,9 @@ const deleteThreadAction = async (params) => {
32
39
  ExpressionAttributeValues: {
33
40
  ':thread_id': params.threadId,
34
41
  },
35
- Limit: 100, // Fetch in smaller batches
36
42
  ExclusiveStartKey: lastEvaluatedKey,
37
43
  });
38
- });
44
+ }, { signal: params.signal });
39
45
  if (result.Items && result.Items.length > 0) {
40
46
  allItems.push(...result.Items);
41
47
  // Safety check: prevent deleting too many items at once
@@ -78,7 +84,7 @@ const deleteThreadAction = async (params) => {
78
84
  },
79
85
  },
80
86
  }));
81
- await (0, shared_1.batchWriteAllWithRetry)(params.client, params.checkpointsTableName, checkpointDeleteRequests);
87
+ await (0, shared_1.batchWriteAllWithRetry)(params.client, params.checkpointsTableName, checkpointDeleteRequests, { signal: params.signal });
82
88
  // Fetch writes for metadata items only (payload items don't have associated writes)
83
89
  const allWriteDeleteRequests = [];
84
90
  const QUERY_CONCURRENCY = 10;
@@ -108,7 +114,9 @@ const deleteThreadAction = async (params) => {
108
114
  }
109
115
  }
110
116
  // Delete all writes using shared batch write utility
111
- await (0, shared_1.batchWriteAllWithRetry)(params.client, params.writesTableName, allWriteDeleteRequests);
117
+ await (0, shared_1.batchWriteAllWithRetry)(params.client, params.writesTableName, allWriteDeleteRequests, {
118
+ signal: params.signal,
119
+ });
112
120
  // Clean up S3 offloaded data
113
121
  if (params.s3Offloader && s3KeysToDelete.length > 0) {
114
122
  await params.s3Offloader.deleteBatch(s3KeysToDelete);
@@ -1 +1 @@
1
- {"version":3,"file":"delete-thread.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":";;;AAEA,yCAAyE;AACzE,oCAKkB;AAClB,oCAA6E;AAC7E,qCAAkC;AAYlC;;;;;GAKG;AACI,MAAM,kBAAkB,GAAG,KAAK,EAAE,MAAgC,EAAiB,EAAE;IAC1F,qBAAqB;IACrB,IAAA,wBAAgB,EAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAElC,sEAAsE;IACtE,MAAM,QAAQ,GAA6B,EAAE,CAAC;IAC9C,IAAI,gBAAqD,CAAC;IAC1D,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,MAAM,cAAc,GAAG,EAAE,CAAC;IAE1B,oDAAoD;IACpD,GAAG,CAAC;QACF,cAAc,EAAE,CAAC;QACjB,IAAI,cAAc,GAAG,cAAc,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAA,0BAAiB,EAAC,KAAK,IAAI,EAAE;YAChD,OAAO,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;gBAC/B,SAAS,EAAE,MAAM,CAAC,oBAAoB;gBACtC,sBAAsB,EAAE,wBAAwB;gBAChD,yBAAyB,EAAE;oBACzB,YAAY,EAAE,MAAM,CAAC,QAAQ;iBAC9B;gBACD,KAAK,EAAE,GAAG,EAAE,2BAA2B;gBACvC,iBAAiB,EAAE,gBAAgB;aACpC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,QAAQ,CAAC,IAAI,CAAC,GAAI,MAAM,CAAC,KAAkC,CAAC,CAAC;YAE7D,wDAAwD;YACxD,IAAI,QAAQ,CAAC,MAAM,GAAG,uCAA+B,CAAC,qBAAqB,EAAE,CAAC;gBAC5E,MAAM,IAAI,KAAK,CACb,+BAA+B,uCAA+B,CAAC,qBAAqB,yCAAyC,CAC9H,CAAC;YACJ,CAAC;QACH,CAAC;QAED,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAC7C,CAAC,QAAQ,gBAAgB,EAAE;IAE3B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,oBAAoB;IAC9B,CAAC;IAED,6CAA6C;IAC7C,MAAM,aAAa,GAAqB,EAAE,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,iEAAiE;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC;QAClC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,yBAAiB,CAAC,EAAE,CAAC;YACxE,aAAa,CAAC,IAAI,CAAC,IAAiC,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC3B,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAC9C,CAAC;YACD,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;gBACzB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IAED,iEAAiE;IACjE,MAAM,wBAAwB,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvD,aAAa,EAAE;YACb,GAAG,EAAE;gBACH,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,aAAa,EAAE,IAAI,CAAC,aAAa;aAClC;SACF;KACF,CAAC,CAAC,CAAC;IAEJ,MAAM,IAAA,+BAAsB,EAC1B,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,oBAAoB,EAC3B,wBAAwB,CACzB,CAAC;IAEF,oFAAoF;IACpF,MAAM,sBAAsB,GAOvB,EAAE,CAAC;IAER,MAAM,iBAAiB,GAAG,EAAE,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,IAAI,iBAAiB,EAAE,CAAC;QACjE,MAAM,eAAe,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,iBAAiB,CAAC,CAAC;QACtE,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CACrC,eAAe,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CACjC,2BAA2B,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,UAAU,CAAC,CAC/E,CACF,CAAC;QAEF,KAAK,MAAM,UAAU,IAAI,aAAa,EAAE,CAAC;YACvC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,cAAc,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;oBAC/C,aAAa,EAAE;wBACb,GAAG,EAAE;4BACH,qCAAqC,EAAE,IAAI,CAAC,qCAAqC;4BACjF,WAAW,EAAE,IAAI,CAAC,WAAW;yBAC9B;qBACF;iBACF,CAAC,CAAC,CAAC;gBACJ,sBAAsB,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC;gBAE/C,mCAAmC;gBACnC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;oBACvB,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;wBAC9B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;4BACtB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;wBACzC,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,qDAAqD;IACrD,MAAM,IAAA,+BAAsB,EAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,sBAAsB,CAAC,CAAC;IAE5F,6BAA6B;IAC7B,IAAI,MAAM,CAAC,WAAW,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;IACvD,CAAC;AACH,CAAC,CAAC;AAxIW,QAAA,kBAAkB,sBAwI7B;AAEF;;;;;;;;GAQG;AACH,KAAK,UAAU,2BAA2B,CACxC,MAAwB,EACxB,SAAiB,EACjB,UAA0B;IAE1B,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,IAAI,OAA4C,CAAC;IAEjD,GAAG,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,IAAA,0BAAiB,EAAC,KAAK,IAAI,EAAE;YAChD,OAAO,MAAM,MAAM,CAAC,KAAK,CAAC;gBACxB,SAAS,EAAE,SAAS;gBACpB,sBAAsB,EAAE,6CAA6C;gBACrE,yBAAyB,EAAE;oBACzB,KAAK,EAAE,eAAM,CAAC,eAAe,CAAC;wBAC5B,SAAS,EAAE,UAAU,CAAC,SAAS;wBAC/B,aAAa,EAAE,UAAU,CAAC,aAAa;wBACvC,aAAa,EAAE,UAAU,CAAC,aAAa;qBACxC,CAAC;iBACH;gBACD,iBAAiB,EAAE,OAAO;aAC3B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,QAAQ,CAAC,IAAI,CAAC,GAAI,MAAM,CAAC,KAA6B,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;IACpC,CAAC,QAAQ,OAAO,EAAE;IAElB,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"delete-thread.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/delete-thread.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-tuple.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/get-tuple.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA0B,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAG/F,OAAO,EAIL,KAAK,oBAAoB,EAG1B,MAAM,UAAU,CAAC;AAKlB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,oBAAoB,KAC3B,OAAO,CAAC,eAAe,GAAG,SAAS,CA4HrC,CAAC"}
1
+ {"version":3,"file":"get-tuple.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/get-tuple.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA0B,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAG/F,OAAO,EAIL,KAAK,oBAAoB,EAG1B,MAAM,UAAU,CAAC;AAKlB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,oBAAoB,KAC3B,OAAO,CAAC,eAAe,GAAG,SAAS,CAmLrC,CAAC"}
@@ -30,31 +30,55 @@ const getTupleAction = async (params) => {
30
30
  thread_id: configurable.thread_id,
31
31
  checkpoint_id: configurable.checkpoint_id,
32
32
  },
33
+ ConsistentRead: true,
33
34
  });
34
- });
35
+ }, { signal: params.signal });
35
36
  return item.Item;
36
37
  }
37
38
  else {
38
- const result = await (0, shared_1.withDynamoDBRetry)(async () => {
39
- return await params.client.query({
40
- TableName: params.checkpointsTableName,
41
- KeyConditionExpression: 'thread_id = :thread_id AND checkpoint_id < :payload_prefix',
42
- ExpressionAttributeValues: {
43
- ':thread_id': configurable.thread_id,
44
- ':payload_prefix': types_1.PAYLOAD_SK_PREFIX,
45
- ...(configurable.checkpoint_ns && {
46
- ':checkpoint_ns': configurable.checkpoint_ns,
47
- }),
48
- },
49
- ...(configurable.checkpoint_ns && {
50
- FilterExpression: 'checkpoint_ns = :checkpoint_ns',
51
- }),
52
- Limit: 1,
53
- ConsistentRead: true,
54
- ScanIndexForward: false, // Descending order
55
- });
56
- });
57
- return result.Items?.[0];
39
+ // Select metadata items only. We cannot bound by `checkpoint_id` in the
40
+ // KeyCondition — any attempt to exclude payload items via `checkpoint_id
41
+ // < 'PAYLOAD#'` silently drops metadata whose IDs sort lexicographically
42
+ // above `PAYLOAD#` (e.g. anything starting with a lowercase letter,
43
+ // which includes common IDs like `ckpt-1`, `checkpoint-2`, etc.), and
44
+ // DynamoDB forbids primary-key attributes inside FilterExpression.
45
+ // Instead, filter on the non-key `type` attribute that only metadata
46
+ // items carry (`attribute_exists(#type)`). Payload items are never
47
+ // returned, regardless of user ID character set.
48
+ //
49
+ // Because Limit applies *before* FilterExpression in DynamoDB, when
50
+ // mixing metadata and payload pages we fetch a larger page and paginate
51
+ // until a metadata item is found. For high-volume threads the ordering
52
+ // still surfaces the newest metadata within the first 1-2 pages.
53
+ const hasNsFilter = !!configurable.checkpoint_ns;
54
+ const filterParts = ['attribute_exists(#type)'];
55
+ if (hasNsFilter)
56
+ filterParts.push('checkpoint_ns = :checkpoint_ns');
57
+ const expressionValues = {
58
+ ':thread_id': configurable.thread_id,
59
+ ...(hasNsFilter && { ':checkpoint_ns': configurable.checkpoint_ns }),
60
+ };
61
+ let lastKey;
62
+ do {
63
+ const result = await (0, shared_1.withDynamoDBRetry)(async () => {
64
+ return await params.client.query({
65
+ TableName: params.checkpointsTableName,
66
+ KeyConditionExpression: 'thread_id = :thread_id',
67
+ ExpressionAttributeValues: expressionValues,
68
+ ExpressionAttributeNames: { '#type': 'type' },
69
+ FilterExpression: filterParts.join(' AND '),
70
+ ConsistentRead: true,
71
+ ScanIndexForward: false, // Descending — newest first
72
+ ExclusiveStartKey: lastKey,
73
+ });
74
+ }, { signal: params.signal });
75
+ const items = result.Items;
76
+ if (items && items.length > 0) {
77
+ return items[0];
78
+ }
79
+ lastKey = result.LastEvaluatedKey;
80
+ } while (lastKey);
81
+ return undefined;
58
82
  }
59
83
  };
60
84
  const item = await getItem((0, validate_configurable_1.validateConfigurable)(params.config.configurable));
@@ -74,7 +98,10 @@ const getTupleAction = async (params) => {
74
98
  checkpointData = item.checkpoint;
75
99
  }
76
100
  else {
77
- // New split format — fetch payload item separately
101
+ // New split format — fetch payload item separately.
102
+ // ConsistentRead must match the metadata read above: without it, a freshly-written
103
+ // payload can be missing from an eventually-consistent read even though the metadata
104
+ // item said it exists, producing spurious "payload not found" errors under write load.
78
105
  const payloadResult = await (0, shared_1.withDynamoDBRetry)(async () => {
79
106
  return await params.client.get({
80
107
  TableName: params.checkpointsTableName,
@@ -82,8 +109,9 @@ const getTupleAction = async (params) => {
82
109
  thread_id: item.thread_id,
83
110
  checkpoint_id: `${types_1.PAYLOAD_SK_PREFIX}${item.checkpoint_id}`,
84
111
  },
112
+ ConsistentRead: true,
85
113
  });
86
- });
114
+ }, { signal: params.signal });
87
115
  const payloadItem = payloadResult.Item;
88
116
  if (!payloadItem) {
89
117
  throw new Error(`Checkpoint payload item not found for thread_id=${item.thread_id}, checkpoint_id=${item.checkpoint_id}`);
@@ -94,6 +122,9 @@ const getTupleAction = async (params) => {
94
122
  const allWriteItems = [];
95
123
  let writesLastEvaluatedKey;
96
124
  do {
125
+ // ConsistentRead is required here: putWrites races with getTuple under concurrent
126
+ // workers, and an eventually-consistent Query can return a stale view that omits
127
+ // writes that have already been acked.
97
128
  const writesResult = await (0, shared_1.withDynamoDBRetry)(async () => {
98
129
  return await params.client.query({
99
130
  TableName: params.writesTableName,
@@ -101,27 +132,34 @@ const getTupleAction = async (params) => {
101
132
  ExpressionAttributeValues: {
102
133
  ':thread_id_checkpoint_id_checkpoint_ns': writer_1.Writer.getPartitionKey(item),
103
134
  },
135
+ ConsistentRead: true,
104
136
  ExclusiveStartKey: writesLastEvaluatedKey,
105
137
  });
106
- });
138
+ }, { signal: params.signal });
107
139
  if (writesResult.Items) {
108
140
  allWriteItems.push(...writesResult.Items);
109
141
  }
110
142
  writesLastEvaluatedKey = writesResult.LastEvaluatedKey;
111
143
  } while (writesLastEvaluatedKey);
112
- const pendingWrites = [];
113
- for (const writeItem of allWriteItems) {
144
+ // Deserialize pending writes in parallel. Each write may involve an S3 download
145
+ // plus decompress + serde — sequential awaits in a hot path were measurable on
146
+ // checkpoints with many offloaded writes.
147
+ const pendingWrites = await Promise.all(allWriteItems.map(async (writeItem) => {
114
148
  const write = writer_1.Writer.fromDynamoDBItem(writeItem);
115
- // Download from S3 if offloaded
116
149
  let rawValue = write.value;
117
- if (writeItem.s3_value_key && params.s3Offloader) {
150
+ if (writeItem.s3_value_key) {
151
+ if (!params.s3Offloader) {
152
+ throw new Error(`Pending write references S3 key '${writeItem.s3_value_key}' but no S3 offloader is configured. ` +
153
+ `Pass s3OffloadConfig when constructing DynamoDBSaver to read offloaded writes.`);
154
+ }
118
155
  rawValue = await params.s3Offloader.download(writeItem.s3_value_key);
119
156
  }
120
- // Decompress write value if compressor is provided (auto-detects gzip)
121
- const writeValue = params.compressor ? await params.compressor.decompress(rawValue) : rawValue;
157
+ const writeValue = params.compressor
158
+ ? await params.compressor.decompress(rawValue)
159
+ : rawValue;
122
160
  const value = await params.serde.loadsTyped(write.type, writeValue);
123
- pendingWrites.push([write.task_id, write.channel, value]);
124
- }
161
+ return [write.task_id, write.channel, value];
162
+ }));
125
163
  // Deserialize the checkpoint tuple from metadata item + payload data
126
164
  const checkpointTuple = await (0, utils_1.deserializeCheckpointTuple)(item, checkpointData, params.serde, params.compressor, params.s3Offloader);
127
165
  // Add pending writes to the tuple
@@ -1 +1 @@
1
- {"version":3,"file":"get-tuple.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/get-tuple.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get-tuple.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/get-tuple.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"put-writes.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAKjD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAU,QAAQ,qBAAqB,KAAG,OAAO,CAAC,IAAI,CAsEjF,CAAC"}
1
+ {"version":3,"file":"put-writes.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAKjD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAU,QAAQ,qBAAqB,KAAG,OAAO,CAAC,IAAI,CAqFjF,CAAC"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.putWritesAction = void 0;
4
+ const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
4
5
  const shared_1 = require("../../shared");
5
6
  const utils_1 = require("../utils");
6
7
  const validate_configurable_1 = require("./validate-configurable");
@@ -20,12 +21,26 @@ const putWritesAction = async (params) => {
20
21
  (0, utils_1.validateWritesCount)(params.writes.length);
21
22
  (0, utils_1.validateTaskId)(params.taskId);
22
23
  (0, utils_1.validateTTLDays)(params.ttlDays);
23
- const writeItems = await Promise.all(params.writes.map(async (write, idx) => {
24
+ // Compute TTL once (ttlSeconds takes precedence) — not per message.
25
+ let ttl;
26
+ if (params.ttlSeconds !== undefined) {
27
+ ttl = (0, shared_1.calculateTTLTimestampFromSeconds)(params.ttlSeconds);
28
+ }
29
+ else if (params.ttlDays !== undefined) {
30
+ ttl = (0, shared_1.calculateTTLTimestamp)(params.ttlDays);
31
+ }
32
+ const writeItems = await Promise.all(params.writes.map(async (write, positionalIdx) => {
24
33
  const [type, rawValue] = await params.serde.dumpsTyped(write[1]);
25
34
  // Compress after serialization if compressor is provided
26
35
  const serializedValue = params.compressor
27
36
  ? await params.compressor.compress(rawValue)
28
37
  : rawValue;
38
+ // Special channels (__error__, __scheduled__, __interrupt__, __resume__) are
39
+ // stored at the stable negative idx from WRITES_IDX_MAP so repeated writes
40
+ // for the same taskId overwrite the correct dedicated slot. Regular writes
41
+ // use their positional index. Matches memory.js:178 from langgraph-checkpoint.
42
+ const channel = write[0];
43
+ const idx = langgraph_checkpoint_1.WRITES_IDX_MAP[channel] ?? positionalIdx;
29
44
  // S3 offloading for large write values
30
45
  let s3ValueKey;
31
46
  let storedValue = serializedValue;
@@ -39,30 +54,32 @@ const putWritesAction = async (params) => {
39
54
  checkpoint_id,
40
55
  task_id: params.taskId,
41
56
  idx,
42
- channel: write[0],
57
+ channel,
43
58
  type,
44
59
  value: storedValue,
45
60
  });
46
61
  const dynamoItem = item.toDynamoDBItem();
47
- // Add S3 reference if offloaded
48
- if (s3ValueKey) {
62
+ if (s3ValueKey)
49
63
  dynamoItem.s3_value_key = s3ValueKey;
50
- }
51
- // Add TTL to the item that will actually be saved (ttlSeconds takes precedence)
52
- if (params.ttlSeconds !== undefined) {
53
- dynamoItem.ttl = (0, shared_1.calculateTTLTimestampFromSeconds)(params.ttlSeconds);
54
- }
55
- else if (params.ttlDays !== undefined) {
56
- dynamoItem.ttl = (0, shared_1.calculateTTLTimestamp)(params.ttlDays);
57
- }
58
- return {
59
- PutRequest: {
60
- Item: dynamoItem,
61
- },
62
- };
64
+ if (ttl !== undefined)
65
+ dynamoItem.ttl = ttl;
66
+ return { PutRequest: { Item: dynamoItem } };
63
67
  }));
64
- // Batch writes using shared utility with retry logic
65
- await (0, shared_1.batchWriteAllWithRetry)(params.client, params.writesTableName, writeItems);
68
+ // Batch writes using shared utility with retry logic.
69
+ // On failure, best-effort-clean any S3 objects we uploaded — errors during cleanup
70
+ // are only logged since the lifecycle rule is the ultimate backstop.
71
+ try {
72
+ await (0, shared_1.batchWriteAllWithRetry)(params.client, params.writesTableName, writeItems, {
73
+ signal: params.signal,
74
+ });
75
+ }
76
+ catch (err) {
77
+ if (params.s3Offloader) {
78
+ const keys = writeItems.map((w) => w.PutRequest.Item.s3_value_key);
79
+ await (0, shared_1.cleanUpS3Orphans)(params.s3Offloader, keys, 'putWrites failure');
80
+ }
81
+ throw err;
82
+ }
66
83
  };
67
84
  exports.putWritesAction = putWritesAction;
68
85
  //# sourceMappingURL=put-writes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"put-writes.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":";;;AAAA,yCAIsB;AAEtB,oCAAgF;AAChF,mEAA+D;AAC/D,qCAAkC;AAElC;;;;;GAKG;AACI,MAAM,eAAe,GAAG,KAAK,EAAE,MAA6B,EAAiB,EAAE;IACpF,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,IAAA,4CAAoB,EACtE,MAAM,CAAC,MAAM,CAAC,YAAY,CAC3B,CAAC;IAEF,IAAI,aAAa,IAAI,IAAI,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IAED,kBAAkB;IAClB,IAAA,2BAAmB,EAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAA,sBAAc,EAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,IAAA,uBAAe,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEhC,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjE,yDAAyD;QACzD,MAAM,eAAe,GAAG,MAAM,CAAC,UAAU;YACvC,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC5C,CAAC,CAAC,QAAQ,CAAC;QAEb,uCAAuC;QACvC,IAAI,UAA8B,CAAC;QACnC,IAAI,WAAW,GAAe,eAAe,CAAC;QAE9C,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,eAAe,CAAC,EAAE,CAAC;YAC5E,UAAU,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,CAC1C,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,EAAE,aAAc,EAAE,SAAS,GAAG,EAAE,CAAC,EACtE,eAAe,CAChB,CAAC;YACF,WAAW,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,gCAAgC;QACnE,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,eAAM,CAAC;YACtB,SAAS;YACT,aAAa;YACb,aAAa;YACb,OAAO,EAAE,MAAM,CAAC,MAAM;YACtB,GAAG;YACH,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YACjB,IAAI;YACJ,KAAK,EAAE,WAAW;SACnB,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QAEzC,gCAAgC;QAChC,IAAI,UAAU,EAAE,CAAC;YACf,UAAU,CAAC,YAAY,GAAG,UAAU,CAAC;QACvC,CAAC;QAED,gFAAgF;QAChF,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACpC,UAAU,CAAC,GAAG,GAAG,IAAA,yCAAgC,EAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACvE,CAAC;aAAM,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YACxC,UAAU,CAAC,GAAG,GAAG,IAAA,8BAAqB,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;QAED,OAAO;YACL,UAAU,EAAE;gBACV,IAAI,EAAE,UAAU;aACjB;SACF,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;IAEF,qDAAqD;IACrD,MAAM,IAAA,+BAAsB,EAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;AAClF,CAAC,CAAC;AAtEW,QAAA,eAAe,mBAsE1B"}
1
+ {"version":3,"file":"put-writes.js","sourceRoot":"","sources":["../../../src/checkpointer/actions/put-writes.ts"],"names":[],"mappings":";;;AAAA,0EAAiE;AAEjE,yCAKsB;AAEtB,oCAAgF;AAChF,mEAA+D;AAC/D,qCAAkC;AAElC;;;;;GAKG;AACI,MAAM,eAAe,GAAG,KAAK,EAAE,MAA6B,EAAiB,EAAE;IACpF,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,IAAA,4CAAoB,EACtE,MAAM,CAAC,MAAM,CAAC,YAAY,CAC3B,CAAC;IAEF,IAAI,aAAa,IAAI,IAAI,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC3C,CAAC;IAED,kBAAkB;IAClB,IAAA,2BAAmB,EAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAA,sBAAc,EAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,IAAA,uBAAe,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEhC,oEAAoE;IACpE,IAAI,GAAuB,CAAC;IAC5B,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACpC,GAAG,GAAG,IAAA,yCAAgC,EAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5D,CAAC;SAAM,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACxC,GAAG,GAAG,IAAA,8BAAqB,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,GAAG,CAClC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;QAC/C,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjE,yDAAyD;QACzD,MAAM,eAAe,GAAG,MAAM,CAAC,UAAU;YACvC,CAAC,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAC5C,CAAC,CAAC,QAAQ,CAAC;QAEb,6EAA6E;QAC7E,2EAA2E;QAC3E,2EAA2E;QAC3E,+EAA+E;QAC/E,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,GAAG,GAAG,qCAAc,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC;QAErD,uCAAuC;QACvC,IAAI,UAA8B,CAAC;QACnC,IAAI,WAAW,GAAe,eAAe,CAAC;QAE9C,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,eAAe,CAAC,EAAE,CAAC;YAC5E,UAAU,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,CAC1C,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,SAAS,EAAE,aAAc,EAAE,SAAS,GAAG,EAAE,CAAC,EACtE,eAAe,CAChB,CAAC;YACF,WAAW,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,gCAAgC;QACnE,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,eAAM,CAAC;YACtB,SAAS;YACT,aAAa;YACb,aAAa;YACb,OAAO,EAAE,MAAM,CAAC,MAAM;YACtB,GAAG;YACH,OAAO;YACP,IAAI;YACJ,KAAK,EAAE,WAAW;SACnB,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACzC,IAAI,UAAU;YAAE,UAAU,CAAC,YAAY,GAAG,UAAU,CAAC;QACrD,IAAI,GAAG,KAAK,SAAS;YAAE,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC;QAE5C,OAAO,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC;IAC9C,CAAC,CAAC,CACH,CAAC;IAEF,sDAAsD;IACtD,mFAAmF;IACnF,qEAAqE;IACrE,IAAI,CAAC;QACH,MAAM,IAAA,+BAAsB,EAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,eAAe,EAAE,UAAU,EAAE;YAC9E,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CACzB,CAAC,CAAC,EAAE,EAAE,CAAE,CAAC,CAAC,UAAU,CAAC,IAAkC,CAAC,YAAY,CACrE,CAAC;YACF,MAAM,IAAA,yBAAgB,EAAC,MAAM,CAAC,WAAW,EAAE,IAAI,EAAE,mBAAmB,CAAC,CAAC;QACxE,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC,CAAC;AArFW,QAAA,eAAe,mBAqF1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"put.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/put.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAOhE,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAIlB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,GAAU,QAAQ,eAAe,KAAG,OAAO,CAAC,cAAc,CAkH/E,CAAC"}
1
+ {"version":3,"file":"put.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/actions/put.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAQhE,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAIlB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,SAAS,GAAU,QAAQ,eAAe,KAAG,OAAO,CAAC,cAAc,CAmL/E,CAAC"}