@jzhuo3/dynamodb-lib 0.1.0 → 0.1.1

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/PUBLISHING.md CHANGED
@@ -34,7 +34,15 @@ npm publish --access public
34
34
 
35
35
  `prepublishOnly` runs the source, unit, build and package checks. Integration tests are explicit because they require an available database; CI runs them against DynamoDB Local. The AWS SDK dependency tree is deliberately bundled with the archive, alongside `npm-shrinkwrap.json`, to preserve the tested Node 18-compatible, patched dependencies. After updating dependencies, refresh the shrinkwrap and run the clean consumer installation check as well as the Node runtime matrix.
36
36
 
37
- This repository contains a verification workflow, but no automatic publishing trigger; the actual release identity and credentials still need to be configured.
37
+ ## Automated publishing
38
+
39
+ Commit and push the workflows and both Release Please configuration files before creating the next release. `release-please.yml` uses the repository variable `RELEASE_APP_CLIENT_ID` and secret `RELEASE_APP_PRIVATE_KEY` to prepare release PRs and create GitHub releases after those PRs are merged.
40
+
41
+ When a non-prerelease GitHub release is published, `.github/workflows/publish.yml` checks that its tag is `vX.Y.Z` and matches `package.json`. It runs the reusable CI workflow against that exact commit, including unit, package, coverage, clean installation, and DynamoDB Local integration checks across Node 18, 20, 22, and 24. Only after verification passes does its `publish` job enter the `npm` environment and publish using Node 24 and npm 11. Draft releases and prereleases do not publish to npm.
42
+
43
+ Configure the npm package's GitHub Actions trusted publisher with owner `maczhuo`, repository `dynamodb-lib`, workflow filename `publish.yml`, environment `npm`, and permission for direct `npm publish`. The GitHub environment `npm` must allow release tags such as `v*`. No `NPM_TOKEN` is required. Any environment approval rules will pause the publish job until satisfied.
44
+
45
+ The workflow does not retroactively publish existing GitHub releases. If a run fails before publication, fix the external configuration and rerun the failed jobs from GitHub Actions. An already published npm version cannot be published again; code or workflow fixes require a new release containing those changes. Initial package creation may still require the manual bootstrap described above.
38
46
 
39
47
  Official references (checked September 2026):
40
48
 
package/dist/cjs/index.js CHANGED
@@ -421,6 +421,7 @@ class DynamoDBTransaction {
421
421
  if (i === retries - 1) {
422
422
  throw error;
423
423
  }
424
+ this.logger.warn('DynamoDB retrying transaction conflict', { mode: this.mode, attempt: i + 1, maxAttempts: retries, delayMs: delay * (i + 1) });
424
425
  await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
425
426
  }
426
427
  else {
@@ -669,10 +670,6 @@ class DynamoDBTransaction {
669
670
  }
670
671
  ExpressionAttributeValues[attributeValueVariable] = value;
671
672
  });
