@farukada/aws-langgraph-dynamodb-ts 0.7.0 → 0.9.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 (208) hide show
  1. package/README.md +52 -9
  2. package/dist/checkpointer/actions/delete-thread.d.ts +3 -4
  3. package/dist/checkpointer/actions/delete-thread.d.ts.map +1 -1
  4. package/dist/checkpointer/actions/delete-thread.js +21 -41
  5. package/dist/checkpointer/actions/delete-thread.js.map +1 -1
  6. package/dist/checkpointer/actions/list.d.ts.map +1 -1
  7. package/dist/checkpointer/actions/list.js +38 -4
  8. package/dist/checkpointer/actions/list.js.map +1 -1
  9. package/dist/checkpointer/actions/put-writes.d.ts.map +1 -1
  10. package/dist/checkpointer/actions/put-writes.js +15 -24
  11. package/dist/checkpointer/actions/put-writes.js.map +1 -1
  12. package/dist/checkpointer/internal/item-reader.d.ts +45 -0
  13. package/dist/checkpointer/internal/item-reader.d.ts.map +1 -1
  14. package/dist/checkpointer/internal/item-reader.js +63 -1
  15. package/dist/checkpointer/internal/item-reader.js.map +1 -1
  16. package/dist/checkpointer/internal/item-writer.d.ts +0 -8
  17. package/dist/checkpointer/internal/item-writer.d.ts.map +1 -1
  18. package/dist/checkpointer/internal/item-writer.js +16 -21
  19. package/dist/checkpointer/internal/item-writer.js.map +1 -1
  20. package/dist/checkpointer/internal/keys.d.ts +19 -3
  21. package/dist/checkpointer/internal/keys.d.ts.map +1 -1
  22. package/dist/checkpointer/internal/keys.js +41 -6
  23. package/dist/checkpointer/internal/keys.js.map +1 -1
  24. package/dist/checkpointer/internal/special-write-cas.d.ts +30 -0
  25. package/dist/checkpointer/internal/special-write-cas.d.ts.map +1 -0
  26. package/dist/checkpointer/internal/special-write-cas.js +104 -0
  27. package/dist/checkpointer/internal/special-write-cas.js.map +1 -0
  28. package/dist/checkpointer/internal/special-write-cleanup.d.ts +18 -12
  29. package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +1 -1
  30. package/dist/checkpointer/internal/special-write-cleanup.js +31 -86
  31. package/dist/checkpointer/internal/special-write-cleanup.js.map +1 -1
  32. package/dist/checkpointer/internal/special-write-verify.d.ts +54 -0
  33. package/dist/checkpointer/internal/special-write-verify.d.ts.map +1 -0
  34. package/dist/checkpointer/internal/special-write-verify.js +65 -0
  35. package/dist/checkpointer/internal/special-write-verify.js.map +1 -0
  36. package/dist/checkpointer/internal/validation.d.ts +8 -4
  37. package/dist/checkpointer/internal/validation.d.ts.map +1 -1
  38. package/dist/checkpointer/internal/validation.js +12 -10
  39. package/dist/checkpointer/internal/validation.js.map +1 -1
  40. package/dist/checkpointer/internal/write-guard.d.ts +13 -0
  41. package/dist/checkpointer/internal/write-guard.d.ts.map +1 -0
  42. package/dist/checkpointer/internal/write-guard.js +39 -0
  43. package/dist/checkpointer/internal/write-guard.js.map +1 -0
  44. package/dist/checkpointer/internal/write-index.d.ts +37 -0
  45. package/dist/checkpointer/internal/write-index.d.ts.map +1 -0
  46. package/dist/checkpointer/internal/write-index.js +42 -0
  47. package/dist/checkpointer/internal/write-index.js.map +1 -0
  48. package/dist/checkpointer/types.d.ts +8 -0
  49. package/dist/checkpointer/types.d.ts.map +1 -1
  50. package/dist/history/actions/add-messages.d.ts.map +1 -1
  51. package/dist/history/actions/add-messages.js +24 -4
  52. package/dist/history/actions/add-messages.js.map +1 -1
  53. package/dist/history/actions/clear.d.ts +4 -6
  54. package/dist/history/actions/clear.d.ts.map +1 -1
  55. package/dist/history/actions/clear.js +18 -33
  56. package/dist/history/actions/clear.js.map +1 -1
  57. package/dist/history/actions/get-messages.d.ts +2 -1
  58. package/dist/history/actions/get-messages.d.ts.map +1 -1
  59. package/dist/history/actions/get-messages.js +28 -2
  60. package/dist/history/actions/get-messages.js.map +1 -1
  61. package/dist/history/actions/list-sessions.d.ts +2 -1
  62. package/dist/history/actions/list-sessions.d.ts.map +1 -1
  63. package/dist/history/actions/list-sessions.js +21 -2
  64. package/dist/history/actions/list-sessions.js.map +1 -1
  65. package/dist/history/actions/reconcile-count.d.ts.map +1 -1
  66. package/dist/history/actions/reconcile-count.js +2 -2
  67. package/dist/history/actions/reconcile-count.js.map +1 -1
  68. package/dist/history/internal/append-saga.d.ts.map +1 -1
  69. package/dist/history/internal/append-saga.js +2 -78
  70. package/dist/history/internal/append-saga.js.map +1 -1
  71. package/dist/history/internal/compensation.d.ts +21 -0
  72. package/dist/history/internal/compensation.d.ts.map +1 -0
  73. package/dist/history/internal/compensation.js +84 -0
  74. package/dist/history/internal/compensation.js.map +1 -0
  75. package/dist/history/internal/keys.d.ts +9 -1
  76. package/dist/history/internal/keys.d.ts.map +1 -1
  77. package/dist/history/internal/keys.js +21 -4
  78. package/dist/history/internal/keys.js.map +1 -1
  79. package/dist/history/internal/message-chunker.d.ts.map +1 -1
  80. package/dist/history/internal/message-chunker.js +13 -5
  81. package/dist/history/internal/message-chunker.js.map +1 -1
  82. package/dist/history/internal/session-count.d.ts +21 -0
  83. package/dist/history/internal/session-count.d.ts.map +1 -1
  84. package/dist/history/internal/session-count.js +51 -0
  85. package/dist/history/internal/session-count.js.map +1 -1
  86. package/dist/history/internal/session-title.d.ts +20 -0
  87. package/dist/history/internal/session-title.d.ts.map +1 -0
  88. package/dist/history/internal/session-title.js +44 -0
  89. package/dist/history/internal/session-title.js.map +1 -0
  90. package/dist/history/internal/setup.d.ts +2 -1
  91. package/dist/history/internal/setup.d.ts.map +1 -1
  92. package/dist/history/internal/setup.js +1 -0
  93. package/dist/history/internal/setup.js.map +1 -1
  94. package/dist/history/internal/title-generator.d.ts +8 -2
  95. package/dist/history/internal/title-generator.d.ts.map +1 -1
  96. package/dist/history/internal/title-generator.js +12 -3
  97. package/dist/history/internal/title-generator.js.map +1 -1
  98. package/dist/history/internal/validation.d.ts +9 -0
  99. package/dist/history/internal/validation.d.ts.map +1 -0
  100. package/dist/history/internal/validation.js +16 -0
  101. package/dist/history/internal/validation.js.map +1 -0
  102. package/dist/history/types.d.ts +12 -0
  103. package/dist/history/types.d.ts.map +1 -1
  104. package/dist/index.d.ts +1 -1
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/shared/codec/s3/offloader.d.ts +7 -1
  108. package/dist/shared/codec/s3/offloader.d.ts.map +1 -1
  109. package/dist/shared/codec/s3/offloader.js +14 -1
  110. package/dist/shared/codec/s3/offloader.js.map +1 -1
  111. package/dist/shared/constants.d.ts +15 -0
  112. package/dist/shared/constants.d.ts.map +1 -1
  113. package/dist/shared/constants.js +16 -1
  114. package/dist/shared/constants.js.map +1 -1
  115. package/dist/shared/dynamodb/conditional-put.d.ts +51 -0
  116. package/dist/shared/dynamodb/conditional-put.d.ts.map +1 -0
  117. package/dist/shared/dynamodb/conditional-put.js +59 -0
  118. package/dist/shared/dynamodb/conditional-put.js.map +1 -0
  119. package/dist/shared/dynamodb/partition-delete.d.ts +33 -0
  120. package/dist/shared/dynamodb/partition-delete.d.ts.map +1 -0
  121. package/dist/shared/dynamodb/partition-delete.js +70 -0
  122. package/dist/shared/dynamodb/partition-delete.js.map +1 -0
  123. package/dist/shared/dynamodb/retry-classifier.d.ts.map +1 -1
  124. package/dist/shared/dynamodb/retry-classifier.js +9 -1
  125. package/dist/shared/dynamodb/retry-classifier.js.map +1 -1
  126. package/dist/shared/logging/redaction-walk.d.ts +23 -0
  127. package/dist/shared/logging/redaction-walk.d.ts.map +1 -0
  128. package/dist/shared/logging/redaction-walk.js +92 -0
  129. package/dist/shared/logging/redaction-walk.js.map +1 -0
  130. package/dist/shared/logging/redaction.d.ts +26 -17
  131. package/dist/shared/logging/redaction.d.ts.map +1 -1
  132. package/dist/shared/logging/redaction.js +24 -48
  133. package/dist/shared/logging/redaction.js.map +1 -1
  134. package/dist/shared/logging/secret-patterns.d.ts +74 -0
  135. package/dist/shared/logging/secret-patterns.d.ts.map +1 -0
  136. package/dist/shared/logging/secret-patterns.js +107 -0
  137. package/dist/shared/logging/secret-patterns.js.map +1 -0
  138. package/dist/shared/validation/primitives.d.ts +9 -1
  139. package/dist/shared/validation/primitives.d.ts.map +1 -1
  140. package/dist/shared/validation/primitives.js +13 -0
  141. package/dist/shared/validation/primitives.js.map +1 -1
  142. package/dist/store/actions/get.d.ts.map +1 -1
  143. package/dist/store/actions/get.js +15 -1
  144. package/dist/store/actions/get.js.map +1 -1
  145. package/dist/store/actions/list-namespaces.d.ts.map +1 -1
  146. package/dist/store/actions/list-namespaces.js +1 -0
  147. package/dist/store/actions/list-namespaces.js.map +1 -1
  148. package/dist/store/actions/put.d.ts.map +1 -1
  149. package/dist/store/actions/put.js +24 -18
  150. package/dist/store/actions/put.js.map +1 -1
  151. package/dist/store/actions/search.d.ts.map +1 -1
  152. package/dist/store/actions/search.js +4 -35
  153. package/dist/store/actions/search.js.map +1 -1
  154. package/dist/store/internal/backend-search.d.ts +5 -0
  155. package/dist/store/internal/backend-search.d.ts.map +1 -0
  156. package/dist/store/internal/backend-search.js +68 -0
  157. package/dist/store/internal/backend-search.js.map +1 -0
  158. package/dist/store/internal/filter.d.ts.map +1 -1
  159. package/dist/store/internal/filter.js +46 -5
  160. package/dist/store/internal/filter.js.map +1 -1
  161. package/dist/store/internal/index-reconcile.d.ts.map +1 -1
  162. package/dist/store/internal/index-reconcile.js +41 -5
  163. package/dist/store/internal/index-reconcile.js.map +1 -1
  164. package/dist/store/internal/item-mapper.d.ts +1 -0
  165. package/dist/store/internal/item-mapper.d.ts.map +1 -1
  166. package/dist/store/internal/item-mapper.js +2 -0
  167. package/dist/store/internal/item-mapper.js.map +1 -1
  168. package/dist/store/internal/keys.d.ts +1 -1
  169. package/dist/store/internal/keys.d.ts.map +1 -1
  170. package/dist/store/internal/keys.js +8 -2
  171. package/dist/store/internal/keys.js.map +1 -1
  172. package/dist/store/internal/overwrite-swap.d.ts +33 -0
  173. package/dist/store/internal/overwrite-swap.d.ts.map +1 -0
  174. package/dist/store/internal/overwrite-swap.js +62 -0
  175. package/dist/store/internal/overwrite-swap.js.map +1 -0
  176. package/dist/store/internal/persist.d.ts +22 -6
  177. package/dist/store/internal/persist.d.ts.map +1 -1
  178. package/dist/store/internal/persist.js +41 -20
  179. package/dist/store/internal/persist.js.map +1 -1
  180. package/dist/store/internal/read-existing.d.ts +19 -0
  181. package/dist/store/internal/read-existing.d.ts.map +1 -0
  182. package/dist/store/internal/read-existing.js +29 -0
  183. package/dist/store/internal/read-existing.js.map +1 -0
  184. package/dist/store/internal/score-direction.d.ts +32 -0
  185. package/dist/store/internal/score-direction.d.ts.map +1 -0
  186. package/dist/store/internal/score-direction.js +39 -0
  187. package/dist/store/internal/score-direction.js.map +1 -0
  188. package/dist/store/internal/search-filter.d.ts +4 -0
  189. package/dist/store/internal/search-filter.d.ts.map +1 -0
  190. package/dist/store/internal/search-filter.js +11 -0
  191. package/dist/store/internal/search-filter.js.map +1 -0
  192. package/dist/store/internal/setup.d.ts +13 -1
  193. package/dist/store/internal/setup.d.ts.map +1 -1
  194. package/dist/store/internal/setup.js +57 -1
  195. package/dist/store/internal/setup.js.map +1 -1
  196. package/dist/store/internal/validation.d.ts +8 -2
  197. package/dist/store/internal/validation.d.ts.map +1 -1
  198. package/dist/store/internal/validation.js +15 -6
  199. package/dist/store/internal/validation.js.map +1 -1
  200. package/dist/store/internal/write-verify.d.ts +31 -6
  201. package/dist/store/internal/write-verify.d.ts.map +1 -1
  202. package/dist/store/internal/write-verify.js +48 -8
  203. package/dist/store/internal/write-verify.js.map +1 -1
  204. package/dist/store/types.d.ts +16 -0
  205. package/dist/store/types.d.ts.map +1 -1
  206. package/dist/store/vector-backend.d.ts +17 -0
  207. package/dist/store/vector-backend.d.ts.map +1 -1
  208. package/package.json +1 -1
