@farukada/aws-langgraph-dynamodb-ts 0.9.0 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1720 -154
- package/dist/backfill/backfill.d.ts +168 -0
- package/dist/backfill/backfill.js +393 -0
- package/dist/checkpointer/actions/delete-thread.d.ts +47 -6
- package/dist/checkpointer/actions/delete-thread.js +58 -21
- package/dist/checkpointer/actions/get-tuple.d.ts +29 -4
- package/dist/checkpointer/actions/get-tuple.js +44 -10
- package/dist/checkpointer/actions/list.d.ts +46 -4
- package/dist/checkpointer/actions/list.js +121 -66
- package/dist/checkpointer/actions/put-writes.d.ts +41 -9
- package/dist/checkpointer/actions/put-writes.js +62 -77
- package/dist/checkpointer/actions/put.d.ts +83 -4
- package/dist/checkpointer/actions/put.js +177 -25
- package/dist/checkpointer/internal/delta-history.d.ts +112 -0
- package/dist/checkpointer/internal/delta-history.js +252 -0
- package/dist/checkpointer/internal/listing.d.ts +149 -0
- package/dist/checkpointer/internal/listing.js +245 -0
- package/dist/checkpointer/internal/parse.d.ts +262 -0
- package/dist/checkpointer/internal/parse.js +372 -0
- package/dist/checkpointer/internal/pending-writes.d.ts +275 -0
- package/dist/checkpointer/internal/pending-writes.js +588 -0
- package/dist/checkpointer/internal/read.d.ts +130 -0
- package/dist/checkpointer/internal/read.js +264 -0
- package/dist/checkpointer/internal/rows.d.ts +571 -0
- package/dist/checkpointer/internal/rows.js +834 -0
- package/dist/checkpointer/internal/setup.d.ts +42 -19
- package/dist/checkpointer/internal/setup.js +65 -29
- package/dist/checkpointer/saver.d.ts +256 -16
- package/dist/checkpointer/saver.js +275 -29
- package/dist/checkpointer/types.d.ts +39 -39
- package/dist/checkpointer/types.js +10 -1
- package/dist/factory/factory.d.ts +134 -28
- package/dist/factory/factory.js +240 -21
- package/dist/factory/types.d.ts +76 -0
- package/dist/factory/types.js +10 -0
- package/dist/history/actions/add-messages.d.ts +31 -4
- package/dist/history/actions/add-messages.js +38 -58
- package/dist/history/actions/clear.d.ts +49 -6
- package/dist/history/actions/clear.js +66 -14
- package/dist/history/actions/get-messages.d.ts +54 -6
- package/dist/history/actions/get-messages.js +126 -43
- package/dist/history/actions/list-sessions.d.ts +52 -10
- package/dist/history/actions/list-sessions.js +139 -40
- package/dist/history/actions/reconcile-count.d.ts +42 -10
- package/dist/history/actions/reconcile-count.js +45 -45
- package/dist/history/chat-message-history.d.ts +220 -33
- package/dist/history/chat-message-history.js +240 -43
- package/dist/history/internal/append.d.ts +212 -0
- package/dist/history/internal/append.js +500 -0
- package/dist/history/internal/message-read.d.ts +84 -0
- package/dist/history/internal/message-read.js +204 -0
- package/dist/history/internal/parse.d.ts +153 -0
- package/dist/history/internal/parse.js +252 -0
- package/dist/history/internal/rows.d.ts +195 -0
- package/dist/history/internal/rows.js +250 -0
- package/dist/history/internal/session.d.ts +331 -0
- package/dist/history/internal/session.js +628 -0
- package/dist/history/internal/setup.d.ts +52 -17
- package/dist/history/internal/setup.js +92 -21
- package/dist/history/session-adapter.d.ts +102 -7
- package/dist/history/session-adapter.js +103 -9
- package/dist/history/types.d.ts +80 -29
- package/dist/history/types.js +10 -1
- package/dist/index.d.ts +42 -11
- package/dist/index.js +33 -12
- package/dist/shared/adapter.d.ts +135 -0
- package/dist/shared/adapter.js +143 -0
- package/dist/shared/clock.d.ts +51 -2
- package/dist/shared/clock.js +57 -2
- package/dist/shared/codec/codec.d.ts +288 -13
- package/dist/shared/codec/codec.js +416 -19
- package/dist/shared/codec/compression.d.ts +43 -7
- package/dist/shared/codec/compression.js +53 -13
- package/dist/shared/codec/json-serde.d.ts +76 -4
- package/dist/shared/codec/json-serde.js +181 -8
- package/dist/shared/codec/s3/client-types.d.ts +53 -0
- package/dist/shared/codec/s3/client-types.js +26 -0
- package/dist/shared/codec/s3/client.d.ts +43 -10
- package/dist/shared/codec/s3/client.js +82 -9
- package/dist/shared/codec/s3/config.d.ts +242 -11
- package/dist/shared/codec/s3/config.js +293 -11
- package/dist/shared/codec/s3/lifecycle.d.ts +164 -6
- package/dist/shared/codec/s3/lifecycle.js +335 -27
- package/dist/shared/codec/s3/offloader.d.ts +393 -18
- package/dist/shared/codec/s3/offloader.js +595 -37
- package/dist/shared/concurrency.d.ts +43 -0
- package/dist/shared/concurrency.js +78 -0
- package/dist/shared/dynamodb/abort.d.ts +47 -0
- package/dist/shared/dynamodb/abort.js +59 -0
- package/dist/shared/dynamodb/batch-write.d.ts +77 -14
- package/dist/shared/dynamodb/batch-write.js +146 -27
- package/dist/shared/dynamodb/cancellation.d.ts +121 -4
- package/dist/shared/dynamodb/cancellation.js +147 -3
- package/dist/shared/dynamodb/client.d.ts +162 -8
- package/dist/shared/dynamodb/client.js +153 -5
- package/dist/shared/dynamodb/idempotent-write.d.ts +551 -0
- package/dist/shared/dynamodb/idempotent-write.js +593 -0
- package/dist/shared/dynamodb/paginate.d.ts +105 -9
- package/dist/shared/dynamodb/paginate.js +175 -7
- package/dist/shared/dynamodb/partition-delete.d.ts +185 -14
- package/dist/shared/dynamodb/partition-delete.js +314 -44
- package/dist/shared/dynamodb/recency-index.d.ts +231 -0
- package/dist/shared/dynamodb/recency-index.js +377 -0
- package/dist/shared/dynamodb/retry.d.ts +276 -8
- package/dist/shared/dynamodb/retry.js +433 -23
- package/dist/shared/dynamodb/table-schema.d.ts +190 -0
- package/dist/shared/dynamodb/table-schema.js +209 -0
- package/dist/shared/errors/base-error.d.ts +184 -10
- package/dist/shared/errors/base-error.js +160 -14
- package/dist/shared/errors/boundary.d.ts +71 -0
- package/dist/shared/errors/boundary.js +143 -0
- package/dist/shared/errors/classify.d.ts +97 -0
- package/dist/shared/errors/classify.js +257 -0
- package/dist/shared/errors/error-code.d.ts +77 -2
- package/dist/shared/errors/error-code.js +83 -1
- package/dist/shared/errors/errors.d.ts +158 -59
- package/dist/shared/errors/errors.js +219 -92
- package/dist/shared/logging/logger.d.ts +69 -3
- package/dist/shared/logging/logger.js +97 -3
- package/dist/shared/logging/redaction.d.ts +92 -8
- package/dist/shared/logging/redaction.js +273 -17
- package/dist/shared/logging/secret-patterns.d.ts +149 -19
- package/dist/shared/logging/secret-patterns.js +188 -27
- package/dist/shared/logging/truncate.d.ts +197 -0
- package/dist/shared/logging/truncate.js +231 -0
- package/dist/shared/options.d.ts +59 -7
- package/dist/shared/options.js +9 -1
- package/dist/shared/ulid.d.ts +77 -7
- package/dist/shared/ulid.js +103 -8
- package/dist/shared/validation/collaborators.d.ts +141 -0
- package/dist/shared/validation/collaborators.js +188 -0
- package/dist/shared/validation/option-shape.d.ts +89 -0
- package/dist/shared/validation/option-shape.js +113 -0
- package/dist/shared/validation/options.d.ts +145 -0
- package/dist/shared/validation/options.js +328 -0
- package/dist/shared/validation/primitives.d.ts +288 -21
- package/dist/shared/validation/primitives.js +353 -50
- package/dist/shared/validation/ttl.d.ts +66 -10
- package/dist/shared/validation/ttl.js +113 -15
- package/dist/store/actions/list-namespaces.d.ts +76 -6
- package/dist/store/actions/list-namespaces.js +166 -24
- package/dist/store/actions/put.d.ts +33 -8
- package/dist/store/actions/put.js +53 -60
- package/dist/store/actions/reconcile-vector-index.d.ts +31 -10
- package/dist/store/actions/reconcile-vector-index.js +34 -15
- package/dist/store/actions/search.d.ts +34 -6
- package/dist/store/actions/search.js +56 -51
- package/dist/store/internal/batch-plan.d.ts +26 -0
- package/dist/store/internal/batch-plan.js +109 -0
- package/dist/store/internal/filter.d.ts +36 -3
- package/dist/store/internal/filter.js +66 -15
- package/dist/store/internal/get-item.d.ts +45 -0
- package/dist/store/internal/get-item.js +115 -0
- package/dist/store/internal/item-write.d.ts +230 -0
- package/dist/store/internal/item-write.js +463 -0
- package/dist/store/internal/parse.d.ts +225 -0
- package/dist/store/internal/parse.js +350 -0
- package/dist/store/internal/rows.d.ts +355 -0
- package/dist/store/internal/rows.js +447 -0
- package/dist/store/internal/semantic-search.d.ts +161 -6
- package/dist/store/internal/semantic-search.js +360 -18
- package/dist/store/internal/setup.d.ts +77 -20
- package/dist/store/internal/setup.js +178 -47
- package/dist/store/internal/table-search.d.ts +100 -0
- package/dist/store/internal/table-search.js +213 -0
- package/dist/store/internal/vector-index.d.ts +247 -0
- package/dist/store/internal/vector-index.js +546 -0
- package/dist/store/store.d.ts +270 -17
- package/dist/store/store.js +329 -38
- package/dist/store/types.d.ts +76 -26
- package/dist/store/types.js +13 -1
- package/dist/store/vector-backend.d.ts +64 -4
- package/dist/store/vector-backend.js +15 -1
- package/package.json +58 -36
- package/dist/checkpointer/actions/delete-thread.d.ts.map +0 -1
- package/dist/checkpointer/actions/delete-thread.js.map +0 -1
- package/dist/checkpointer/actions/get-tuple.d.ts.map +0 -1
- package/dist/checkpointer/actions/get-tuple.js.map +0 -1
- package/dist/checkpointer/actions/list.d.ts.map +0 -1
- package/dist/checkpointer/actions/list.js.map +0 -1
- package/dist/checkpointer/actions/put-writes.d.ts.map +0 -1
- package/dist/checkpointer/actions/put-writes.js.map +0 -1
- package/dist/checkpointer/actions/put.d.ts.map +0 -1
- package/dist/checkpointer/actions/put.js.map +0 -1
- package/dist/checkpointer/internal/assemble.d.ts +0 -10
- package/dist/checkpointer/internal/assemble.d.ts.map +0 -1
- package/dist/checkpointer/internal/assemble.js +0 -37
- package/dist/checkpointer/internal/assemble.js.map +0 -1
- package/dist/checkpointer/internal/configurable.d.ts +0 -13
- package/dist/checkpointer/internal/configurable.d.ts.map +0 -1
- package/dist/checkpointer/internal/configurable.js +0 -23
- package/dist/checkpointer/internal/configurable.js.map +0 -1
- package/dist/checkpointer/internal/fetch.d.ts +0 -10
- package/dist/checkpointer/internal/fetch.d.ts.map +0 -1
- package/dist/checkpointer/internal/fetch.js +0 -46
- package/dist/checkpointer/internal/fetch.js.map +0 -1
- package/dist/checkpointer/internal/filter-match.d.ts +0 -12
- package/dist/checkpointer/internal/filter-match.d.ts.map +0 -1
- package/dist/checkpointer/internal/filter-match.js +0 -14
- package/dist/checkpointer/internal/filter-match.js.map +0 -1
- package/dist/checkpointer/internal/item-reader.d.ts +0 -55
- package/dist/checkpointer/internal/item-reader.d.ts.map +0 -1
- package/dist/checkpointer/internal/item-reader.js +0 -88
- package/dist/checkpointer/internal/item-reader.js.map +0 -1
- package/dist/checkpointer/internal/item-writer.d.ts +0 -26
- package/dist/checkpointer/internal/item-writer.d.ts.map +0 -1
- package/dist/checkpointer/internal/item-writer.js +0 -92
- package/dist/checkpointer/internal/item-writer.js.map +0 -1
- package/dist/checkpointer/internal/keys.d.ts +0 -31
- package/dist/checkpointer/internal/keys.d.ts.map +0 -1
- package/dist/checkpointer/internal/keys.js +0 -87
- package/dist/checkpointer/internal/keys.js.map +0 -1
- package/dist/checkpointer/internal/query.d.ts +0 -20
- package/dist/checkpointer/internal/query.d.ts.map +0 -1
- package/dist/checkpointer/internal/query.js +0 -36
- package/dist/checkpointer/internal/query.js.map +0 -1
- package/dist/checkpointer/internal/setup.d.ts.map +0 -1
- package/dist/checkpointer/internal/setup.js.map +0 -1
- package/dist/checkpointer/internal/special-write-cas.d.ts +0 -30
- package/dist/checkpointer/internal/special-write-cas.d.ts.map +0 -1
- package/dist/checkpointer/internal/special-write-cas.js +0 -104
- package/dist/checkpointer/internal/special-write-cas.js.map +0 -1
- package/dist/checkpointer/internal/special-write-cleanup.d.ts +0 -24
- package/dist/checkpointer/internal/special-write-cleanup.d.ts.map +0 -1
- package/dist/checkpointer/internal/special-write-cleanup.js +0 -47
- package/dist/checkpointer/internal/special-write-cleanup.js.map +0 -1
- package/dist/checkpointer/internal/special-write-verify.d.ts +0 -54
- package/dist/checkpointer/internal/special-write-verify.d.ts.map +0 -1
- package/dist/checkpointer/internal/special-write-verify.js +0 -65
- package/dist/checkpointer/internal/special-write-verify.js.map +0 -1
- package/dist/checkpointer/internal/validation.d.ts +0 -13
- package/dist/checkpointer/internal/validation.d.ts.map +0 -1
- package/dist/checkpointer/internal/validation.js +0 -30
- package/dist/checkpointer/internal/validation.js.map +0 -1
- package/dist/checkpointer/internal/write-guard.d.ts +0 -13
- package/dist/checkpointer/internal/write-guard.d.ts.map +0 -1
- package/dist/checkpointer/internal/write-guard.js +0 -39
- package/dist/checkpointer/internal/write-guard.js.map +0 -1
- package/dist/checkpointer/internal/write-index.d.ts +0 -37
- package/dist/checkpointer/internal/write-index.d.ts.map +0 -1
- package/dist/checkpointer/internal/write-index.js +0 -42
- package/dist/checkpointer/internal/write-index.js.map +0 -1
- package/dist/checkpointer/saver.d.ts.map +0 -1
- package/dist/checkpointer/saver.js.map +0 -1
- package/dist/checkpointer/types.d.ts.map +0 -1
- package/dist/checkpointer/types.js.map +0 -1
- package/dist/factory/factory.d.ts.map +0 -1
- package/dist/factory/factory.js.map +0 -1
- package/dist/history/actions/add-messages.d.ts.map +0 -1
- package/dist/history/actions/add-messages.js.map +0 -1
- package/dist/history/actions/clear.d.ts.map +0 -1
- package/dist/history/actions/clear.js.map +0 -1
- package/dist/history/actions/get-messages.d.ts.map +0 -1
- package/dist/history/actions/get-messages.js.map +0 -1
- package/dist/history/actions/list-sessions.d.ts.map +0 -1
- package/dist/history/actions/list-sessions.js.map +0 -1
- package/dist/history/actions/reconcile-count.d.ts.map +0 -1
- package/dist/history/actions/reconcile-count.js.map +0 -1
- package/dist/history/chat-message-history.d.ts.map +0 -1
- package/dist/history/chat-message-history.js.map +0 -1
- package/dist/history/internal/append-saga.d.ts +0 -20
- package/dist/history/internal/append-saga.d.ts.map +0 -1
- package/dist/history/internal/append-saga.js +0 -35
- package/dist/history/internal/append-saga.js.map +0 -1
- package/dist/history/internal/compensation.d.ts +0 -21
- package/dist/history/internal/compensation.d.ts.map +0 -1
- package/dist/history/internal/compensation.js +0 -84
- package/dist/history/internal/compensation.js.map +0 -1
- package/dist/history/internal/item-mapper.d.ts +0 -12
- package/dist/history/internal/item-mapper.d.ts.map +0 -1
- package/dist/history/internal/item-mapper.js +0 -33
- package/dist/history/internal/item-mapper.js.map +0 -1
- package/dist/history/internal/keys.d.ts +0 -17
- package/dist/history/internal/keys.d.ts.map +0 -1
- package/dist/history/internal/keys.js +0 -49
- package/dist/history/internal/keys.js.map +0 -1
- package/dist/history/internal/message-chunker.d.ts +0 -14
- package/dist/history/internal/message-chunker.d.ts.map +0 -1
- package/dist/history/internal/message-chunker.js +0 -68
- package/dist/history/internal/message-chunker.js.map +0 -1
- package/dist/history/internal/message-transaction.d.ts +0 -26
- package/dist/history/internal/message-transaction.d.ts.map +0 -1
- package/dist/history/internal/message-transaction.js +0 -60
- package/dist/history/internal/message-transaction.js.map +0 -1
- package/dist/history/internal/query.d.ts +0 -10
- package/dist/history/internal/query.d.ts.map +0 -1
- package/dist/history/internal/query.js +0 -31
- package/dist/history/internal/query.js.map +0 -1
- package/dist/history/internal/session-count.d.ts +0 -41
- package/dist/history/internal/session-count.d.ts.map +0 -1
- package/dist/history/internal/session-count.js +0 -109
- package/dist/history/internal/session-count.js.map +0 -1
- package/dist/history/internal/session-title.d.ts +0 -20
- package/dist/history/internal/session-title.d.ts.map +0 -1
- package/dist/history/internal/session-title.js +0 -44
- package/dist/history/internal/session-title.js.map +0 -1
- package/dist/history/internal/session-update.d.ts +0 -28
- package/dist/history/internal/session-update.d.ts.map +0 -1
- package/dist/history/internal/session-update.js +0 -70
- package/dist/history/internal/session-update.js.map +0 -1
- package/dist/history/internal/setup.d.ts.map +0 -1
- package/dist/history/internal/setup.js.map +0 -1
- package/dist/history/internal/title-generator.d.ts +0 -13
- package/dist/history/internal/title-generator.d.ts.map +0 -1
- package/dist/history/internal/title-generator.js +0 -25
- package/dist/history/internal/title-generator.js.map +0 -1
- package/dist/history/internal/ttl-anchor.d.ts +0 -25
- package/dist/history/internal/ttl-anchor.d.ts.map +0 -1
- package/dist/history/internal/ttl-anchor.js +0 -38
- package/dist/history/internal/ttl-anchor.js.map +0 -1
- package/dist/history/internal/validation.d.ts +0 -9
- package/dist/history/internal/validation.d.ts.map +0 -1
- package/dist/history/internal/validation.js +0 -16
- package/dist/history/internal/validation.js.map +0 -1
- package/dist/history/session-adapter.d.ts.map +0 -1
- package/dist/history/session-adapter.js.map +0 -1
- package/dist/history/types.d.ts.map +0 -1
- package/dist/history/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/shared/clock.d.ts.map +0 -1
- package/dist/shared/clock.js.map +0 -1
- package/dist/shared/codec/codec.d.ts.map +0 -1
- package/dist/shared/codec/codec.js.map +0 -1
- package/dist/shared/codec/compression.d.ts.map +0 -1
- package/dist/shared/codec/compression.js.map +0 -1
- package/dist/shared/codec/descriptor-keys.d.ts +0 -4
- package/dist/shared/codec/descriptor-keys.d.ts.map +0 -1
- package/dist/shared/codec/descriptor-keys.js +0 -14
- package/dist/shared/codec/descriptor-keys.js.map +0 -1
- package/dist/shared/codec/json-serde.d.ts.map +0 -1
- package/dist/shared/codec/json-serde.js.map +0 -1
- package/dist/shared/codec/s3/client.d.ts.map +0 -1
- package/dist/shared/codec/s3/client.js.map +0 -1
- package/dist/shared/codec/s3/config.d.ts.map +0 -1
- package/dist/shared/codec/s3/config.js.map +0 -1
- package/dist/shared/codec/s3/delete.d.ts +0 -8
- package/dist/shared/codec/s3/delete.d.ts.map +0 -1
- package/dist/shared/codec/s3/delete.js +0 -29
- package/dist/shared/codec/s3/delete.js.map +0 -1
- package/dist/shared/codec/s3/lifecycle.d.ts.map +0 -1
- package/dist/shared/codec/s3/lifecycle.js.map +0 -1
- package/dist/shared/codec/s3/offloader.d.ts.map +0 -1
- package/dist/shared/codec/s3/offloader.js.map +0 -1
- package/dist/shared/codec/s3/orphans.d.ts +0 -18
- package/dist/shared/codec/s3/orphans.d.ts.map +0 -1
- package/dist/shared/codec/s3/orphans.js +0 -58
- package/dist/shared/codec/s3/orphans.js.map +0 -1
- package/dist/shared/codec/s3/read-write.d.ts +0 -14
- package/dist/shared/codec/s3/read-write.d.ts.map +0 -1
- package/dist/shared/codec/s3/read-write.js +0 -43
- package/dist/shared/codec/s3/read-write.js.map +0 -1
- package/dist/shared/codec/s3/retry.d.ts +0 -5
- package/dist/shared/codec/s3/retry.d.ts.map +0 -1
- package/dist/shared/codec/s3/retry.js +0 -25
- package/dist/shared/codec/s3/retry.js.map +0 -1
- package/dist/shared/constants.d.ts +0 -64
- package/dist/shared/constants.d.ts.map +0 -1
- package/dist/shared/constants.js +0 -67
- package/dist/shared/constants.js.map +0 -1
- package/dist/shared/dynamodb/backoff.d.ts +0 -15
- package/dist/shared/dynamodb/backoff.d.ts.map +0 -1
- package/dist/shared/dynamodb/backoff.js +0 -48
- package/dist/shared/dynamodb/backoff.js.map +0 -1
- package/dist/shared/dynamodb/batch-write.d.ts.map +0 -1
- package/dist/shared/dynamodb/batch-write.js.map +0 -1
- package/dist/shared/dynamodb/cancellation.d.ts.map +0 -1
- package/dist/shared/dynamodb/cancellation.js.map +0 -1
- package/dist/shared/dynamodb/client.d.ts.map +0 -1
- package/dist/shared/dynamodb/client.js.map +0 -1
- package/dist/shared/dynamodb/conditional-put.d.ts +0 -51
- package/dist/shared/dynamodb/conditional-put.d.ts.map +0 -1
- package/dist/shared/dynamodb/conditional-put.js +0 -59
- package/dist/shared/dynamodb/conditional-put.js.map +0 -1
- package/dist/shared/dynamodb/drain-unprocessed.d.ts +0 -19
- package/dist/shared/dynamodb/drain-unprocessed.d.ts.map +0 -1
- package/dist/shared/dynamodb/drain-unprocessed.js +0 -44
- package/dist/shared/dynamodb/drain-unprocessed.js.map +0 -1
- package/dist/shared/dynamodb/paginate-core.d.ts +0 -22
- package/dist/shared/dynamodb/paginate-core.d.ts.map +0 -1
- package/dist/shared/dynamodb/paginate-core.js +0 -52
- package/dist/shared/dynamodb/paginate-core.js.map +0 -1
- package/dist/shared/dynamodb/paginate.d.ts.map +0 -1
- package/dist/shared/dynamodb/paginate.js.map +0 -1
- package/dist/shared/dynamodb/partition-delete.d.ts.map +0 -1
- package/dist/shared/dynamodb/partition-delete.js.map +0 -1
- package/dist/shared/dynamodb/retry-classifier.d.ts +0 -9
- package/dist/shared/dynamodb/retry-classifier.d.ts.map +0 -1
- package/dist/shared/dynamodb/retry-classifier.js +0 -87
- package/dist/shared/dynamodb/retry-classifier.js.map +0 -1
- package/dist/shared/dynamodb/retry.d.ts.map +0 -1
- package/dist/shared/dynamodb/retry.js.map +0 -1
- package/dist/shared/dynamodb/scan.d.ts +0 -15
- package/dist/shared/dynamodb/scan.d.ts.map +0 -1
- package/dist/shared/dynamodb/scan.js +0 -20
- package/dist/shared/dynamodb/scan.js.map +0 -1
- package/dist/shared/dynamodb/types.d.ts +0 -24
- package/dist/shared/dynamodb/types.d.ts.map +0 -1
- package/dist/shared/dynamodb/types.js +0 -3
- package/dist/shared/dynamodb/types.js.map +0 -1
- package/dist/shared/errors/base-error.d.ts.map +0 -1
- package/dist/shared/errors/base-error.js.map +0 -1
- package/dist/shared/errors/error-code.d.ts.map +0 -1
- package/dist/shared/errors/error-code.js.map +0 -1
- package/dist/shared/errors/errors.d.ts.map +0 -1
- package/dist/shared/errors/errors.js.map +0 -1
- package/dist/shared/errors/wrap-error.d.ts +0 -16
- package/dist/shared/errors/wrap-error.d.ts.map +0 -1
- package/dist/shared/errors/wrap-error.js +0 -30
- package/dist/shared/errors/wrap-error.js.map +0 -1
- package/dist/shared/logging/logger.d.ts.map +0 -1
- package/dist/shared/logging/logger.js.map +0 -1
- package/dist/shared/logging/redaction-walk.d.ts +0 -23
- package/dist/shared/logging/redaction-walk.d.ts.map +0 -1
- package/dist/shared/logging/redaction-walk.js +0 -92
- package/dist/shared/logging/redaction-walk.js.map +0 -1
- package/dist/shared/logging/redaction.d.ts.map +0 -1
- package/dist/shared/logging/redaction.js.map +0 -1
- package/dist/shared/logging/secret-patterns.d.ts.map +0 -1
- package/dist/shared/logging/secret-patterns.js.map +0 -1
- package/dist/shared/options.d.ts.map +0 -1
- package/dist/shared/options.js.map +0 -1
- package/dist/shared/ulid.d.ts.map +0 -1
- package/dist/shared/ulid.js.map +0 -1
- package/dist/shared/validation/primitives.d.ts.map +0 -1
- package/dist/shared/validation/primitives.js.map +0 -1
- package/dist/shared/validation/ttl.d.ts.map +0 -1
- package/dist/shared/validation/ttl.js.map +0 -1
- package/dist/store/actions/get.d.ts +0 -5
- package/dist/store/actions/get.d.ts.map +0 -1
- package/dist/store/actions/get.js +0 -35
- package/dist/store/actions/get.js.map +0 -1
- package/dist/store/actions/list-namespaces.d.ts.map +0 -1
- package/dist/store/actions/list-namespaces.js.map +0 -1
- package/dist/store/actions/put.d.ts.map +0 -1
- package/dist/store/actions/put.js.map +0 -1
- package/dist/store/actions/reconcile-vector-index.d.ts.map +0 -1
- package/dist/store/actions/reconcile-vector-index.js.map +0 -1
- package/dist/store/actions/search.d.ts.map +0 -1
- package/dist/store/actions/search.js.map +0 -1
- package/dist/store/internal/backend-search.d.ts +0 -5
- package/dist/store/internal/backend-search.d.ts.map +0 -1
- package/dist/store/internal/backend-search.js +0 -68
- package/dist/store/internal/backend-search.js.map +0 -1
- package/dist/store/internal/filter.d.ts.map +0 -1
- package/dist/store/internal/filter.js.map +0 -1
- package/dist/store/internal/index-reconcile.d.ts +0 -22
- package/dist/store/internal/index-reconcile.d.ts.map +0 -1
- package/dist/store/internal/index-reconcile.js +0 -105
- package/dist/store/internal/index-reconcile.js.map +0 -1
- package/dist/store/internal/index-sync.d.ts +0 -11
- package/dist/store/internal/index-sync.d.ts.map +0 -1
- package/dist/store/internal/index-sync.js +0 -26
- package/dist/store/internal/index-sync.js.map +0 -1
- package/dist/store/internal/item-mapper.d.ts +0 -25
- package/dist/store/internal/item-mapper.d.ts.map +0 -1
- package/dist/store/internal/item-mapper.js +0 -53
- package/dist/store/internal/item-mapper.js.map +0 -1
- package/dist/store/internal/keys.d.ts +0 -18
- package/dist/store/internal/keys.d.ts.map +0 -1
- package/dist/store/internal/keys.js +0 -42
- package/dist/store/internal/keys.js.map +0 -1
- package/dist/store/internal/namespace-match.d.ts +0 -12
- package/dist/store/internal/namespace-match.d.ts.map +0 -1
- package/dist/store/internal/namespace-match.js +0 -41
- package/dist/store/internal/namespace-match.js.map +0 -1
- package/dist/store/internal/overwrite-swap.d.ts +0 -33
- package/dist/store/internal/overwrite-swap.d.ts.map +0 -1
- package/dist/store/internal/overwrite-swap.js +0 -62
- package/dist/store/internal/overwrite-swap.js.map +0 -1
- package/dist/store/internal/persist.d.ts +0 -27
- package/dist/store/internal/persist.d.ts.map +0 -1
- package/dist/store/internal/persist.js +0 -59
- package/dist/store/internal/persist.js.map +0 -1
- package/dist/store/internal/query.d.ts +0 -6
- package/dist/store/internal/query.d.ts.map +0 -1
- package/dist/store/internal/query.js +0 -32
- package/dist/store/internal/query.js.map +0 -1
- package/dist/store/internal/ranker.d.ts +0 -13
- package/dist/store/internal/ranker.d.ts.map +0 -1
- package/dist/store/internal/ranker.js +0 -31
- package/dist/store/internal/ranker.js.map +0 -1
- package/dist/store/internal/read-existing.d.ts +0 -19
- package/dist/store/internal/read-existing.d.ts.map +0 -1
- package/dist/store/internal/read-existing.js +0 -29
- package/dist/store/internal/read-existing.js.map +0 -1
- package/dist/store/internal/score-direction.d.ts +0 -32
- package/dist/store/internal/score-direction.d.ts.map +0 -1
- package/dist/store/internal/score-direction.js +0 -39
- package/dist/store/internal/score-direction.js.map +0 -1
- package/dist/store/internal/search-filter.d.ts +0 -4
- package/dist/store/internal/search-filter.d.ts.map +0 -1
- package/dist/store/internal/search-filter.js +0 -11
- package/dist/store/internal/search-filter.js.map +0 -1
- package/dist/store/internal/semantic-search.d.ts.map +0 -1
- package/dist/store/internal/semantic-search.js.map +0 -1
- package/dist/store/internal/setup.d.ts.map +0 -1
- package/dist/store/internal/setup.js.map +0 -1
- package/dist/store/internal/validation.d.ts +0 -13
- package/dist/store/internal/validation.d.ts.map +0 -1
- package/dist/store/internal/validation.js +0 -35
- package/dist/store/internal/validation.js.map +0 -1
- package/dist/store/internal/write-verify.d.ts +0 -37
- package/dist/store/internal/write-verify.d.ts.map +0 -1
- package/dist/store/internal/write-verify.js +0 -68
- package/dist/store/internal/write-verify.js.map +0 -1
- package/dist/store/store.d.ts.map +0 -1
- package/dist/store/store.js.map +0 -1
- package/dist/store/types.d.ts.map +0 -1
- package/dist/store/types.js.map +0 -1
- package/dist/store/vector-backend.d.ts.map +0 -1
- package/dist/store/vector-backend.js.map +0 -1
|
@@ -0,0 +1,593 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Hides how a row write whose outcome matters is issued and settled.
|
|
4
|
+
*
|
|
5
|
+
* A write can lose its acknowledgement, be re-sent by the retry layer, race a
|
|
6
|
+
* concurrent writer, or leave an S3 object that only its row names. Three
|
|
7
|
+
* things answer that, and they are decided together here: a guard that admits
|
|
8
|
+
* the write only while the row still holds what the caller observed; a client
|
|
9
|
+
* request token, drawn once per logical write with a deadline inside the
|
|
10
|
+
* window the service honours it for, so a re-send of a committed write is
|
|
11
|
+
* discarded (record 6); and a strongly consistent read-back that turns a write
|
|
12
|
+
* whose outcome was lost into one of three verdicts. Whether a write needs the
|
|
13
|
+
* token is the payload descriptor's question, answered here once.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.WRITE_ID_ATTRIBUTE = exports.OVERWRITE_CAS_MAX_ATTEMPTS = void 0;
|
|
17
|
+
exports.referencesS3Object = referencesS3Object;
|
|
18
|
+
exports.transactIdempotently = transactIdempotently;
|
|
19
|
+
exports.putIdempotently = putIdempotently;
|
|
20
|
+
exports.deleteIdempotently = deleteIdempotently;
|
|
21
|
+
exports.commitRow = commitRow;
|
|
22
|
+
exports.revisionGuard = revisionGuard;
|
|
23
|
+
exports.writeIdGuard = writeIdGuard;
|
|
24
|
+
exports.isConditionalCheckFailed = isConditionalCheckFailed;
|
|
25
|
+
exports.rejectedRow = rejectedRow;
|
|
26
|
+
exports.offloadedKey = offloadedKey;
|
|
27
|
+
exports.identityOf = identityOf;
|
|
28
|
+
exports.verdictFor = verdictFor;
|
|
29
|
+
exports.readRow = readRow;
|
|
30
|
+
exports.verifyRow = verifyRow;
|
|
31
|
+
exports.isRowAbsent = isRowAbsent;
|
|
32
|
+
const node_crypto_1 = require("node:crypto");
|
|
33
|
+
const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
|
|
34
|
+
const clock_1 = require("../clock");
|
|
35
|
+
const codec_1 = require("../codec/codec");
|
|
36
|
+
const classify_1 = require("../errors/classify");
|
|
37
|
+
const error_code_1 = require("../errors/error-code");
|
|
38
|
+
const cancellation_1 = require("./cancellation");
|
|
39
|
+
const retry_1 = require("./retry");
|
|
40
|
+
const table_schema_1 = require("./table-schema");
|
|
41
|
+
/**
|
|
42
|
+
* Whether a payload descriptor names an S3 object, and so whether the write
|
|
43
|
+
* carrying it can strand one.
|
|
44
|
+
*
|
|
45
|
+
* An inline payload references nothing outside its own row: a re-landed copy
|
|
46
|
+
* of such a write is an ordinary last-write-wins outcome, not a lost object.
|
|
47
|
+
* Only the offloaded write is worth the extra write capacity a transaction
|
|
48
|
+
* costs, which is why the question is asked of the descriptor rather than of
|
|
49
|
+
* the adapter — an adapter with an offloader configured still writes inline
|
|
50
|
+
* whenever the payload is under its threshold.
|
|
51
|
+
*
|
|
52
|
+
* Accepts: `descriptor` — a full payload descriptor, or the projection a
|
|
53
|
+
* pre-write read returns without the inline bytes.
|
|
54
|
+
*
|
|
55
|
+
* Returns: true when the payload was offloaded to S3.
|
|
56
|
+
*
|
|
57
|
+
* Throws: nothing.
|
|
58
|
+
*/
|
|
59
|
+
function referencesS3Object(descriptor) {
|
|
60
|
+
return descriptor.location === codec_1.PayloadLocation.S3;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Commit `actions` as one
|
|
64
|
+
* {@link https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html | TransactWriteItems}
|
|
65
|
+
* under a client request token, so a re-send DynamoDB already applied lands as
|
|
66
|
+
* a no-op instead of as a second write.
|
|
67
|
+
*
|
|
68
|
+
* A write that offloads its payload uploads the object first and commits the
|
|
69
|
+
* row second. Lose the row's acknowledgement and the retry can commit it
|
|
70
|
+
* twice; the cleanup that follows then releases an object the other attempt's
|
|
71
|
+
* row still names, leaving a live row pointing at nothing. A delete has the
|
|
72
|
+
* mirror problem: its retry, re-evaluated rather than deduplicated, meets a row
|
|
73
|
+
* a competitor wrote after the first attempt landed and erases it. `PutItem`
|
|
74
|
+
* and `DeleteItem` take no token and cannot be made to; a one-item transaction
|
|
75
|
+
* can, and inside the service's 10-minute window the re-send is discarded
|
|
76
|
+
* rather than applied.
|
|
77
|
+
*
|
|
78
|
+
* **What the token guarantees, and what it does not.** A write whose first
|
|
79
|
+
* attempt **committed** is applied exactly once, at that moment — so a later
|
|
80
|
+
* writer supersedes it normally and a concurrent delete stands. A write whose
|
|
81
|
+
* first attempt was **rejected by its condition** carries no idempotency at
|
|
82
|
+
* all: a cancelled transaction never completes, so DynamoDB caches no result
|
|
83
|
+
* for its token, and a retry with the same token is a **fresh evaluation**
|
|
84
|
+
* against the table as it stands at retry time. The short version — "a retried
|
|
85
|
+
* write lands once" — is therefore false, and every caller that reasons about a
|
|
86
|
+
* rejection must reason about the table, not about the token.
|
|
87
|
+
*
|
|
88
|
+
* Two readings that sentence must not be given, because the shorter version of
|
|
89
|
+
* it invites both. It is about writes **this library sends with a token**: it
|
|
90
|
+
* says nothing about a `BatchWriteItem`, which can carry no token at all, and
|
|
91
|
+
* nothing about a first request that was already wrong, which no token can
|
|
92
|
+
* help — a token makes a *re-sent* request harmless and has nothing to say
|
|
93
|
+
* about a race that needs no retry to go wrong. And "exactly once" is about
|
|
94
|
+
* **application, not ordering**: a cancelled-then-retried write applies later
|
|
95
|
+
* than its first attempt, or not at all. What a cancellation does still
|
|
96
|
+
* reserve is the token's *parameters*, which is why a re-pin must draw a fresh
|
|
97
|
+
* one rather than re-present this one.
|
|
98
|
+
*
|
|
99
|
+
* **Stable across a re-send, fresh across a re-pin.** The input — token
|
|
100
|
+
* included — is built once here, outside the retry closure, so every attempt
|
|
101
|
+
* of one budget re-sends the identical request and the token deduplicates it.
|
|
102
|
+
* A compare-and-swap that loses and re-pins calls this again and draws a new
|
|
103
|
+
* token, which is required rather than merely tidy: the re-pinned request
|
|
104
|
+
* carries a different `ConditionExpression`, and the same token presented with
|
|
105
|
+
* changed parameters inside the window is refused with
|
|
106
|
+
* `IdempotentParameterMismatchException` — a name that appears in no retry
|
|
107
|
+
* list and in no handler in this package, so reusing a token would surface a
|
|
108
|
+
* raw SDK error to a caller. That refusal is only observable against real
|
|
109
|
+
* DynamoDB: the local image does not reserve a cancelled token's parameters
|
|
110
|
+
* and simply re-evaluates the changed body, so the unit and integration tiers
|
|
111
|
+
* can assert only that two re-pins carry different tokens, and the refusal
|
|
112
|
+
* itself belongs to the tier that runs against AWS.
|
|
113
|
+
*
|
|
114
|
+
* **At most one guarded action, and only ever as many actions as must land
|
|
115
|
+
* together.** A cancellation is read as a guard rejection only while exactly
|
|
116
|
+
* one cause remains once the items along for the ride are set aside, so a
|
|
117
|
+
* second *guarded* action whose condition fails in the same race would turn
|
|
118
|
+
* that race into an unrecognised non-retryable error — which is exactly what a
|
|
119
|
+
* competing writer of the same checkpoint id would produce, since it fails
|
|
120
|
+
* both rows at once. Every row-at-a-time caller here
|
|
121
|
+
* passes a single action for a second reason as well: a transaction cancels
|
|
122
|
+
* whole, so one item per transaction keeps each write's outcome independent of
|
|
123
|
+
* its neighbours', which is what the fan-out writers rely on. More than one
|
|
124
|
+
* action is for the callers whose rows are atomic by contract and carry no
|
|
125
|
+
* condition between them.
|
|
126
|
+
*
|
|
127
|
+
* Accepts: `deps` — the adapter's client, table and retry policy. `actions` —
|
|
128
|
+
* the `Put`, `Delete`, `Update` or `ConditionCheck` entries to commit
|
|
129
|
+
* together, captured by reference and re-sent unchanged on every attempt of
|
|
130
|
+
* the budget, so a caller must mutate neither the array nor an entry of it while this call is in flight.
|
|
131
|
+
* `options.signal` — aborts between attempts. `options.rng` — the jitter
|
|
132
|
+
* source, replacing the default one for a caller whose tests need the backoff
|
|
133
|
+
* to take no time. `options.minAttempts` — the fewest attempts the write is
|
|
134
|
+
* given: the budget is the larger of it and the adapter's own `maxAttempts`,
|
|
135
|
+
* so a caller policy may raise it, never lower it. Without `rng` and
|
|
136
|
+
* `minAttempts` the retry receives the adapter's policy, the signal and the
|
|
137
|
+
* deadline, and nothing else.
|
|
138
|
+
*
|
|
139
|
+
* Returns: nothing. The transaction committed, or it threw.
|
|
140
|
+
*
|
|
141
|
+
* Throws: whatever the transaction throws.
|
|
142
|
+
*
|
|
143
|
+
* Guarantees: the retrying stops while the token is still honoured. The budget
|
|
144
|
+
* carries a deadline of {@link MAX_WRITE_LIFETIME_MS} from now, half the
|
|
145
|
+
* window the service deduplicates over, so the wait that would carry this
|
|
146
|
+
* write past it is never started.
|
|
147
|
+
*
|
|
148
|
+
* Nothing in the token enforces that window — the service honours a token for
|
|
149
|
+
* `TOKEN_IDEMPOTENCY_WINDOW_MS` whatever the caller's policy says, and a
|
|
150
|
+
* re-send arriving after it closes is a new request that is applied. The
|
|
151
|
+
* deadline is what keeps the budget inside it, and it bounds only the waits
|
|
152
|
+
* *between* attempts: it is tested before each backoff and cannot shorten an
|
|
153
|
+
* attempt already in flight. On a client this library builds the per-attempt
|
|
154
|
+
* `DEFAULT_REQUEST_TIMEOUT_MS` bounds that attempt as well; on an **injected**
|
|
155
|
+
* client, which is used exactly as given and may carry no request timeout at
|
|
156
|
+
* all, a single hung request can still carry the budget past the window, and
|
|
157
|
+
* nothing here prevents it.
|
|
158
|
+
*
|
|
159
|
+
* That deadline is spread onto a copy of the policy and never assigned onto
|
|
160
|
+
* it: `retryFor` hands back the adapter's own options object when there is no
|
|
161
|
+
* signal, and stamping a deadline onto that object would bound every later
|
|
162
|
+
* call of the same adapter by this call's clock.
|
|
163
|
+
*/
|
|
164
|
+
async function transactIdempotently(deps, actions, options = {}) {
|
|
165
|
+
const input = { TransactItems: actions, ClientRequestToken: (0, node_crypto_1.randomUUID)() };
|
|
166
|
+
const deadlineAt = (0, clock_1.nowMs)() + retry_1.MAX_WRITE_LIFETIME_MS;
|
|
167
|
+
await (0, retry_1.withDynamoDBRetry)((request) => deps.client.transactWrite(input, request), {
|
|
168
|
+
...(0, retry_1.retryFor)(deps, options.signal),
|
|
169
|
+
...(options.minAttempts === undefined
|
|
170
|
+
? {}
|
|
171
|
+
: { maxAttempts: Math.max(options.minAttempts, deps.retry?.maxAttempts ?? 0) }),
|
|
172
|
+
...(options.rng === undefined ? {} : { rng: options.rng }),
|
|
173
|
+
deadlineAt,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Commit one row under a request token; see {@link transactIdempotently} for
|
|
178
|
+
* why the write takes a transaction's shape and what the token buys.
|
|
179
|
+
*
|
|
180
|
+
* Accepts: `deps` — the adapter's client, table and retry policy. `item` — the
|
|
181
|
+
* row to commit. It is captured by reference and re-sent unchanged on every
|
|
182
|
+
* attempt of the budget, so a caller must not mutate it while this call is in
|
|
183
|
+
* flight: the re-send would carry the same token with different parameters,
|
|
184
|
+
* which the service refuses with `IdempotentParameterMismatchException`.
|
|
185
|
+
* `guard` — the condition fragments from `revisionGuard`, or a caller's own;
|
|
186
|
+
* omitted writes unconditionally, which is the case a token helps most, since
|
|
187
|
+
* nothing else stops a re-send from landing. `signal` — aborts between
|
|
188
|
+
* attempts.
|
|
189
|
+
*
|
|
190
|
+
* Returns: nothing. The write committed, or it threw.
|
|
191
|
+
*
|
|
192
|
+
* Throws: whatever the transaction throws. A guard rejection now arrives as a
|
|
193
|
+
* `TransactionCanceledException` whose single reason is `ConditionalCheckFailed`
|
|
194
|
+
* rather than as a `ConditionalCheckFailedException`; both answer
|
|
195
|
+
* `isConditionalCheckFailed` and both carry the rejected row to
|
|
196
|
+
* `rejectedRow`, so a caller reads them the same way. A spent budget
|
|
197
|
+
* throws `RETRY_EXHAUSTED` as any other call does.
|
|
198
|
+
*/
|
|
199
|
+
async function putIdempotently(deps, item, guard, signal) {
|
|
200
|
+
await transactIdempotently(deps, [{ Put: { TableName: deps.tableName, Item: item, ...guard } }], {
|
|
201
|
+
signal,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Remove one row under a request token; see {@link transactIdempotently} for
|
|
206
|
+
* why the delete takes a transaction's shape and what the token buys.
|
|
207
|
+
*
|
|
208
|
+
* It buys more here than a put's token does. An unconditional `DeleteItem`
|
|
209
|
+
* cannot be turned away, so a retry that arrives after the first attempt
|
|
210
|
+
* already committed removes whatever a competitor has written since. Under a
|
|
211
|
+
* token that replay is answered from the idempotency cache and never reaches
|
|
212
|
+
* the row, which is what makes a condition failure *informative*: it now
|
|
213
|
+
* proves a genuine race rather than possibly reporting this call's own
|
|
214
|
+
* landed attempt.
|
|
215
|
+
*
|
|
216
|
+
* Accepts: `deps` — the adapter's client, table and retry policy. `key` — the
|
|
217
|
+
* row's key, captured by reference and re-sent unchanged for the same reason a
|
|
218
|
+
* put's item is. `guard` — the condition fragments pinning what the caller
|
|
219
|
+
* observed; omitted deletes unconditionally. `signal` — aborts between
|
|
220
|
+
* attempts.
|
|
221
|
+
*
|
|
222
|
+
* Returns: nothing. The delete committed, or it threw.
|
|
223
|
+
*
|
|
224
|
+
* Throws: as {@link putIdempotently} does. A rejection carries the row that
|
|
225
|
+
* turned it away only while there is one — an absent row cancels with no
|
|
226
|
+
* `Item` at all, which is how a caller tells "someone rewrote it" from "it was
|
|
227
|
+
* already gone". **That reading is only sound while the guard asks for the
|
|
228
|
+
* row.** Every guard `revisionGuard` builds carries
|
|
229
|
+
* `ReturnValuesOnConditionCheckFailure: 'ALL_OLD'`; a caller passing its own
|
|
230
|
+
* guard without it, or no guard at all, gets an empty rejection for a row that
|
|
231
|
+
* is very much still there — and a caller that then releases what that row
|
|
232
|
+
* names has deleted an object a live row points at.
|
|
233
|
+
*/
|
|
234
|
+
async function deleteIdempotently(deps, key, guard, signal) {
|
|
235
|
+
await transactIdempotently(deps, [{ Delete: { TableName: deps.tableName, Key: key, ...guard } }], { signal });
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Commit one row: as a tokened one-item transaction when it names an S3
|
|
239
|
+
* object, and as a plain `PutItem` otherwise.
|
|
240
|
+
*
|
|
241
|
+
* The descriptor decides, not the adapter. An adapter with an offloader still
|
|
242
|
+
* writes inline whenever a payload is under its threshold, and a row that names
|
|
243
|
+
* no object has nothing a re-sent write could strand, so a transaction would
|
|
244
|
+
* charge twice the write capacity to buy nothing.
|
|
245
|
+
*
|
|
246
|
+
* Accepts: `row` — the item to write. `payload` — its payload descriptor, which
|
|
247
|
+
* decides the shape. `options.guard` — the condition the write carries, if any.
|
|
248
|
+
* `options.signal` — cancels the retries.
|
|
249
|
+
*
|
|
250
|
+
* Returns: nothing, once the write committed.
|
|
251
|
+
*
|
|
252
|
+
* Throws: the guard's rejection — a rejection that `isConditionalCheckFailed`
|
|
253
|
+
* answers true for, with the row that turned the write away readable through
|
|
254
|
+
* `rejectedRow`; whatever the write throws once its retries are spent.
|
|
255
|
+
*/
|
|
256
|
+
async function commitRow(deps, row, payload, options = {}) {
|
|
257
|
+
if (referencesS3Object(payload)) {
|
|
258
|
+
await putIdempotently(deps, row, options.guard, options.signal);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
await (0, retry_1.withDynamoDBRetry)((request) => deps.client.put({ TableName: deps.tableName, Item: row, ...options.guard }, request), (0, retry_1.retryFor)(deps, options.signal));
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Compare-and-swap attempts before a caller gives up and overwrites
|
|
265
|
+
* unconditionally. Kept small on purpose: DynamoDB charges write capacity for a
|
|
266
|
+
* *failed* conditional write too, sized on the existing item, so an aggressive
|
|
267
|
+
* loop turns contention into cost. Three attempts settle every realistic race,
|
|
268
|
+
* and the fallback is exactly the pre-0.9.0 behaviour rather than an error.
|
|
269
|
+
*/
|
|
270
|
+
exports.OVERWRITE_CAS_MAX_ATTEMPTS = 3;
|
|
271
|
+
const RETURN_REJECTED_ROW = { ReturnValuesOnConditionCheckFailure: 'ALL_OLD' };
|
|
272
|
+
/**
|
|
273
|
+
* Build the condition admitting a write only while the row still holds the
|
|
274
|
+
* revision this caller observed.
|
|
275
|
+
*
|
|
276
|
+
* Without it, two concurrent overwrites both read the same previous payload
|
|
277
|
+
* descriptor, both commit their own nonced upload, and both delete that same
|
|
278
|
+
* previous object — leaving the loser's upload orphaned with nothing left
|
|
279
|
+
* recording that it ever existed. A post-commit read-back cannot repair that,
|
|
280
|
+
* because neither writer can learn of an object it never saw; only refusing the
|
|
281
|
+
* second write until it re-reads can.
|
|
282
|
+
*
|
|
283
|
+
* A row with no revision attribute was written before 0.9.0. Pinning its
|
|
284
|
+
* *absence* is what makes the swap correct across an upgrade: the first writer
|
|
285
|
+
* to touch such a row stamps one, and any racer still holding the pre-upgrade
|
|
286
|
+
* observation is turned away.
|
|
287
|
+
*
|
|
288
|
+
* Accepts: `attribute` — the revision attribute's name, since the checkpointer
|
|
289
|
+
* reuses its `writeGroup` rather than carrying a second one. `observed` — the
|
|
290
|
+
* three states a caller can have seen: no row, a row with no revision, a row
|
|
291
|
+
* with one.
|
|
292
|
+
*
|
|
293
|
+
* Returns: the condition fragments for a `PutCommand`, one per state —
|
|
294
|
+
* `attribute_not_exists(PK)`, `attribute_not_exists(<attribute>)`, and
|
|
295
|
+
* equality. Every one asks DynamoDB to attach the existing row to a rejection,
|
|
296
|
+
* so a swap that loses re-pins from the exception instead of spending a second
|
|
297
|
+
* strongly-consistent read.
|
|
298
|
+
*
|
|
299
|
+
* Throws: nothing.
|
|
300
|
+
*
|
|
301
|
+
* Guarantees: it pins the revision the caller observed — a value, its absence,
|
|
302
|
+
* or the row's own absence — and nothing else about the row. A revision is
|
|
303
|
+
* drawn afresh by every write that replaces the row (`randomUUID()` for a
|
|
304
|
+
* store record, the call's own `writeGroup` for a special row) and nothing
|
|
305
|
+
* restores a spent one, so a **satisfied** guard proves that nothing replaced
|
|
306
|
+
* the row between the caller's read and this write: the row overwritten is the
|
|
307
|
+
* row observed, still naming the descriptor the caller read off it. That is
|
|
308
|
+
* what makes it safe to release the payload this write superseded — the
|
|
309
|
+
* caller is holding the descriptor the row really named, not a stale copy of
|
|
310
|
+
* one a racer has already replaced and released. An update that leaves the
|
|
311
|
+
* revision alone can still have touched the row in between — the recency-index
|
|
312
|
+
* backfill is the one such write in this package, and it adds index keys and
|
|
313
|
+
* nothing else — so what the guard pins is the row's identity, not every byte
|
|
314
|
+
* of it.
|
|
315
|
+
*
|
|
316
|
+
* A **rejected** guard proves the mirror and no more: the row is not the one
|
|
317
|
+
* observed. It is never evidence that a competitor won, because a write whose
|
|
318
|
+
* acknowledgement was lost can be turned away by the row it committed itself
|
|
319
|
+
* — see {@link isConditionalCheckFailed} — and it carries no idempotency for a
|
|
320
|
+
* retry either, since a rejected attempt commits nothing for a token to be
|
|
321
|
+
* answered from.
|
|
322
|
+
*/
|
|
323
|
+
function revisionGuard(attribute, observed) {
|
|
324
|
+
if (!observed.exists)
|
|
325
|
+
return {
|
|
326
|
+
...RETURN_REJECTED_ROW,
|
|
327
|
+
ConditionExpression: `attribute_not_exists(${table_schema_1.PARTITION_KEY_ATTRIBUTE})`,
|
|
328
|
+
};
|
|
329
|
+
if (observed.revision === undefined) {
|
|
330
|
+
return {
|
|
331
|
+
...RETURN_REJECTED_ROW,
|
|
332
|
+
ConditionExpression: 'attribute_not_exists(#rev)',
|
|
333
|
+
ExpressionAttributeNames: { '#rev': attribute },
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
...RETURN_REJECTED_ROW,
|
|
338
|
+
ConditionExpression: '#rev = :rev',
|
|
339
|
+
ExpressionAttributeNames: { '#rev': attribute },
|
|
340
|
+
ExpressionAttributeValues: { ':rev': observed.revision },
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/** The field a payload descriptor carries the id of the write that produced it in. */
|
|
344
|
+
exports.WRITE_ID_ATTRIBUTE = 'writeId';
|
|
345
|
+
/**
|
|
346
|
+
* Build the condition admitting a delete only while the row still carries the
|
|
347
|
+
* per-write id the reader observed on it.
|
|
348
|
+
*
|
|
349
|
+
* A partition-wide delete reads a partition and then deletes what it saw. A row
|
|
350
|
+
* rewritten in between was acknowledged to its writer and is erased anyway, and
|
|
351
|
+
* the object it named is released — which this turns into a refusal the pass
|
|
352
|
+
* reports instead. The id is the write's own, never recomputed from the row's
|
|
353
|
+
* state, so nothing can restore it and no second writer can arrive at it.
|
|
354
|
+
*
|
|
355
|
+
* Accepts: `attribute` — the attribute the id lives on, top-level for a row
|
|
356
|
+
* that carries one (a pending write's `writeGroup`, a session row's own id) and
|
|
357
|
+
* the payload attribute otherwise. `id` — the id the read observed; a row
|
|
358
|
+
* observed *without* one is deleted unconditionally rather than pinned, so this
|
|
359
|
+
* is never called for it. `field` — the field inside the attribute, which turns
|
|
360
|
+
* the condition into a document path over a descriptor; omitted for a
|
|
361
|
+
* top-level pin.
|
|
362
|
+
*
|
|
363
|
+
* Returns: the condition fragments for a `DeleteCommand`, asking DynamoDB to
|
|
364
|
+
* attach the existing row to a rejection so the refusal can be told from a row
|
|
365
|
+
* that was already gone with no second read.
|
|
366
|
+
*
|
|
367
|
+
* Throws: nothing.
|
|
368
|
+
*
|
|
369
|
+
* Guarantees: one equality and nothing else. A document path over an attribute
|
|
370
|
+
* that is absent, or present without the field, evaluates false rather than
|
|
371
|
+
* failing the request, so one shape covers an offloaded row, an inline one and
|
|
372
|
+
* a row a racer has rewritten into either.
|
|
373
|
+
*
|
|
374
|
+
* What a satisfied guard proves is the delete's counterpart of
|
|
375
|
+
* {@link revisionGuard}'s: the row removed is the row the partition read saw,
|
|
376
|
+
* not a replacement a later write left at the same key — which is what makes
|
|
377
|
+
* the object that read recorded against it the right one to release. A
|
|
378
|
+
* rejection means the row now carries some other write's id, and this pass
|
|
379
|
+
* leaves it in place and reports it rather than re-pinning, because a row it
|
|
380
|
+
* never read is not its to delete.
|
|
381
|
+
*/
|
|
382
|
+
function writeIdGuard(attribute, id, field) {
|
|
383
|
+
const names = { '#pin': attribute };
|
|
384
|
+
if (field !== undefined)
|
|
385
|
+
names['#field'] = field;
|
|
386
|
+
return {
|
|
387
|
+
...RETURN_REJECTED_ROW,
|
|
388
|
+
ConditionExpression: field === undefined ? '#pin = :pin' : '#pin.#field = :pin',
|
|
389
|
+
ExpressionAttributeNames: names,
|
|
390
|
+
ExpressionAttributeValues: { ':pin': id },
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Whether a conditional write was turned away by its guard.
|
|
395
|
+
*
|
|
396
|
+
* The same rejection has two shapes, because a `PutItem` reports it as an
|
|
397
|
+
* exception of its own while a `TransactWriteItems` reports it as one
|
|
398
|
+
* cancellation reason among one per item. Both are the same event to a caller,
|
|
399
|
+
* so both answer true here and neither is a caller's business to tell apart.
|
|
400
|
+
*
|
|
401
|
+
* Accepts: `error` — any error; the exception's name and, for a cancelled
|
|
402
|
+
* transaction, its reasons are read.
|
|
403
|
+
*
|
|
404
|
+
* Returns: true for `ConditionalCheckFailedException`, and for a cancellation
|
|
405
|
+
* whose one cause is a `ConditionalCheckFailed` reason, as the classifier
|
|
406
|
+
* decides both.
|
|
407
|
+
*
|
|
408
|
+
* Throws: nothing.
|
|
409
|
+
*
|
|
410
|
+
* Guarantees: **not** evidence that a competitor won. A `PutCommand` retried
|
|
411
|
+
* after its response was lost can re-hit the row it wrote itself and fail
|
|
412
|
+
* identically, and the two are indistinguishable from the rejection alone —
|
|
413
|
+
* which is why every caller reads the row back before deleting anything.
|
|
414
|
+
*/
|
|
415
|
+
function isConditionalCheckFailed(error) {
|
|
416
|
+
return (0, classify_1.classifyAwsError)(error) === error_code_1.ErrorCode.CONDITION_CONFLICT;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* The row that turned a conditional write away, when DynamoDB attached it
|
|
420
|
+
* (`ReturnValuesOnConditionCheckFailure: 'ALL_OLD'`). Verified against real
|
|
421
|
+
* DynamoDB: the document client does not unmarshall an *error* payload the way
|
|
422
|
+
* it unmarshalls a response, so the item arrives in raw AttributeValue form and
|
|
423
|
+
* is unmarshalled here. Undefined when the rejection carries no item — the row
|
|
424
|
+
* was deleted between the observation and the write — in which case the caller
|
|
425
|
+
* falls back to a read.
|
|
426
|
+
*
|
|
427
|
+
* A cancelled transaction attaches the same row to the cancellation reason of
|
|
428
|
+
* the item whose condition failed, rather than to the error itself, and leaves
|
|
429
|
+
* it in the same raw form — so there is one place more to look and still one
|
|
430
|
+
* unmarshalling.
|
|
431
|
+
*
|
|
432
|
+
* Accepts: `error` — any error; only a rejection from a guard built by
|
|
433
|
+
* {@link revisionGuard} carries the item.
|
|
434
|
+
*
|
|
435
|
+
* Returns: the row as a plain document, or undefined.
|
|
436
|
+
*
|
|
437
|
+
* Throws: whatever `unmarshall` rejects for an item that is not in
|
|
438
|
+
* AttributeValue form.
|
|
439
|
+
*/
|
|
440
|
+
function rejectedRow(error) {
|
|
441
|
+
const attached = error.Item;
|
|
442
|
+
const raw = attached ?? (0, cancellation_1.conditionalCheckFailure)(error)?.Item;
|
|
443
|
+
return raw === undefined ? undefined : (0, util_dynamodb_1.unmarshall)(raw);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* The S3 key an offloaded descriptor points at.
|
|
447
|
+
*
|
|
448
|
+
* Accepts: any descriptor, or none.
|
|
449
|
+
*
|
|
450
|
+
* Returns: the key, or undefined for an inline or absent payload — "this row
|
|
451
|
+
* names no object", which is what a caller comparing two writes needs.
|
|
452
|
+
*
|
|
453
|
+
* Throws: nothing.
|
|
454
|
+
*/
|
|
455
|
+
function offloadedKey(descriptor) {
|
|
456
|
+
return descriptor?.location === codec_1.PayloadLocation.S3 ? descriptor.s3Key : undefined;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* The identity `row` carries under `probe`.
|
|
460
|
+
*
|
|
461
|
+
* Accepts: `probe.kind` — `'attribute'` reads the identity straight out of the
|
|
462
|
+
* attribute, `'descriptor'` reads the S3 key of the descriptor stored there.
|
|
463
|
+
* `row` — as read, or undefined when there is none.
|
|
464
|
+
*
|
|
465
|
+
* Returns: the identity, or undefined when the row is absent or carries none.
|
|
466
|
+
*
|
|
467
|
+
* Throws: nothing.
|
|
468
|
+
*/
|
|
469
|
+
function identityOf(probe, row) {
|
|
470
|
+
const stored = row?.[probe.attribute];
|
|
471
|
+
if (probe.kind === 'attribute')
|
|
472
|
+
return stored;
|
|
473
|
+
return offloadedKey(stored);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* The verdict for a row already in hand.
|
|
477
|
+
*
|
|
478
|
+
* Accepts: `row` — the row a conditional-write rejection carried back, which
|
|
479
|
+
* makes this verdict cost no read at all.
|
|
480
|
+
*
|
|
481
|
+
* Returns: `'landed'` when the row's identity is this write's, `'not-landed'`
|
|
482
|
+
* otherwise. Never `'unverified'`: the row was seen.
|
|
483
|
+
*
|
|
484
|
+
* Throws: nothing.
|
|
485
|
+
*/
|
|
486
|
+
function verdictFor(probe, row) {
|
|
487
|
+
return identityOf(probe, row) === probe.expected ? 'landed' : 'not-landed';
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* The projection for `read`, with no attribute name left unused, which DynamoDB
|
|
491
|
+
* also refuses.
|
|
492
|
+
*/
|
|
493
|
+
function projectionOf(read) {
|
|
494
|
+
const nested = read.descriptors ?? [];
|
|
495
|
+
const whole = [read.attribute, ...(read.also ?? [])].filter((name) => !nested.includes(name));
|
|
496
|
+
const names = {};
|
|
497
|
+
const paths = [];
|
|
498
|
+
whole.forEach((name, index) => {
|
|
499
|
+
names[`#a${index}`] = name;
|
|
500
|
+
paths.push(`#a${index}`);
|
|
501
|
+
});
|
|
502
|
+
nested.forEach((name, index) => {
|
|
503
|
+
names[`#d${index}`] = name;
|
|
504
|
+
paths.push(`#d${index}.#loc`, `#d${index}.#s3k`);
|
|
505
|
+
});
|
|
506
|
+
if (nested.length > 0)
|
|
507
|
+
Object.assign(names, { '#loc': 'location', '#s3k': 's3Key' });
|
|
508
|
+
return { expression: paths.join(', '), names };
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Read the row a probe names, strongly consistently.
|
|
512
|
+
*
|
|
513
|
+
* Accepts: `read.attribute` — always projected. `read.also` — further
|
|
514
|
+
* attributes, for a caller that needs the row itself back rather than only its
|
|
515
|
+
* identity. `read.descriptors` — descriptor attributes, projected as their
|
|
516
|
+
* `location` and `s3Key` only; each still comes back under its own name, as a
|
|
517
|
+
* map holding those two.
|
|
518
|
+
*
|
|
519
|
+
* Returns: the projected row, or undefined when there is none.
|
|
520
|
+
*
|
|
521
|
+
* Throws: the underlying error, which {@link verifyRow} turns into a verdict
|
|
522
|
+
* and a caller that wants the cause keeps. The two are separate functions
|
|
523
|
+
* because a synthetic error built from a caught one lost the original cause.
|
|
524
|
+
*
|
|
525
|
+
* Guarantees: strongly consistent — a read that may lag is no evidence at all
|
|
526
|
+
* about a write that may have landed.
|
|
527
|
+
*/
|
|
528
|
+
async function readRow(deps, read) {
|
|
529
|
+
const { expression, names } = projectionOf(read);
|
|
530
|
+
const result = await (0, retry_1.withDynamoDBRetry)((request) => deps.client.get({
|
|
531
|
+
TableName: deps.tableName,
|
|
532
|
+
Key: read.key,
|
|
533
|
+
ConsistentRead: true,
|
|
534
|
+
ProjectionExpression: expression,
|
|
535
|
+
ExpressionAttributeNames: names,
|
|
536
|
+
}, request), deps.retry);
|
|
537
|
+
return result.Item;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Read one row back to establish what an ambiguous write actually did.
|
|
541
|
+
*
|
|
542
|
+
* No failure is proof of a non-commit: `withDynamoDBRetry` re-issues a write
|
|
543
|
+
* whose response was lost, and those re-issues can time out at the transport
|
|
544
|
+
* without reaching DynamoDB, so the budget is spent on a `RETRY_EXHAUSTED` error
|
|
545
|
+
* while the row is live. Every caller that is about to delete something on the
|
|
546
|
+
* strength of a failure reads the row first, through here.
|
|
547
|
+
*
|
|
548
|
+
* Accepts: `probe.expected` — the identity that would prove the write landed.
|
|
549
|
+
* `undefined` means there is nothing at stake — a record with no revision, a
|
|
550
|
+
* checkpoint with nothing offloaded — and no read is spent.
|
|
551
|
+
*
|
|
552
|
+
* Returns: the verdict and, when one was read, the row. See
|
|
553
|
+
* {@link WriteVerdict} for what each answer licenses the caller to do.
|
|
554
|
+
*
|
|
555
|
+
* Throws: nothing. A failed read is the `'unverified'` answer, not an error:
|
|
556
|
+
* the caller is already handling a failure and needs a decision, not a second
|
|
557
|
+
* one.
|
|
558
|
+
*/
|
|
559
|
+
async function verifyRow(deps, probe) {
|
|
560
|
+
if (probe.expected === undefined)
|
|
561
|
+
return { verdict: 'not-landed' };
|
|
562
|
+
try {
|
|
563
|
+
const row = await readRow(deps, probe);
|
|
564
|
+
return { verdict: verdictFor(probe, row), row };
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
return { verdict: 'unverified' };
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Whether a row is confirmed absent right now — what resolves an ambiguous
|
|
572
|
+
* retry-exhausted *delete*, where the delete may well have landed server-side
|
|
573
|
+
* and only its acknowledgement was lost. Only the partition key is projected:
|
|
574
|
+
* existence is the whole question.
|
|
575
|
+
*
|
|
576
|
+
* Accepts: `key` — the row's.
|
|
577
|
+
*
|
|
578
|
+
* Returns: `true` only when a strongly consistent read found no row; `false`
|
|
579
|
+
* when it found one or when the read itself failed, because a failed read
|
|
580
|
+
* confirms nothing — "not confirmed", never "still there": the caller only
|
|
581
|
+
* rethrows on `false`, so nothing is deleted on the strength of a read that
|
|
582
|
+
* did not happen.
|
|
583
|
+
*
|
|
584
|
+
* Throws: nothing.
|
|
585
|
+
*/
|
|
586
|
+
async function isRowAbsent(deps, key) {
|
|
587
|
+
try {
|
|
588
|
+
return (await readRow(deps, { key, attribute: table_schema_1.PARTITION_KEY_ATTRIBUTE })) === undefined;
|
|
589
|
+
}
|
|
590
|
+
catch {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
}
|