672
- this.logger.debug('Update Expression: %s', UpdateExpression);
673
- this.logger.debug('Condition Expression: %s', ConditionExpression);
674
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
675
- this.logger.debug('Expression Attribute Values: %o', ExpressionAttributeValues);
676
673
  this.transactWriteItems.push({
677
674
  Update: {
678
675
  TableName: table.name,
@@ -748,6 +745,7 @@ class DynamoDBTransaction {
748
745
  const result = {
749
746
  numberOfTransactItems: 0,
750
747
  };
748
+ this.logger.debug('DynamoDB transaction started', { mode: this.mode, itemCount: this.mode === DynamoDBTransactionMode.READ ? this.transactGetItems.length : this.transactWriteItems.length });
751
749
  switch (this.mode) {
752
750
  case DynamoDBTransactionMode.READ:
753
751
  command = new lib_dynamodb_1.TransactGetCommand({
@@ -759,21 +757,21 @@ class DynamoDBTransaction {
759
757
  const { Responses } = (await this.txConflictRetryWrapper(command, this.txRetryLimit, this.txRetryDelay).catch((e) => {
760
758
  this._cleanup();
761
759
  if (isTransactionConflictError(e)) {
762
- this.logger.error('Transaction conflict due to TransactionConflict');
760
+ this.logger.error('DynamoDB transaction conflict retry limit reached', { errorName: e.name });
763
761
  throw new DynamoDBError(409, {
764
762
  message: e.message,
765
763
  cause: e,
766
764
  });
767
765
  }
768
766
  else if (e.name === 'TransactionCanceledException') {
769
- this.logger.error(e.message);
767
+ this.logger.error('DynamoDB transaction canceled', { errorName: e.name });
770
768
  throw new DynamoDBError(400, {
771
769
  message: e.message,
772
770
  cause: e,
773
771
  });
774
772
  }
775
773
  else {
776
- this.logger.error('Transaction failed due to %s', e.message);
774
+ this.logger.error('Transaction failed', { errorName: e.name });
777
775
  throw new DynamoDBError(500, {
778
776
  message: e.message,
779
777
  cause: e,
@@ -822,28 +820,28 @@ class DynamoDBTransaction {
822
820
  await this.txConflictRetryWrapper(command, this.txRetryLimit, this.txRetryDelay).catch((e) => {
823
821
  this._cleanup();
824
822
  if (isTransactionConflictError(e)) {
825
- this.logger.error('Transaction conflict due to TransactionConflict');
823
+ this.logger.error('DynamoDB transaction conflict retry limit reached', { errorName: e.name });
826
824
  throw new DynamoDBError(409, {
827
825
  message: e.message,
828
826
  cause: e,
829
827
  });
830
828
  }
831
829
  else if (e.name === 'TransactionCanceledException') {
832
- this.logger.error(e.message);
830
+ this.logger.error('DynamoDB transaction canceled', { errorName: e.name });
833
831
  throw new DynamoDBError(400, {
834
832
  message: e.message,
835
833
  cause: e,
836
834
  });
837
835
  }
838
836
  else if (e.name === 'ValidationException') {
839
- this.logger.error('The request parameters are invalid with error: %s', e.message);
837
+ this.logger.error('The request parameters are invalid', { errorName: e.name });
840
838
  throw new DynamoDBError(400, {
841
839
  message: 'Invalid request parameters',
842
840
  cause: e,
843
841
  });
844
842
  }
845
843
  else {
846
- this.logger.error('Transaction failed due to %s', e.message);
844
+ this.logger.error('Transaction failed', { errorName: e.name });
847
845
  throw new DynamoDBError(500, {
848
846
  message: e.message,
849
847
  cause: e,
@@ -856,6 +854,7 @@ class DynamoDBTransaction {
856
854
  default:
857
855
  throw new Error('Invalid transaction mode');
858
856
  }
857
+ this.logger.debug('DynamoDB transaction completed', { mode: this.mode, itemCount: result.numberOfTransactItems });
859
858
  return result;
860
859
  }
861
860
  }
@@ -925,8 +924,7 @@ class DynamoDBService {
925
924
  ExpressionAttributeNames[attributeNameVariable] = attributeName;
926
925
  });
927
926
  }
928
- this.logger.debug('Projection Expression: %s', ProjectionExpression);
929
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
927
+ this.logger.debug('DynamoDB request started', { operation: 'getOne', tableKey });
930
928
  const getCommand = new lib_dynamodb_1.GetCommand({
931
929
  TableName: table.name,
932
930
  Key: keys,
@@ -941,22 +939,14 @@ class DynamoDBService {
941
939
  .send(getCommand)
942
940
  .catch((e) => {
943
941
  if (e.name === 'ValidationException') {
944
- this.logger.error('The request parameters are invalid with error: %s', e.message, {
945
- tableKey,
946
- primaryKey,
947
- opts,
948
- });
942
+ this.logger.error('The request parameters are invalid', { errorName: e.name, tableKey });
949
943
  throw new DynamoDBError(400, {
950
944
  message: 'Invalid request parameters',
951
945
  cause: e,
952
946
  });
953
947
  }
954
948
  else {
955
- this.logger.error('Failed to get item with error: %s', e.message, {
956
- tableKey,
957
- primaryKey,
958
- opts,
959
- });
949
+ this.logger.error('Failed to get item', { errorName: e.name, tableKey });
960
950
  throw new DynamoDBError(500, {
961
951
  message: 'Failed to get item',
962
952
  cause: e,
@@ -967,6 +957,7 @@ class DynamoDBService {
967
957
  if (item && table.timeToLiveAttribute && item[table.timeToLiveAttribute] !== undefined) {
968
958
  const now = getUnixTime(new Date());
969
959
  if (item[table.timeToLiveAttribute] <= now) {
960
+ this.logger.debug('DynamoDB getOne completed', { tableKey, outcome: 'expired' });
970
961
  return null;
971
962
  }
972
963
  // Delete the time to live attribute from the item if not requested
@@ -974,6 +965,7 @@ class DynamoDBService {
974
965
  delete item[table.timeToLiveAttribute];
975
966
  }
976
967
  }
968
+ this.logger.debug('DynamoDB getOne completed', { tableKey, outcome: item ? 'found' : 'not-found' });
977
969
  // Item will be undefined if not found
978
970
  // Convert keys back to camelCase if camelOrSnake is 'camel' or to snake_case if camelOrSnake is 'snake'
979
971
  if (item) {
@@ -1007,7 +999,6 @@ class DynamoDBService {
1007
999
  const keys = primaryKeys.map((primaryKey, indx) => {
1008
1000
  const isArrayOfKeys = Array.isArray(primaryKey);
1009
1001
  const pkStr = serializeKey(primaryKey);
1010
- this.logger.debug('Primary Key: %s', pkStr);
1011
1002
  if (pkIndexMap.has(pkStr)) {
1012
1003
  throw new Error(`Duplicate primary key: ${pkStr}`);
1013
1004
  }
@@ -1056,13 +1047,12 @@ class DynamoDBService {
1056
1047
  ExpressionAttributeNames[attributeNameVariable] = attributeName;
1057
1048
  });
1058
1049
  }
1059
- this.logger.debug('Projection Expression: %s', ProjectionExpression);
1060
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
1061
1050
  const results = new Array(primaryKeys.length).fill(null);
1062
1051
  const now = getUnixTime(new Date());
1063
1052
  let unprocessedAttempt = 0;
1064
1053
  while (keys.length > 0) {
1065
1054
  const batch = keys.splice(0, 100);
1055
+ this.logger.debug('DynamoDB batch request started', { operation: 'getMany', tableKey, itemCount: batch.length });
1066
1056
  const batchGetCommand = new lib_dynamodb_1.BatchGetCommand({
1067
1057
  RequestItems: {
1068
1058
  [table.name]: {
@@ -1076,19 +1066,23 @@ class DynamoDBService {
1076
1066
  },
1077
1067
  ReturnConsumedCapacity: opts.returnConsumedCapacity ?? client_dynamodb_1.ReturnConsumedCapacity.NONE,
1078
1068
  });
1079
- const { Responses: responses, UnprocessedKeys: rawUnprocessedKeys } = await this.ddbDocClient.send(batchGetCommand);
1069
+ const { Responses: responses, UnprocessedKeys: rawUnprocessedKeys } = await this.ddbDocClient.send(batchGetCommand).catch((error) => {
1070
+ this.logger.error('Failed to get items', { tableKey, errorName: error.name });
1071
+ throw error;
1072
+ });
1080
1073
  const items = responses && responses[table.name] ? responses[table.name] : [];
1081
1074
  const unprocessedKeys = (rawUnprocessedKeys && rawUnprocessedKeys[table.name]?.Keys) || [];
1082
- this.logger.debug(`GetMany Responsed Items: %o`, items);
1083
- this.logger.debug('Now: %d', now);
1084
1075
  if (unprocessedKeys.length > 0) {
1085
1076
  if (unprocessedAttempt >= MAX_UNPROCESSED_ATTEMPTS) {
1077
+ this.logger.error('DynamoDB batch retry limit reached', { operation: 'getMany', tableKey, retryCount: unprocessedAttempt });
1086
1078
  throw new DynamoDBError(500, {
1087
1079
  message: 'Failed to retrieve items after multiple attempts',
1088
1080
  cause: new Error('Too many unprocessed items'),
1089
1081
  });
1090
1082
  }
1091
- await sleep(backoffDelayMs(unprocessedAttempt));
1083
+ const delayMs = backoffDelayMs(unprocessedAttempt);
1084
+ this.logger.warn('DynamoDB retrying unprocessed batch items', { operation: 'getMany', tableKey, itemCount: unprocessedKeys.length, attempt: unprocessedAttempt + 1, delayMs });
1085
+ await sleep(delayMs);
1092
1086
  unprocessedAttempt += 1;
1093
1087
  }
1094
1088
  else {
@@ -1111,7 +1105,6 @@ class DynamoDBService {
1111
1105
  const pkStr = serializeKey(table.sortKey
1112
1106
  ? [item[table.partitionKey], item[table.sortKey]]
1113
1107
  : item[table.partitionKey]);
1114
- this.logger.debug('Primary Key: %s', pkStr);
1115
1108
  // Delete the primary key values from the item if not requested
1116
1109
  if (optIncludePartitionKey === false) {
1117
1110
  delete item[table.partitionKey];
@@ -1138,6 +1131,7 @@ class DynamoDBService {
1138
1131
  }
1139
1132
  });
1140
1133
  }
1134
+ this.logger.debug('DynamoDB getMany completed', { tableKey, requestedCount: primaryKeys.length, returnedCount: results.filter(item => item !== null).length });
1141
1135
  return results;
1142
1136
  }
1143
1137
  async find(tableKey, keyCondition, filter = null, opts = {}, camelOrSnake = 'camel') {
@@ -1300,7 +1294,6 @@ class DynamoDBService {
1300
1294
  indexName: indexToScan.indexName,
1301
1295
  projectionType: indexToScan
1302
1296
  .projectionType,
1303
- projectionFields,
1304
1297
  });
1305
1298
  }
1306
1299
  break;
@@ -1330,12 +1323,11 @@ class DynamoDBService {
1330
1323
  ]).length > 0;
1331
1324
  }
1332
1325
  if (hasFieldNotInsideGSIProjection) {
1333
- this.logger.error('Some of the projection fields are not included in the GSI projection, which may cause additional read cost', {
1326
+ this.logger.error('Some of the projection fields are not included in the GSI projection, so the query cannot be executed', {
1334
1327
  tableKey,
1335
1328
  indexName: indexToScan.indexName,
1336
1329
  projectionType: indexToScan
1337
1330
  .projectionType,
1338
- projectionFields,
1339
1331
  });
1340
1332
  throw new Error('All projection fields must be included in the GSI projection');
1341
1333
  }
@@ -1392,11 +1384,6 @@ class DynamoDBService {
1392
1384
  }
1393
1385
  ExpressionAttributeValues[attributeValueVariable] = value;
1394
1386
  });
1395
- this.logger.debug('Projection Expression: %s', ProjectionExpression);
1396
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
1397
- this.logger.debug('Expression Attribute Values: %o', ExpressionAttributeValues);
1398
- this.logger.debug('Key Condition Expression: %s', KeyConditionExpression);
1399
- this.logger.debug('Filter Expression: %s', FilterExpression);
1400
1387
  const numOfNeededItems = opts.limit; // May be undefined then no limit
1401
1388
  const result = {
1402
1389
  items: [],
@@ -1458,32 +1445,24 @@ class DynamoDBService {
1458
1445
  : {}),
1459
1446
  };
1460
1447
  while (true) {
1448
+ this.logger.debug('DynamoDB query page requested', { tableKey, hasCursor: !!params.ExclusiveStartKey, hasFilter: !!params.FilterExpression });
1461
1449
  const { Items, LastEvaluatedKey } = await this.ddbDocClient
1462
1450
  .send(new lib_dynamodb_1.QueryCommand(params))
1463
1451
  .catch((e) => {
1464
1452
  if (e.name === 'ValidationException') {
1465
- this.logger.error('The request parameters are invalid with error: %s', e.message, {
1466
- tableKey,
1467
- keyCondition,
1468
- filter,
1469
- opts,
1470
- });
1453
+ this.logger.error('The request parameters are invalid', { errorName: e.name, tableKey });
1471
1454
  throw new DynamoDBError(400, {
1472
1455
  message: 'Invalid request parameters',
1473
1456
  cause: e,
1474
1457
  });
1475
1458
  }
1476
- this.logger.error('Failed to query items with error: %s', e.message, {
1477
- tableKey,
1478
- keyCondition,
1479
- filter,
1480
- opts,
1481
- });
1459
+ this.logger.error('Failed to query items', { errorName: e.name, tableKey });
1482
1460
  throw new DynamoDBError(500, {
1483
1461
  message: 'Failed to query items',
1484
1462
  cause: e,
1485
1463
  });
1486
1464
  });
1465
+ this.logger.debug('DynamoDB query page received', { tableKey, itemCount: Items?.length ?? 0, hasMore: !!LastEvaluatedKey });
1487
1466
  if (LastEvaluatedKey) {
1488
1467
  params.ExclusiveStartKey = LastEvaluatedKey;
1489
1468
  }
@@ -1628,6 +1607,7 @@ class DynamoDBService {
1628
1607
  ExpressionAttributeNames[key] = value;
1629
1608
  });
1630
1609
  }
1610
+ this.logger.debug('DynamoDB request started', { operation: 'createOne', tableKey });
1631
1611
  const putCommand = new lib_dynamodb_1.PutCommand({
1632
1612
  TableName: table.name,
1633
1613
  Item: normalizedItem,
@@ -1680,22 +1660,18 @@ class DynamoDBService {
1680
1660
  const error = e;
1681
1661
  if (error.name === 'ConditionalCheckFailedException' &&
1682
1662
  opts.onConflict === 'ignore') {
1683
- this.logger.debug('Item %s already exists', normalizedItem[table.partitionKey] +
1684
- (table.sortKey ? `:${normalizedItem[table.sortKey]}` : ''));
1663
+ this.logger.debug('DynamoDB create skipped: item already exists', { tableKey });
1685
1664
  result.created = false;
1686
1665
  }
1687
1666
  else {
1688
- this.logger.error('Failed to create item with error: %s', error.message, {
1689
- tableKey,
1690
- item: normalizedItem,
1691
- opts,
1692
- });
1667
+ this.logger.error('Failed to create item', { errorName: error.name, tableKey });
1693
1668
  throw new DynamoDBError(500, {
1694
1669
  message: 'Failed to create item',
1695
1670
  cause: error,
1696
1671
  });
1697
1672
  }
1698
1673
  }
1674
+ this.logger.debug('DynamoDB createOne completed', { tableKey, created: result.created });
1699
1675
  return result;
1700
1676
  }
1701
1677
  async createMany(tableKey, primaryKeys, items, opts = {}) {
@@ -1740,6 +1716,7 @@ class DynamoDBService {
1740
1716
  let unprocessedAttempt = 0;
1741
1717
  while (normalizedItems.length > 0) {
1742
1718
  const batch = normalizedItems.splice(0, 25);
1719
+ this.logger.debug('DynamoDB batch request started', { operation: 'createMany', tableKey, itemCount: batch.length });
1743
1720
  const batchWriteCommand = new lib_dynamodb_1.BatchWriteCommand({
1744
1721
  RequestItems: {
1745
1722
  [table.name]: batch.map(item => ({
@@ -1753,11 +1730,7 @@ class DynamoDBService {
1753
1730
  const { UnprocessedItems } = await this.ddbDocClient
1754
1731
  .send(batchWriteCommand)
1755
1732
  .catch((e) => {
1756
- this.logger.error('Failed to create items with error: %s', e.message, {
1757
- tableKey,
1758
- items: batch,
1759
- opts,
1760
- });
1733
+ this.logger.error('Failed to create items', { errorName: e.name, tableKey });
1761
1734
  throw new DynamoDBError(500, {
1762
1735
  message: 'Failed to create items',
1763
1736
  cause: e,
@@ -1766,12 +1739,15 @@ class DynamoDBService {
1766
1739
  const unprocessedItems = UnprocessedItems?.[table.name];
1767
1740
  if (unprocessedItems !== undefined && unprocessedItems.length > 0) {
1768
1741
  if (unprocessedAttempt >= MAX_UNPROCESSED_ATTEMPTS) {
1742
+ this.logger.error('DynamoDB batch retry limit reached', { operation: 'createMany', tableKey, retryCount: unprocessedAttempt });
1769
1743
  throw new DynamoDBError(500, {
1770
1744
  message: 'Failed to create items after multiple attempts',
1771
1745
  cause: new Error('Too many unprocessed items'),
1772
1746
  });
1773
1747
  }
1774
- await sleep(backoffDelayMs(unprocessedAttempt));
1748
+ const delayMs = backoffDelayMs(unprocessedAttempt);
1749
+ this.logger.warn('DynamoDB retrying unprocessed batch items', { operation: 'createMany', tableKey, itemCount: unprocessedItems.length, attempt: unprocessedAttempt + 1, delayMs });
1750
+ await sleep(delayMs);
1775
1751
  unprocessedAttempt += 1;
1776
1752
  normalizedItems.push(...unprocessedItems
1777
1753
  .map(item => item.PutRequest?.Item)
@@ -1781,6 +1757,7 @@ class DynamoDBService {
1781
1757
  unprocessedAttempt = 0;
1782
1758
  }
1783
1759
  }
1760
+ this.logger.debug('DynamoDB createMany completed', { tableKey, itemCount: result.numberOfItems });
1784
1761
  return result;
1785
1762
  }
1786
1763
  async update(tableKey, primaryKey, commands, condition = null, opts = {}, camelOrSnake = 'camel') {
@@ -1856,10 +1833,7 @@ class DynamoDBService {
1856
1833
  }
1857
1834
  ExpressionAttributeValues[attributeValueVariable] = value;
1858
1835
  });
1859
- this.logger.debug('Update Expression: %s', UpdateExpression);
1860
- this.logger.debug('Condition Expression: %s', ConditionExpression);
1861
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
1862
- this.logger.debug('Expression Attribute Values: %o', ExpressionAttributeValues);
1836
+ this.logger.debug('DynamoDB request started', { operation: 'update', tableKey });
1863
1837
  const updateCommand = new lib_dynamodb_1.UpdateCommand({
1864
1838
  TableName: table.name,
1865
1839
  Key: keys,
@@ -1893,29 +1867,17 @@ class DynamoDBService {
1893
1867
  catch (e) {
1894
1868
  const error = e;
1895
1869
  if (error.name === 'ValidationException') {
1896
- this.logger.error('The request parameters are invalid with error: %s', error.message, {
1897
- tableKey,
1898
- primaryKey,
1899
- commands,
1900
- condition,
1901
- opts,
1902
- });
1870
+ this.logger.error('The request parameters are invalid', { errorName: error.name, tableKey });
1903
1871
  throw new DynamoDBError(400, {
1904
1872
  message: 'Invalid request parameters',
1905
1873
  cause: e,
1906
1874
  });
1907
1875
  }
1908
1876
  else if (error.name === 'ConditionalCheckFailedException') {
1909
- this.logger.debug('Condition failed to update item %s', primaryKey.toString());
1877
+ this.logger.debug('DynamoDB update skipped: condition not met', { tableKey });
1910
1878
  }
1911
1879
  else {
1912
- this.logger.error('Failed to update item with error: %s', error.message, {
1913
- tableKey,
1914
- primaryKey,
1915
- commands,
1916
- condition,
1917
- opts,
1918
- });
1880
+ this.logger.error('Failed to update item', { errorName: error.name, tableKey });
1919
1881
  throw new DynamoDBError(500, {
1920
1882
  message: 'Failed to update item',
1921
1883
  cause: e,
@@ -1941,6 +1903,7 @@ class DynamoDBService {
1941
1903
  result.item = newItem;
1942
1904
  }
1943
1905
  }
1906
+ this.logger.debug('DynamoDB update completed', { tableKey, updatedOrCreated: result.updatedOrCreated });
1944
1907
  return result;
1945
1908
  }
1946
1909
  async deleteOne(tableKey, primaryKey, condition = null, opts = {}, camelOrSnake = 'camel') {
@@ -1976,6 +1939,7 @@ class DynamoDBService {
1976
1939
  }
1977
1940
  ConditionExpression = condition.expression;
1978
1941
  }
1942
+ this.logger.debug('DynamoDB request started', { operation: 'deleteOne', tableKey });
1979
1943
  const deleteCommand = new lib_dynamodb_1.DeleteCommand({
1980
1944
  TableName: table.name,
1981
1945
  Key: keys,
@@ -2003,27 +1967,17 @@ class DynamoDBService {
2003
1967
  catch (e) {
2004
1968
  const error = e;
2005
1969
  if (error.name === 'ValidationException') {
2006
- this.logger.error('The request parameters are invalid with error: %s', error.message, {
2007
- tableKey,
2008
- primaryKey,
2009
- condition,
2010
- opts,
2011
- });
1970
+ this.logger.error('The request parameters are invalid', { errorName: error.name, tableKey });
2012
1971
  throw new DynamoDBError(400, {
2013
1972
  message: 'Invalid request parameters',
2014
1973
  cause: e,
2015
1974
  });
2016
1975
  }
2017
1976
  else if (error.name === 'ConditionalCheckFailedException') {
2018
- this.logger.debug('Condition failed to delete item %s', primaryKey.toString());
1977
+ this.logger.debug('DynamoDB delete skipped: condition not met', { tableKey });
2019
1978
  }
2020
1979
  else {
2021
- this.logger.error('Failed to delete item with error: %s', error.message, {
2022
- tableKey,
2023
- primaryKey,
2024
- condition,
2025
- opts,
2026
- });
1980
+ this.logger.error('Failed to delete item', { errorName: error.name, tableKey });
2027
1981
  throw new DynamoDBError(500, {
2028
1982
  message: 'Failed to delete item',
2029
1983
  cause: e,
@@ -2049,6 +2003,7 @@ class DynamoDBService {
2049
2003
  result.item = newItem;
2050
2004
  }
2051
2005
  }
2006
+ this.logger.debug('DynamoDB deleteOne completed', { tableKey, deleted: result.deleted });
2052
2007
  return result;
2053
2008
  }
2054
2009
  async deleteMany(tableKey, primaryKeys, opts = {}) {
@@ -2073,6 +2028,7 @@ class DynamoDBService {
2073
2028
  let unprocessedAttempt = 0;
2074
2029
  while (keys.length > 0) {
2075
2030
  const batch = keys.splice(0, 25);
2031
+ this.logger.debug('DynamoDB batch request started', { operation: 'deleteMany', tableKey, itemCount: batch.length });
2076
2032
  const batchWriteCommand = new lib_dynamodb_1.BatchWriteCommand({
2077
2033
  RequestItems: {
2078
2034
  [table.name]: batch.map(key => ({
@@ -2086,11 +2042,7 @@ class DynamoDBService {
2086
2042
  const { UnprocessedItems } = await this.ddbDocClient
2087
2043
  .send(batchWriteCommand)
2088
2044
  .catch((e) => {
2089
- this.logger.error('Failed to delete items with error: %s', e.message, {
2090
- tableKey,
2091
- keys: batch,
2092
- opts,
2093
- });
2045
+ this.logger.error('Failed to delete items', { errorName: e.name, tableKey });
2094
2046
  throw new DynamoDBError(500, {
2095
2047
  message: 'Failed to delete items',
2096
2048
  cause: e,
@@ -2099,12 +2051,15 @@ class DynamoDBService {
2099
2051
  const unprocessedItems = UnprocessedItems?.[table.name];
2100
2052
  if (unprocessedItems !== undefined && unprocessedItems.length > 0) {
2101
2053
  if (unprocessedAttempt >= MAX_UNPROCESSED_ATTEMPTS) {
2054
+ this.logger.error('DynamoDB batch retry limit reached', { operation: 'deleteMany', tableKey, retryCount: unprocessedAttempt });
2102
2055
  throw new DynamoDBError(500, {
2103
2056
  message: 'Failed to delete items after multiple attempts',
2104
2057
  cause: new Error('Too many unprocessed items'),
2105
2058
  });
2106
2059
  }
2107
- await sleep(backoffDelayMs(unprocessedAttempt));
2060
+ const delayMs = backoffDelayMs(unprocessedAttempt);
2061
+ this.logger.warn('DynamoDB retrying unprocessed batch items', { operation: 'deleteMany', tableKey, itemCount: unprocessedItems.length, attempt: unprocessedAttempt + 1, delayMs });
2062
+ await sleep(delayMs);
2108
2063
  unprocessedAttempt += 1;
2109
2064
  keys.push(...unprocessedItems
2110
2065
  .map(item => item.DeleteRequest?.Key)
@@ -2114,6 +2069,7 @@ class DynamoDBService {
2114
2069
  unprocessedAttempt = 0;
2115
2070
  }
2116
2071
  }
2072
+ this.logger.debug('DynamoDB deleteMany completed', { tableKey, itemCount: result.numberOfItems });
2117
2073
  return result;
2118
2074
  }
2119
2075
  transaction(opts) {