@@ -1,12 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.codecDeps = codecDeps;
4
- exports.resolveWriteIndex = resolveWriteIndex;
5
4
  exports.buildCheckpointItems = buildCheckpointItems;
6
5
  exports.buildWriteItems = buildWriteItems;
7
- const langgraph_checkpoint_1 = require("@langchain/langgraph-checkpoint");
8
6
  const codec_1 = require("../../shared/codec/codec");
9
7
  const keys_1 = require("./keys");
8
+ const write_index_1 = require("./write-index");
10
9
  /** Map a context to the codec collaborators. */
11
10
  function codecDeps(context) {
12
11
  return { serde: context.serde, compression: context.compression, offloader: context.offloader };
@@ -16,16 +15,6 @@ function withTtl(item, ttlTimestamp) {
16
15
  item.ttl = ttlTimestamp;
17
16
  return item;
18
17
  }
19
- /**
20
- * Resolve a write's DynamoDB sort-key index: WRITES_IDX_MAP's slot for a
21
- * known special channel, else its position. `Object.hasOwn` guards against
22
- * WRITES_IDX_MAP's own `Object.prototype` chain — a channel literally named
23
- * `constructor`/`toString`/etc. must fall through to `positional`, not
24
- * resolve to an inherited function reference.
25
- */
26
- function resolveWriteIndex(channel, positional) {
27
- return Object.hasOwn(langgraph_checkpoint_1.WRITES_IDX_MAP, channel) ? langgraph_checkpoint_1.WRITES_IDX_MAP[channel] : positional;
28
- }
29
18
  /** Encode a checkpoint + metadata into its META and PAYLOAD items. */
30
19
  async function buildCheckpointItems(context, threadId, checkpointNs, checkpoint, metadata, parentCheckpointId, ttlTimestamp) {
31
20
  const deps = codecDeps(context);
@@ -70,24 +59,30 @@ async function buildWriteItems(context, threadId, checkpointNs, checkpointId, ta
70
59
  const deps = codecDeps(context);
71
60
  const pk = (0, keys_1.partitionKey)(threadId);
72
61
  const items = [];
73
- for (let positional = 0; positional < writes.length; positional++) {
74
- const [channel, value] = writes[positional];
62
+ for (const { channel, value, index, occurrence } of (0, write_index_1.resolveWriteIndices)(writes)) {
75
63
  /**
76
- * `positional` indexes this (post-dedup) array, not the caller's original
77
- * one — stable across retries of an identical array, which is all
78
- * first-write-wins requires (see dedupeWritesByIndex).
64
+ * `channel` is part of the key as well as the index: two channels can
65
+ * share an index (each channel's first occurrence is 0), so without it
66
+ * their uploads would collide on one S3 object within a single call.
79
67
  */
80
- const index = resolveWriteIndex(channel, positional);
81
- const baseKeyParts = [threadId, checkpointNs, checkpointId, taskId, `write-${index}`];
82
68
  const descriptor = await (0, codec_1.encodePayload)(value, deps, {
83
- keyParts: [...baseKeyParts, nonce],
69
+ keyParts: [threadId, checkpointNs, checkpointId, taskId, `write-${index}`, channel, nonce],
84
70
  });
85
71
  const item = {
86
72
  PK: pk,
87
- SK: (0, keys_1.writeSortKey)(checkpointNs, checkpointId, taskId, index),
73
+ SK: (0, keys_1.writeSortKey)(checkpointNs, checkpointId, taskId, index, channel),
88
74
  taskId,
89
75
  index,
90
76
  channel,
77
+ /**
78
+ * Shared by every row this call writes. Positions shift when a retried
79
+ * task's write mix changes, so a channel an earlier call already
80
+ * committed can land at a second index and be replayed twice; the group
81
+ * is what lets the read side tell that apart from a channel a single
82
+ * call legitimately wrote more than once.
83
+ */
84
+ writeGroup: nonce,
85
+ occurrence,
91
86
  value: descriptor,
92
87
  };
93
88
  items.push(withTtl(item, ttlTimestamp));
@@ -1 +1 @@
1
- {"version":3,"file":"item-writer.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"item-writer.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/item-writer.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  /** Reserved separator joining sort-key segments; forbidden inside any segment. */
2
2
  export declare const SORT_KEY_SEPARATOR = "#";
3
- /** Partition key for a thread: the thread id itself. */
3
+ /** Partition key for a thread: the adapter tag plus the thread id. */
4
4
  export declare function partitionKey(threadId: string): string;
5
5
  /** Sort key for a checkpoint's lightweight metadata item. */
6
6
  export declare function metaSortKey(checkpointNs: string, checkpointId: string): string;
@@ -8,8 +8,24 @@ export declare function metaSortKey(checkpointNs: string, checkpointId: string):
8
8
  export declare function metaSortKeyPrefix(checkpointNs: string): string;
9
9
  /** Sort key for a checkpoint's heavy payload item. */
10
10
  export declare function payloadSortKey(checkpointNs: string, checkpointId: string): string;
11
- /** Sort key for a single pending write. */
12
- export declare function writeSortKey(checkpointNs: string, checkpointId: string, taskId: string, index: number): string;
11
+ /**
12
+ * Sort key for a single pending write. The trailing `channel` segment is what
13
+ * keeps two *different* channels from ever occupying one row: without it, a
14
+ * retried task whose write mix changed could compute an index another
15
+ * channel already holds, and the first-write-wins guard — which cannot tell a
16
+ * genuine retry from an unrelated write — would silently discard it. The
17
+ * channel is appended verbatim as the final segment, so two sort keys collide
18
+ * only when their channels are byte-identical; `writeSortKeyPrefix` stops at
19
+ * the checkpoint id, ahead of this segment, so `begins_with` reads are
20
+ * unaffected.
21
+ */
22
+ export declare function writeSortKey(checkpointNs: string, checkpointId: string, taskId: string, index: number, channel: string): string;
23
+ /**
24
+ * True when `sortKey` is one this adapter writes. A partition query carries no
25
+ * sort-key condition, so a partition-wide delete uses this to leave any row it
26
+ * does not own in place rather than deleting the whole partition blindly.
27
+ */
28
+ export declare function isCheckpointerSortKey(sortKey: string): boolean;
13
29
  /** `begins_with` prefix selecting every WRITE item for one checkpoint. */
14
30
  export declare function writeSortKeyPrefix(checkpointNs: string, checkpointId: string): string;
15
31
  //# sourceMappingURL=keys.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/keys.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAmBtC,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED,2CAA2C;AAC3C,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,MAAM,CAKR;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAErF"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/keys.ts"],"names":[],"mappings":"AAEA,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,MAAM,CAAC;AA8BtC,sEAAsE;AACtE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,MAAM,CAaR;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI9D;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAErF"}
@@ -6,7 +6,9 @@ exports.metaSortKey = metaSortKey;
6
6
  exports.metaSortKeyPrefix = metaSortKeyPrefix;
7
7
  exports.payloadSortKey = payloadSortKey;
8
8
  exports.writeSortKey = writeSortKey;
9
+ exports.isCheckpointerSortKey = isCheckpointerSortKey;
9
10
  exports.writeSortKeyPrefix = writeSortKeyPrefix;
11
+ const errors_1 = require("../../shared/errors/errors");
10
12
  /** Reserved separator joining sort-key segments; forbidden inside any segment. */
11
13
  exports.SORT_KEY_SEPARATOR = '#';
12
14
  /** Fixed digit width for the WRITE index so sort keys order numerically. */
@@ -24,9 +26,19 @@ var CheckpointItemKind;
24
26
  CheckpointItemKind["PAYLOAD"] = "PAYLOAD";
25
27
  CheckpointItemKind["WRITE"] = "WRITE";
26
28
  })(CheckpointItemKind || (CheckpointItemKind = {}));
27
- /** Partition key for a thread: the thread id itself. */
29
+ /**
30
+ * Adapter tag prefixed to every checkpointer partition key. Without it a
31
+ * `thread_id` reused as a `sessionId` or a store namespace root — an ordinary
32
+ * design choice — put all three adapters' rows in one partition on a table
33
+ * shared via `DynamoDBFactory.createAll()`, where a partition-wide delete
34
+ * reached another adapter's data and composed sort keys could collide
35
+ * byte-for-byte. The three tags differ in their first character, so the key
36
+ * spaces are disjoint by construction.
37
+ */
38
+ const ADAPTER_PARTITION_PREFIX = `CHKPT${exports.SORT_KEY_SEPARATOR}`;
39
+ /** Partition key for a thread: the adapter tag plus the thread id. */
28
40
  function partitionKey(threadId) {
29
- return threadId;
41
+ return `${ADAPTER_PARTITION_PREFIX}${threadId}`;
30
42
  }
31
43
  /** Sort key for a checkpoint's lightweight metadata item. */
32
44
  function metaSortKey(checkpointNs, checkpointId) {
@@ -40,10 +52,33 @@ function metaSortKeyPrefix(checkpointNs) {
40
52
  function payloadSortKey(checkpointNs, checkpointId) {
41
53
  return `${CheckpointItemKind.PAYLOAD}${exports.SORT_KEY_SEPARATOR}${checkpointNs}${exports.SORT_KEY_SEPARATOR}${checkpointId}`;
42
54
  }
43
- /** Sort key for a single pending write. */
44
- function writeSortKey(checkpointNs, checkpointId, taskId, index) {
45
- const paddedIndex = (index + WRITE_INDEX_OFFSET).toString().padStart(WRITE_INDEX_PAD_WIDTH, '0');
46
- return [CheckpointItemKind.WRITE, checkpointNs, checkpointId, taskId, paddedIndex].join(exports.SORT_KEY_SEPARATOR);
55
+ /**
56
+ * Sort key for a single pending write. The trailing `channel` segment is what
57
+ * keeps two *different* channels from ever occupying one row: without it, a
58
+ * retried task whose write mix changed could compute an index another
59
+ * channel already holds, and the first-write-wins guard — which cannot tell a
60
+ * genuine retry from an unrelated write — would silently discard it. The
61
+ * channel is appended verbatim as the final segment, so two sort keys collide
62
+ * only when their channels are byte-identical; `writeSortKeyPrefix` stops at
63
+ * the checkpoint id, ahead of this segment, so `begins_with` reads are
64
+ * unaffected.
65
+ */
66
+ function writeSortKey(checkpointNs, checkpointId, taskId, index, channel) {
67
+ const offsetIndex = index + WRITE_INDEX_OFFSET;
68
+ if (offsetIndex < 0 || offsetIndex.toString().length > WRITE_INDEX_PAD_WIDTH) {
69
+ throw new errors_1.ValidationError(`write index ${index} is outside the range encodable at offset ${WRITE_INDEX_OFFSET} ` +
70
+ `with ${WRITE_INDEX_PAD_WIDTH} digits`, 'index');
71
+ }
72
+ const paddedIndex = offsetIndex.toString().padStart(WRITE_INDEX_PAD_WIDTH, '0');
73
+ return [CheckpointItemKind.WRITE, checkpointNs, checkpointId, taskId, paddedIndex, channel].join(exports.SORT_KEY_SEPARATOR);
74
+ }
75
+ /**
76
+ * True when `sortKey` is one this adapter writes. A partition query carries no
77
+ * sort-key condition, so a partition-wide delete uses this to leave any row it
78
+ * does not own in place rather than deleting the whole partition blindly.
79
+ */
80
+ function isCheckpointerSortKey(sortKey) {
81
+ return Object.values(CheckpointItemKind).some((kind) => sortKey.startsWith(`${kind}${exports.SORT_KEY_SEPARATOR}`));
47
82
  }
48
83
  /** `begins_with` prefix selecting every WRITE item for one checkpoint. */
49
84
  function writeSortKeyPrefix(checkpointNs, checkpointId) {
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/keys.ts"],"names":[],"mappings":";;;;;;;;;AAAA,kFAAkF;AACrE,QAAA,kBAAkB,GAAG,GAAG,CAAC;AAEtC,4EAA4E;AAC5E,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAE7B,6EAA6E;AAC7E,IAAK,kBAIJ;AAJD,WAAK,kBAAkB;IACrB,mCAAa,CAAA;IACb,yCAAmB,CAAA;IACnB,qCAAe,CAAA;AACjB,CAAC,EAJI,kBAAkB,KAAlB,kBAAkB,QAItB;AAED,wDAAwD;AACxD,sBAA6B,QAAgB;IAC3C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6DAA6D;AAC7D,qBAA4B,YAAoB,EAAE,YAAoB;IACpE,OAAO,GAAG,kBAAkB,CAAC,IAAI,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,GAAG,YAAY,EAAE,CAAC;AAC9G,CAAC;AAED,gFAAgF;AAChF,2BAAkC,YAAoB;IACpD,OAAO,GAAG,kBAAkB,CAAC,IAAI,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,EAAE,CAAC;AAC/F,CAAC;AAED,sDAAsD;AACtD,wBAA+B,YAAoB,EAAE,YAAoB;IACvE,OAAO,GAAG,kBAAkB,CAAC,OAAO,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,GAAG,YAAY,EAAE,CAAC;AACjH,CAAC;AAED,2CAA2C;AAC3C,sBACE,YAAoB,EACpB,YAAoB,EACpB,MAAc,EACd,KAAa;IAEb,MAAM,WAAW,GAAG,CAAC,KAAK,GAAG,kBAAkB,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;IACjG,OAAO,CAAC,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,IAAI,CACrF,QAAA,kBAAkB,CACnB,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,4BAAmC,YAAoB,EAAE,YAAoB;IAC3E,OAAO,GAAG,kBAAkB,CAAC,KAAK,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,EAAE,CAAC;AACpI,CAAC"}
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/keys.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,uDAA6D;AAE7D,kFAAkF;AACrE,QAAA,kBAAkB,GAAG,GAAG,CAAC;AAEtC,4EAA4E;AAC5E,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAE7B,6EAA6E;AAC7E,IAAK,kBAIJ;AAJD,WAAK,kBAAkB;IACrB,mCAAa,CAAA;IACb,yCAAmB,CAAA;IACnB,qCAAe,CAAA;AACjB,CAAC,EAJI,kBAAkB,KAAlB,kBAAkB,QAItB;AAED;;;;;;;;GAQG;AACH,MAAM,wBAAwB,GAAG,QAAQ,QAAA,kBAAkB,EAAE,CAAC;AAE9D,sEAAsE;AACtE,sBAA6B,QAAgB;IAC3C,OAAO,GAAG,wBAAwB,GAAG,QAAQ,EAAE,CAAC;AAClD,CAAC;AAED,6DAA6D;AAC7D,qBAA4B,YAAoB,EAAE,YAAoB;IACpE,OAAO,GAAG,kBAAkB,CAAC,IAAI,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,GAAG,YAAY,EAAE,CAAC;AAC9G,CAAC;AAED,gFAAgF;AAChF,2BAAkC,YAAoB;IACpD,OAAO,GAAG,kBAAkB,CAAC,IAAI,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,EAAE,CAAC;AAC/F,CAAC;AAED,sDAAsD;AACtD,wBAA+B,YAAoB,EAAE,YAAoB;IACvE,OAAO,GAAG,kBAAkB,CAAC,OAAO,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,GAAG,YAAY,EAAE,CAAC;AACjH,CAAC;AAED;;;;;;;;;;GAUG;AACH,sBACE,YAAoB,EACpB,YAAoB,EACpB,MAAc,EACd,KAAa,EACb,OAAe;IAEf,MAAM,WAAW,GAAG,KAAK,GAAG,kBAAkB,CAAC;IAC/C,IAAI,WAAW,GAAG,CAAC,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QAC7E,MAAM,IAAI,wBAAe,CACvB,eAAe,KAAK,6CAA6C,kBAAkB,GAAG;YACpF,QAAQ,qBAAqB,SAAS,EACxC,OAAO,CACR,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;IAChF,OAAO,CAAC,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAC9F,QAAA,kBAAkB,CACnB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,+BAAsC,OAAe;IACnD,OAAO,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CACrD,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,QAAA,kBAAkB,EAAE,CAAC,CACnD,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,4BAAmC,YAAoB,EAAE,YAAoB;IAC3E,OAAO,GAAG,kBAAkB,CAAC,KAAK,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,GAAG,YAAY,GAAG,QAAA,kBAAkB,EAAE,CAAC;AACpI,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { CheckpointWriteItem } from '../types';
2
+ import type { CheckpointerContext } from './setup';
3
+ import { type SpecialWriteOutcome } from './special-write-verify';
4
+ /**
5
+ * Overwrite one special row, pinned to the `writeGroup` this call observed, and
6
+ * report the descriptor it superseded.
7
+ *
8
+ * Overwriting is correct for special channels — every reference implementation
9
+ * does it — but two concurrent calls to the same channel both read the same
10
+ * previous descriptor and both delete it, orphaning the loser's upload. Pinning
11
+ * the observed `writeGroup` and re-reading on rejection makes each call
12
+ * supersede exactly one payload.
13
+ *
14
+ * `BatchWriteItem` cannot carry conditions, which is why this path issues
15
+ * individual puts; a call holds at most one row per special channel, so that is
16
+ * four writes at worst.
17
+ *
18
+ * The compare-and-swap runs only when an offloader is configured — matching
19
+ * `store/internal/persist.ts` — because without one there is no S3 object to
20
+ * orphan, so a plain unconditional put stays correct and costs no extra
21
+ * ConsistentRead or write capacity. That shortcut is also why the surviving
22
+ * `catch` may report `committed: false` without verifying: with no offloader
23
+ * there is no object for the caller to delete on the strength of it.
24
+ *
25
+ * Never rejects: the caller runs this concurrently with the regular writes
26
+ * under `Promise.all`, whose own cleanup depends on every branch resolving
27
+ * rather than short-circuiting.
28
+ */
29
+ export declare function writeSpecialItem(context: CheckpointerContext, item: CheckpointWriteItem): Promise<SpecialWriteOutcome>;
30
+ //# sourceMappingURL=special-write-cas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special-write-cas.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-cas.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAIL,KAAK,mBAAmB,EAEzB,MAAM,wBAAwB,CAAC;AAsEhC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAoB9B"}
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.writeSpecialItem = writeSpecialItem;
4
+ const conditional_put_1 = require("../../shared/dynamodb/conditional-put");
5
+ const retry_1 = require("../../shared/dynamodb/retry");
6
+ const special_write_verify_1 = require("./special-write-verify");
7
+ /**
8
+ * Retry a conditional put up to {@link OVERWRITE_CAS_MAX_ATTEMPTS} times,
9
+ * re-reading the row each time a racer's write invalidates the pinned
10
+ * `writeGroup`. Extracted from {@link writeSpecialItem} to keep both
11
+ * functions under the repo's block-nesting limit.
12
+ *
13
+ * A rejection is not proof a competitor won: `withDynamoDBRetry` retries
14
+ * transient errors, so an attempt can commit server-side, its response can be
15
+ * lost, and the retried put can hit the row it just wrote and fail the same
16
+ * guard — indistinguishable from a competitor's win by the rejection alone.
17
+ * Each attempt's pinned observation is captured in `attempted` before the
18
+ * put, so that when {@link verifyAfterFailure} finds the row already holding
19
+ * *this item's own* `writeGroup`, the outcome reports having superseded
20
+ * whatever `attempted` held — never the item's own just-committed payload,
21
+ * which would strand the live row pointing at a deleted object.
22
+ *
23
+ * Only a rejection whose re-read proves some *other* writer holds the row is
24
+ * retried; every other failure is already settled by the verification.
25
+ */
26
+ async function attemptCasWrites(context, item, initial) {
27
+ let observed = initial;
28
+ for (let attempt = 1; attempt <= conditional_put_1.OVERWRITE_CAS_MAX_ATTEMPTS; attempt++) {
29
+ const attempted = observed;
30
+ try {
31
+ await (0, retry_1.withDynamoDBRetry)(() => context.client.put({
32
+ TableName: context.tableName,
33
+ Item: item,
34
+ ...(0, conditional_put_1.revisionGuard)(special_write_verify_1.SPECIAL_REVISION_ATTRIBUTE, attempted),
35
+ }));
36
+ return { done: true, outcome: { committed: true, superseded: attempted.value } };
37
+ }
38
+ catch (error) {
39
+ const verified = await (0, special_write_verify_1.verifyAfterFailure)(context, item, attempted, error);
40
+ if (!verified.observed || !(0, conditional_put_1.isConditionalCheckFailed)(error)) {
41
+ return { done: true, outcome: verified.outcome };
42
+ }
43
+ observed = verified.observed;
44
+ }
45
+ }
46
+ return { done: false, observed };
47
+ }
48
+ /**
49
+ * Overwrite the row unconditionally once the compare-and-swap budget is spent,
50
+ * verifying rather than assuming if that put fails too.
51
+ */
52
+ async function overwriteUnconditionally(context, item, observed) {
53
+ try {
54
+ await (0, retry_1.withDynamoDBRetry)(() => context.client.put({ TableName: context.tableName, Item: item }));
55
+ return { committed: true, superseded: observed.value };
56
+ }
57
+ catch (error) {
58
+ return (await (0, special_write_verify_1.verifyAfterFailure)(context, item, observed, error)).outcome;
59
+ }
60
+ }
61
+ /**
62
+ * Overwrite one special row, pinned to the `writeGroup` this call observed, and
63
+ * report the descriptor it superseded.
64
+ *
65
+ * Overwriting is correct for special channels — every reference implementation
66
+ * does it — but two concurrent calls to the same channel both read the same
67
+ * previous descriptor and both delete it, orphaning the loser's upload. Pinning
68
+ * the observed `writeGroup` and re-reading on rejection makes each call
69
+ * supersede exactly one payload.
70
+ *
71
+ * `BatchWriteItem` cannot carry conditions, which is why this path issues
72
+ * individual puts; a call holds at most one row per special channel, so that is
73
+ * four writes at worst.
74
+ *
75
+ * The compare-and-swap runs only when an offloader is configured — matching
76
+ * `store/internal/persist.ts` — because without one there is no S3 object to
77
+ * orphan, so a plain unconditional put stays correct and costs no extra
78
+ * ConsistentRead or write capacity. That shortcut is also why the surviving
79
+ * `catch` may report `committed: false` without verifying: with no offloader
80
+ * there is no object for the caller to delete on the strength of it.
81
+ *
82
+ * Never rejects: the caller runs this concurrently with the regular writes
83
+ * under `Promise.all`, whose own cleanup depends on every branch resolving
84
+ * rather than short-circuiting.
85
+ */
86
+ async function writeSpecialItem(context, item) {
87
+ try {
88
+ if (!context.offloader) {
89
+ await (0, retry_1.withDynamoDBRetry)(() => context.client.put({ TableName: context.tableName, Item: item }));
90
+ return { committed: true };
91
+ }
92
+ const initial = await (0, special_write_verify_1.readSpecialRow)(context, item);
93
+ const attempt = await attemptCasWrites(context, item, initial);
94
+ if (attempt.done)
95
+ return attempt.outcome;
96
+ context.logger.warn('putWrites: special-write compare-and-swap exhausted; overwriting unconditionally, which ' +
97
+ 'can orphan one S3 object under a concurrent call (reclaimed by ensureS3LifecycleRule)', { sortKey: item.SK, channel: item.channel, attempts: conditional_put_1.OVERWRITE_CAS_MAX_ATTEMPTS });
98
+ return await overwriteUnconditionally(context, item, attempt.observed);
99
+ }
100
+ catch (error) {
101
+ return { committed: false, error: error };
102
+ }
103
+ }
104
+ //# sourceMappingURL=special-write-cas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special-write-cas.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-cas.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,24 @@
1
1
  import type { CheckpointWriteItem } from '../types';
2
2
  import type { CheckpointerContext } from './setup';
3
3
  /**
4
- * Write special (negative-index) items unconditionally — overwrite is
5
- * correct there, matching every reference checkpointer implementation. When
6
- * an offloader is configured, reads each item's previous descriptor first,
7
- * so a settled outcome can clean up the correct side: the previous
8
- * descriptor on success, each new upload on a confirmed non-commit (batch
9
- * write failed, or the previous-descriptor read itself failed before the
10
- * batch write was even attempted), neither when the outcome is genuinely
11
- * ambiguous (see {@link splitSpecialOutcome}). Never rejects — a failed read
12
- * is reported the same way a failed write is, via the return value —
13
- * because the caller runs this concurrently with writeRegularItems via
14
- * `Promise.all`, whose own regular-write cleanup depends on every branch of
15
- * that `Promise.all` resolving rather than short-circuiting on a reject.
4
+ * Write special (negative-index) items, then clean up the correct side of each.
5
+ *
6
+ * Overwrite is correct here, matching every reference checkpointer. Each item
7
+ * is written with a compare-and-swap on its row's `writeGroup` (see
8
+ * {@link writeSpecialItem}) so a concurrent call to the same special channel
9
+ * cannot make both callers delete the same superseded object and orphan one
10
+ * upload. A committed item cleans up the payload it actually superseded; an
11
+ * item confirmed never to have committed cleans up its own new upload.
12
+ *
13
+ * "Confirmed" is load-bearing, and {@link writeSpecialItem} is what earns it:
14
+ * an ambiguous failure is verified against the row and reported as committed
15
+ * unless the read proves otherwise. Deleting on *unknown* would strand a live
16
+ * row pointing at a deleted object; leaking one object instead is recoverable.
17
+ *
18
+ * Never rejects — a failure is reported via the return value, because the
19
+ * caller runs this concurrently with `writeRegularItems` under `Promise.all`,
20
+ * whose own cleanup depends on every branch resolving rather than
21
+ * short-circuiting.
16
22
  */
17
23
  export declare function writeSpecialItemsWithCleanup(context: CheckpointerContext, items: CheckpointWriteItem[]): Promise<Error | undefined>;
18
24
  //# sourceMappingURL=special-write-cleanup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"special-write-cleanup.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-cleanup.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AA4EnD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,mBAAmB,EAC5B,KAAK,EAAE,mBAAmB,EAAE,GAC3B,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAoC5B"}
1
+ {"version":3,"file":"special-write-cleanup.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-cleanup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAgBnD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,mBAAmB,EAC5B,KAAK,EAAE,mBAAmB,EAAE,GAC3B,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAmB5B"}
@@ -3,100 +3,45 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.writeSpecialItemsWithCleanup = writeSpecialItemsWithCleanup;
4
4
  const descriptor_keys_1 = require("../../shared/codec/descriptor-keys");
5
5
  const orphans_1 = require("../../shared/codec/s3/orphans");
6
- const batch_write_1 = require("../../shared/dynamodb/batch-write");
7
- const retry_1 = require("../../shared/dynamodb/retry");
8
- /**
9
- * Read each special item's stored descriptor before it's overwritten —
10
- * mirrors store/actions/put.ts's readExisting. Keyed by SK (unique per
11
- * special channel per call).
12
- */
13
- async function readPreviousDescriptors(context, items) {
14
- const previous = new Map();
15
- for (const item of items) {
16
- const result = await (0, retry_1.withDynamoDBRetry)(() => context.client.get({
17
- TableName: context.tableName,
18
- Key: { PK: item.PK, SK: item.SK },
19
- ConsistentRead: true,
20
- ProjectionExpression: '#v',
21
- ExpressionAttributeNames: { '#v': 'value' },
22
- }));
23
- previous.set(item.SK, result.Item?.value);
24
- }
25
- return previous;
26
- }
27
- /**
28
- * Split `items` by outcome after a failed batch write (always a
29
- * BatchWriteAllIncompleteError — batchWriteAll's only throw, and every one of
30
- * its own failedChunks is in turn always a BatchWriteIncompleteError with an
31
- * accurate `unprocessed`, whichever of drainUnprocessedWrites's exit paths
32
- * produced it — see drain-unprocessed.ts): never-committed items are safe to
33
- * clean up their own upload, every other item its previous one. Uses `.name`,
34
- * not `instanceof` (banned repo-wide, see base-error.ts).
35
- */
36
- function splitSpecialOutcome(items, error) {
37
- const neverCommittedSks = new Set();
38
- for (const failedChunk of error.failedChunks) {
39
- for (const request of failedChunk.unprocessed) {
40
- /** Always a PutRequest echoing this submission — special writes never send DeleteRequests. */
41
- const { SK } = request.PutRequest.Item;
42
- neverCommittedSks.add(SK);
43
- }
44
- }
45
- return {
46
- committed: items.filter((item) => !neverCommittedSks.has(item.SK)),
47
- neverCommitted: items.filter((item) => neverCommittedSks.has(item.SK)),
48
- };
49
- }
50
- /** Best-effort delete S3 objects backing `descriptors`, if an offloader is configured. */
6
+ const special_write_cas_1 = require("./special-write-cas");
7
+ /** Best-effort delete the S3 objects backing `descriptors`, if offloading is on. */
51
8
  async function deleteDescriptors(context, descriptors, label) {
52
9
  if (!context.offloader)
53
10
  return;
54
- await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(descriptors), label, context.logger);
55
- }
56
- /** Best-effort delete the previous descriptors of `items` (now safely superseded). */
57
- async function cleanUpPrevious(context, items, previous) {
58
- const descriptors = items
59
- .map((item) => previous.get(item.SK))
60
- .filter((descriptor) => descriptor !== undefined);
61
- await deleteDescriptors(context, descriptors, 'putWrites.special.previous');
11
+ const present = descriptors.filter((d) => d !== undefined);
12
+ if (present.length === 0)
13
+ return;
14
+ await (0, orphans_1.cleanUpS3Orphans)(context.offloader, (0, descriptor_keys_1.collectS3Keys)(present), label, context.logger);
62
15
  }
63
16
  /**
64
- * Write special (negative-index) items unconditionally — overwrite is
65
- * correct there, matching every reference checkpointer implementation. When
66
- * an offloader is configured, reads each item's previous descriptor first,
67
- * so a settled outcome can clean up the correct side: the previous
68
- * descriptor on success, each new upload on a confirmed non-commit (batch
69
- * write failed, or the previous-descriptor read itself failed before the
70
- * batch write was even attempted), neither when the outcome is genuinely
71
- * ambiguous (see {@link splitSpecialOutcome}). Never rejects — a failed read
72
- * is reported the same way a failed write is, via the return value —
73
- * because the caller runs this concurrently with writeRegularItems via
74
- * `Promise.all`, whose own regular-write cleanup depends on every branch of
75
- * that `Promise.all` resolving rather than short-circuiting on a reject.
17
+ * Write special (negative-index) items, then clean up the correct side of each.
18
+ *
19
+ * Overwrite is correct here, matching every reference checkpointer. Each item
20
+ * is written with a compare-and-swap on its row's `writeGroup` (see
21
+ * {@link writeSpecialItem}) so a concurrent call to the same special channel
22
+ * cannot make both callers delete the same superseded object and orphan one
23
+ * upload. A committed item cleans up the payload it actually superseded; an
24
+ * item confirmed never to have committed cleans up its own new upload.
25
+ *
26
+ * "Confirmed" is load-bearing, and {@link writeSpecialItem} is what earns it:
27
+ * an ambiguous failure is verified against the row and reported as committed
28
+ * unless the read proves otherwise. Deleting on *unknown* would strand a live
29
+ * row pointing at a deleted object; leaking one object instead is recoverable.
30
+ *
31
+ * Never rejects — a failure is reported via the return value, because the
32
+ * caller runs this concurrently with `writeRegularItems` under `Promise.all`,
33
+ * whose own cleanup depends on every branch resolving rather than
34
+ * short-circuiting.
76
35
  */
77
36
  async function writeSpecialItemsWithCleanup(context, items) {
78
37
  if (items.length === 0)
79
38
  return undefined;
80
- let previous = new Map();
81
- if (context.offloader) {
82
- try {
83
- previous = await readPreviousDescriptors(context, items);
84
- }
85
- catch (error) {
86
- await deleteDescriptors(context, items.map((item) => item.value), 'putWrites.special.newUpload');
87
- return error;
88
- }
89
- }
90
- try {
91
- await (0, batch_write_1.batchWriteAll)(context.client, context.tableName, items.map((item) => ({ PutRequest: { Item: item } })));
92
- }
93
- catch (error) {
94
- const { committed, neverCommitted } = splitSpecialOutcome(items, error);
95
- await deleteDescriptors(context, neverCommitted.map((item) => item.value), 'putWrites.special.newUpload');
96
- await cleanUpPrevious(context, committed, previous);
97
- return error;
98
- }
99
- await cleanUpPrevious(context, items, previous);
100
- return undefined;
39
+ const outcomes = await Promise.all(items.map(async (item) => [
40
+ item,
41
+ await (0, special_write_cas_1.writeSpecialItem)(context, item),
42
+ ]));
43
+ await deleteDescriptors(context, outcomes.filter(([, o]) => o.committed).map(([, o]) => o.superseded), 'putWrites.special.previous');
44
+ await deleteDescriptors(context, outcomes.filter(([, o]) => !o.committed).map(([item]) => item.value), 'putWrites.special.newUpload');
45
+ return outcomes.find(([, o]) => o.error)?.[1].error;
101
46
  }
102
47
  //# sourceMappingURL=special-write-cleanup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"special-write-cleanup.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-cleanup.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"special-write-cleanup.js","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-cleanup.ts"],"names":[],"mappings":";;;AACA,wEAAmE;AACnE,2DAAiE;AAGjE,2DAAuD;AAGvD,oFAAoF;AACpF,KAAK,UAAU,iBAAiB,CAC9B,OAA4B,EAC5B,WAA8C,EAC9C,KAAa;IAEb,IAAI,CAAC,OAAO,CAAC,SAAS;QAAE,OAAO;IAC/B,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAA0B,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IACnF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACjC,MAAM,IAAA,0BAAgB,EAAC,OAAO,CAAC,SAAS,EAAE,IAAA,+BAAa,EAAC,OAAO,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;AAC3F,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACI,KAAK,uCACV,OAA4B,EAC5B,KAA4B;IAE5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAChC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAuD,EAAE,CAAC;QAC7E,IAAI;QACJ,MAAM,IAAA,oCAAgB,EAAC,OAAO,EAAE,IAAI,CAAC;KACtC,CAAC,CACH,CAAC;IACF,MAAM,iBAAiB,CACrB,OAAO,EACP,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,EACpE,4BAA4B,CAC7B,CAAC;IACF,MAAM,iBAAiB,CACrB,OAAO,EACP,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EACpE,6BAA6B,CAC9B,CAAC;IACF,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AACtD,CAAC"}
@@ -0,0 +1,54 @@
1
+ import type { PayloadDescriptor } from '../../shared/codec/codec';
2
+ import type { CheckpointWriteItem } from '../types';
3
+ import type { CheckpointerContext } from './setup';
4
+ /**
5
+ * A special row already carries a per-call ULID in `writeGroup`, so it needs no
6
+ * extra revision attribute to compare and swap on.
7
+ */
8
+ export declare const SPECIAL_REVISION_ATTRIBUTE = "writeGroup";
9
+ /** What a special item's row held before this call tried to overwrite it. */
10
+ export interface SpecialRowState {
11
+ exists: boolean;
12
+ value?: PayloadDescriptor;
13
+ revision?: string;
14
+ }
15
+ /** Outcome of one special item's conditional write. Never thrown, always returned. */
16
+ export interface SpecialWriteOutcome {
17
+ committed: boolean;
18
+ superseded?: PayloadDescriptor;
19
+ error?: Error;
20
+ }
21
+ /** What a post-failure verification read established about the attempt. */
22
+ export interface VerifiedFailure {
23
+ outcome: SpecialWriteOutcome;
24
+ /** Present only when the row was read and holds some other writer's group. */
25
+ observed?: SpecialRowState;
26
+ }
27
+ /** Read a special row's current descriptor and the writeGroup guarding it. */
28
+ export declare function readSpecialRow(context: CheckpointerContext, item: CheckpointWriteItem): Promise<SpecialRowState>;
29
+ /**
30
+ * Read the row back after a put failed, and report what that failure actually
31
+ * did — never assuming it did nothing.
32
+ *
33
+ * No rejection is proof of a non-commit. `withDynamoDBRetry` re-issues a put
34
+ * whose response was lost, and those re-issues can time out at the transport
35
+ * without ever reaching DynamoDB, so the budget is spent on a
36
+ * `RetryExhaustedError` and never on a `ConditionalCheckFailedException` — yet
37
+ * the row is committed. Reporting that as a confirmed non-commit let
38
+ * `writeSpecialItemsWithCleanup` delete the S3 object the live row points at,
39
+ * so every later `getTuple()` on that checkpoint failed with `NoSuchKey`,
40
+ * permanently.
41
+ *
42
+ * Three answers are possible:
43
+ * - the row holds this item's own `writeGroup`: the write landed, and the
44
+ * descriptor this attempt pinned is the dead one.
45
+ * - the row holds some other group: the write is confirmed not to be what is
46
+ * live, so this item's own upload is the dead one. `observed` is returned so
47
+ * a rejected compare-and-swap can re-pin and try again.
48
+ * - the read itself fails: nothing is confirmed, so the outcome still reports a
49
+ * commit and keeps the originating error. That leaks one S3 object at worst
50
+ * (reclaimed by `ensureS3LifecycleRule`) where the alternative strands a live
51
+ * row — the same trade `store/internal/persist.ts` makes.
52
+ */
53
+ export declare function verifyAfterFailure(context: CheckpointerContext, item: CheckpointWriteItem, attempted: SpecialRowState, error: Error): Promise<VerifiedFailure>;
54
+ //# sourceMappingURL=special-write-verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"special-write-verify.d.ts","sourceRoot":"","sources":["../../../src/checkpointer/internal/special-write-verify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,sFAAsF;AACtF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,8EAA8E;AAC9E,wBAAsB,cAAc,CAClC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,eAAe,CAAC,CAgB1B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,mBAAmB,EACzB,SAAS,EAAE,eAAe,EAC1B,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,eAAe,CAAC,CAU1B"}