@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/dist/esm/index.js CHANGED
@@ -388,6 +388,7 @@ export class DynamoDBTransaction {
388
388
  if (i === retries - 1) {
389
389
  throw error;
390
390
  }
391
+ this.logger.warn('DynamoDB retrying transaction conflict', { mode: this.mode, attempt: i + 1, maxAttempts: retries, delayMs: delay * (i + 1) });
391
392
  await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
392
393
  }
393
394
  else {
@@ -636,10 +637,6 @@ export class DynamoDBTransaction {
636
637
  }
637
638
  ExpressionAttributeValues[attributeValueVariable] = value;
638
639
  });
639
- this.logger.debug('Update Expression: %s', UpdateExpression);
640
- this.logger.debug('Condition Expression: %s', ConditionExpression);
641
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
642
- this.logger.debug('Expression Attribute Values: %o', ExpressionAttributeValues);
643
640
  this.transactWriteItems.push({
644
641
  Update: {
645
642
  TableName: table.name,
@@ -715,6 +712,7 @@ export class DynamoDBTransaction {
715
712
  const result = {
716
713
  numberOfTransactItems: 0,
717
714
  };
715
+ this.logger.debug('DynamoDB transaction started', { mode: this.mode, itemCount: this.mode === DynamoDBTransactionMode.READ ? this.transactGetItems.length : this.transactWriteItems.length });
718
716
  switch (this.mode) {
719
717
  case DynamoDBTransactionMode.READ:
720
718
  command = new TransactGetCommand({
@@ -726,21 +724,21 @@ export class DynamoDBTransaction {
726
724
  const { Responses } = (await this.txConflictRetryWrapper(command, this.txRetryLimit, this.txRetryDelay).catch((e) => {
727
725
  this._cleanup();
728
726
  if (isTransactionConflictError(e)) {
729
- this.logger.error('Transaction conflict due to TransactionConflict');
727
+ this.logger.error('DynamoDB transaction conflict retry limit reached', { errorName: e.name });
730
728
  throw new DynamoDBError(409, {
731
729
  message: e.message,
732
730
  cause: e,
733
731
  });
734
732
  }
735
733
  else if (e.name === 'TransactionCanceledException') {
736
- this.logger.error(e.message);
734
+ this.logger.error('DynamoDB transaction canceled', { errorName: e.name });
737
735
  throw new DynamoDBError(400, {
738
736
  message: e.message,
739
737
  cause: e,
740
738
  });
741
739
  }
742
740
  else {
743
- this.logger.error('Transaction failed due to %s', e.message);
741
+ this.logger.error('Transaction failed', { errorName: e.name });
744
742
  throw new DynamoDBError(500, {
745
743
  message: e.message,
746
744
  cause: e,
@@ -789,28 +787,28 @@ export class DynamoDBTransaction {
789
787
  await this.txConflictRetryWrapper(command, this.txRetryLimit, this.txRetryDelay).catch((e) => {
790
788
  this._cleanup();
791
789
  if (isTransactionConflictError(e)) {
792
- this.logger.error('Transaction conflict due to TransactionConflict');
790
+ this.logger.error('DynamoDB transaction conflict retry limit reached', { errorName: e.name });
793
791
  throw new DynamoDBError(409, {
794
792
  message: e.message,
795
793
  cause: e,
796
794
  });
797
795
  }
798
796
  else if (e.name === 'TransactionCanceledException') {
799
- this.logger.error(e.message);
797
+ this.logger.error('DynamoDB transaction canceled', { errorName: e.name });
800
798
  throw new DynamoDBError(400, {
801
799
  message: e.message,
802
800
  cause: e,
803
801
  });
804
802
  }
805
803
  else if (e.name === 'ValidationException') {
806
- this.logger.error('The request parameters are invalid with error: %s', e.message);
804
+ this.logger.error('The request parameters are invalid', { errorName: e.name });
807
805
  throw new DynamoDBError(400, {
808
806
  message: 'Invalid request parameters',
809
807
  cause: e,
810
808
  });
811
809
  }
812
810
  else {
813
- this.logger.error('Transaction failed due to %s', e.message);
811
+ this.logger.error('Transaction failed', { errorName: e.name });
814
812
  throw new DynamoDBError(500, {
815
813
  message: e.message,
816
814
  cause: e,
@@ -823,6 +821,7 @@ export class DynamoDBTransaction {
823
821
  default:
824
822
  throw new Error('Invalid transaction mode');
825
823
  }
824
+ this.logger.debug('DynamoDB transaction completed', { mode: this.mode, itemCount: result.numberOfTransactItems });
826
825
  return result;
827
826
  }
828
827
  }
@@ -891,8 +890,7 @@ export default class DynamoDBService {
891
890
  ExpressionAttributeNames[attributeNameVariable] = attributeName;
892
891
  });
893
892
  }
894
- this.logger.debug('Projection Expression: %s', ProjectionExpression);
895
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
893
+ this.logger.debug('DynamoDB request started', { operation: 'getOne', tableKey });
896
894
  const getCommand = new GetCommand({
897
895
  TableName: table.name,
898
896
  Key: keys,
@@ -907,22 +905,14 @@ export default class DynamoDBService {
907
905
  .send(getCommand)
908
906
  .catch((e) => {
909
907
  if (e.name === 'ValidationException') {
910
- this.logger.error('The request parameters are invalid with error: %s', e.message, {
911
- tableKey,
912
- primaryKey,
913
- opts,
914
- });
908
+ this.logger.error('The request parameters are invalid', { errorName: e.name, tableKey });
915
909
  throw new DynamoDBError(400, {
916
910
  message: 'Invalid request parameters',
917
911
  cause: e,
918
912
  });
919
913
  }
920
914
  else {
921
- this.logger.error('Failed to get item with error: %s', e.message, {
922
- tableKey,
923
- primaryKey,
924
- opts,
925
- });
915
+ this.logger.error('Failed to get item', { errorName: e.name, tableKey });
926
916
  throw new DynamoDBError(500, {
927
917
  message: 'Failed to get item',
928
918
  cause: e,
@@ -933,6 +923,7 @@ export default class DynamoDBService {
933
923
  if (item && table.timeToLiveAttribute && item[table.timeToLiveAttribute] !== undefined) {
934
924
  const now = getUnixTime(new Date());
935
925
  if (item[table.timeToLiveAttribute] <= now) {
926
+ this.logger.debug('DynamoDB getOne completed', { tableKey, outcome: 'expired' });
936
927
  return null;
937
928
  }
938
929
  // Delete the time to live attribute from the item if not requested
@@ -940,6 +931,7 @@ export default class DynamoDBService {
940
931
  delete item[table.timeToLiveAttribute];
941
932
  }
942
933
  }
934
+ this.logger.debug('DynamoDB getOne completed', { tableKey, outcome: item ? 'found' : 'not-found' });
943
935
  // Item will be undefined if not found
944
936
  // Convert keys back to camelCase if camelOrSnake is 'camel' or to snake_case if camelOrSnake is 'snake'
945
937
  if (item) {
@@ -973,7 +965,6 @@ export default class DynamoDBService {
973
965
  const keys = primaryKeys.map((primaryKey, indx) => {
974
966
  const isArrayOfKeys = Array.isArray(primaryKey);
975
967
  const pkStr = serializeKey(primaryKey);
976
- this.logger.debug('Primary Key: %s', pkStr);
977
968
  if (pkIndexMap.has(pkStr)) {
978
969
  throw new Error(`Duplicate primary key: ${pkStr}`);
979
970
  }
@@ -1022,13 +1013,12 @@ export default class DynamoDBService {
1022
1013
  ExpressionAttributeNames[attributeNameVariable] = attributeName;
1023
1014
  });
1024
1015
  }
1025
- this.logger.debug('Projection Expression: %s', ProjectionExpression);
1026
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
1027
1016
  const results = new Array(primaryKeys.length).fill(null);
1028
1017
  const now = getUnixTime(new Date());
1029
1018
  let unprocessedAttempt = 0;
1030
1019
  while (keys.length > 0) {
1031
1020
  const batch = keys.splice(0, 100);
1021
+ this.logger.debug('DynamoDB batch request started', { operation: 'getMany', tableKey, itemCount: batch.length });
1032
1022
  const batchGetCommand = new BatchGetCommand({
1033
1023
  RequestItems: {
1034
1024
  [table.name]: {
@@ -1042,19 +1032,23 @@ export default class DynamoDBService {
1042
1032
  },
1043
1033
  ReturnConsumedCapacity: opts.returnConsumedCapacity ?? ReturnConsumedCapacity.NONE,
1044
1034
  });
1045
- const { Responses: responses, UnprocessedKeys: rawUnprocessedKeys } = await this.ddbDocClient.send(batchGetCommand);
1035
+ const { Responses: responses, UnprocessedKeys: rawUnprocessedKeys } = await this.ddbDocClient.send(batchGetCommand).catch((error) => {
1036
+ this.logger.error('Failed to get items', { tableKey, errorName: error.name });
1037
+ throw error;
1038
+ });
1046
1039
  const items = responses && responses[table.name] ? responses[table.name] : [];
1047
1040
  const unprocessedKeys = (rawUnprocessedKeys && rawUnprocessedKeys[table.name]?.Keys) || [];
1048
- this.logger.debug(`GetMany Responsed Items: %o`, items);
1049
- this.logger.debug('Now: %d', now);
1050
1041
  if (unprocessedKeys.length > 0) {
1051
1042
  if (unprocessedAttempt >= MAX_UNPROCESSED_ATTEMPTS) {
1043
+ this.logger.error('DynamoDB batch retry limit reached', { operation: 'getMany', tableKey, retryCount: unprocessedAttempt });
1052
1044
  throw new DynamoDBError(500, {
1053
1045
  message: 'Failed to retrieve items after multiple attempts',
1054
1046
  cause: new Error('Too many unprocessed items'),
1055
1047
  });
1056
1048
  }
1057
- await sleep(backoffDelayMs(unprocessedAttempt));
1049
+ const delayMs = backoffDelayMs(unprocessedAttempt);
1050
+ this.logger.warn('DynamoDB retrying unprocessed batch items', { operation: 'getMany', tableKey, itemCount: unprocessedKeys.length, attempt: unprocessedAttempt + 1, delayMs });
1051
+ await sleep(delayMs);
1058
1052
  unprocessedAttempt += 1;
1059
1053
  }
1060
1054
  else {
@@ -1077,7 +1071,6 @@ export default class DynamoDBService {
1077
1071
  const pkStr = serializeKey(table.sortKey
1078
1072
  ? [item[table.partitionKey], item[table.sortKey]]
1079
1073
  : item[table.partitionKey]);
1080
- this.logger.debug('Primary Key: %s', pkStr);
1081
1074
  // Delete the primary key values from the item if not requested
1082
1075
  if (optIncludePartitionKey === false) {
1083
1076
  delete item[table.partitionKey];
@@ -1104,6 +1097,7 @@ export default class DynamoDBService {
1104
1097
  }
1105
1098
  });
1106
1099
  }
1100
+ this.logger.debug('DynamoDB getMany completed', { tableKey, requestedCount: primaryKeys.length, returnedCount: results.filter(item => item !== null).length });
1107
1101
  return results;
1108
1102
  }
1109
1103
  async find(tableKey, keyCondition, filter = null, opts = {}, camelOrSnake = 'camel') {
@@ -1266,7 +1260,6 @@ export default class DynamoDBService {
1266
1260
  indexName: indexToScan.indexName,
1267
1261
  projectionType: indexToScan
1268
1262
  .projectionType,
1269
- projectionFields,
1270
1263
  });
1271
1264
  }
1272
1265
  break;
@@ -1296,12 +1289,11 @@ export default class DynamoDBService {
1296
1289
  ]).length > 0;
1297
1290
  }
1298
1291
  if (hasFieldNotInsideGSIProjection) {
1299
- this.logger.error('Some of the projection fields are not included in the GSI projection, which may cause additional read cost', {
1292
+ this.logger.error('Some of the projection fields are not included in the GSI projection, so the query cannot be executed', {
1300
1293
  tableKey,
1301
1294
  indexName: indexToScan.indexName,
1302
1295
  projectionType: indexToScan
1303
1296
  .projectionType,
1304
- projectionFields,
1305
1297
  });
1306
1298
  throw new Error('All projection fields must be included in the GSI projection');
1307
1299
  }
@@ -1358,11 +1350,6 @@ export default class DynamoDBService {
1358
1350
  }
1359
1351
  ExpressionAttributeValues[attributeValueVariable] = value;
1360
1352
  });
1361
- this.logger.debug('Projection Expression: %s', ProjectionExpression);
1362
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
1363
- this.logger.debug('Expression Attribute Values: %o', ExpressionAttributeValues);
1364
- this.logger.debug('Key Condition Expression: %s', KeyConditionExpression);
1365
- this.logger.debug('Filter Expression: %s', FilterExpression);
1366
1353
  const numOfNeededItems = opts.limit; // May be undefined then no limit
1367
1354
  const result = {
1368
1355
  items: [],
@@ -1424,32 +1411,24 @@ export default class DynamoDBService {
1424
1411
  : {}),
1425
1412
  };
1426
1413
  while (true) {
1414
+ this.logger.debug('DynamoDB query page requested', { tableKey, hasCursor: !!params.ExclusiveStartKey, hasFilter: !!params.FilterExpression });
1427
1415
  const { Items, LastEvaluatedKey } = await this.ddbDocClient
1428
1416
  .send(new QueryCommand(params))
1429
1417
  .catch((e) => {
1430
1418
  if (e.name === 'ValidationException') {
1431
- this.logger.error('The request parameters are invalid with error: %s', e.message, {
1432
- tableKey,
1433
- keyCondition,
1434
- filter,
1435
- opts,
1436
- });
1419
+ this.logger.error('The request parameters are invalid', { errorName: e.name, tableKey });
1437
1420
  throw new DynamoDBError(400, {
1438
1421
  message: 'Invalid request parameters',
1439
1422
  cause: e,
1440
1423
  });
1441
1424
  }
1442
- this.logger.error('Failed to query items with error: %s', e.message, {
1443
- tableKey,
1444
- keyCondition,
1445
- filter,
1446
- opts,
1447
- });
1425
+ this.logger.error('Failed to query items', { errorName: e.name, tableKey });
1448
1426
  throw new DynamoDBError(500, {
1449
1427
  message: 'Failed to query items',
1450
1428
  cause: e,
1451
1429
  });
1452
1430
  });
1431
+ this.logger.debug('DynamoDB query page received', { tableKey, itemCount: Items?.length ?? 0, hasMore: !!LastEvaluatedKey });
1453
1432
  if (LastEvaluatedKey) {
1454
1433
  params.ExclusiveStartKey = LastEvaluatedKey;
1455
1434
  }
@@ -1594,6 +1573,7 @@ export default class DynamoDBService {
1594
1573
  ExpressionAttributeNames[key] = value;
1595
1574
  });
1596
1575
  }
1576
+ this.logger.debug('DynamoDB request started', { operation: 'createOne', tableKey });
1597
1577
  const putCommand = new PutCommand({
1598
1578
  TableName: table.name,
1599
1579
  Item: normalizedItem,
@@ -1646,22 +1626,18 @@ export default class DynamoDBService {
1646
1626
  const error = e;
1647
1627
  if (error.name === 'ConditionalCheckFailedException' &&
1648
1628
  opts.onConflict === 'ignore') {
1649
- this.logger.debug('Item %s already exists', normalizedItem[table.partitionKey] +
1650
- (table.sortKey ? `:${normalizedItem[table.sortKey]}` : ''));
1629
+ this.logger.debug('DynamoDB create skipped: item already exists', { tableKey });
1651
1630
  result.created = false;
1652
1631
  }
1653
1632
  else {
1654
- this.logger.error('Failed to create item with error: %s', error.message, {
1655
- tableKey,
1656
- item: normalizedItem,
1657
- opts,
1658
- });
1633
+ this.logger.error('Failed to create item', { errorName: error.name, tableKey });
1659
1634
  throw new DynamoDBError(500, {
1660
1635
  message: 'Failed to create item',
1661
1636
  cause: error,
1662
1637
  });
1663
1638
  }
1664
1639
  }
1640
+ this.logger.debug('DynamoDB createOne completed', { tableKey, created: result.created });
1665
1641
  return result;
1666
1642
  }
1667
1643
  async createMany(tableKey, primaryKeys, items, opts = {}) {
@@ -1706,6 +1682,7 @@ export default class DynamoDBService {
1706
1682
  let unprocessedAttempt = 0;
1707
1683
  while (normalizedItems.length > 0) {
1708
1684
  const batch = normalizedItems.splice(0, 25);
1685
+ this.logger.debug('DynamoDB batch request started', { operation: 'createMany', tableKey, itemCount: batch.length });
1709
1686
  const batchWriteCommand = new BatchWriteCommand({
1710
1687
  RequestItems: {
1711
1688
  [table.name]: batch.map(item => ({
@@ -1719,11 +1696,7 @@ export default class DynamoDBService {
1719
1696
  const { UnprocessedItems } = await this.ddbDocClient
1720
1697
  .send(batchWriteCommand)
1721
1698
  .catch((e) => {
1722
- this.logger.error('Failed to create items with error: %s', e.message, {
1723
- tableKey,
1724
- items: batch,
1725
- opts,
1726
- });
1699
+ this.logger.error('Failed to create items', { errorName: e.name, tableKey });
1727
1700
  throw new DynamoDBError(500, {
1728
1701
  message: 'Failed to create items',
1729
1702
  cause: e,
@@ -1732,12 +1705,15 @@ export default class DynamoDBService {
1732
1705
  const unprocessedItems = UnprocessedItems?.[table.name];
1733
1706
  if (unprocessedItems !== undefined && unprocessedItems.length > 0) {
1734
1707
  if (unprocessedAttempt >= MAX_UNPROCESSED_ATTEMPTS) {
1708
+ this.logger.error('DynamoDB batch retry limit reached', { operation: 'createMany', tableKey, retryCount: unprocessedAttempt });
1735
1709
  throw new DynamoDBError(500, {
1736
1710
  message: 'Failed to create items after multiple attempts',
1737
1711
  cause: new Error('Too many unprocessed items'),
1738
1712
  });
1739
1713
  }
1740
- await sleep(backoffDelayMs(unprocessedAttempt));
1714
+ const delayMs = backoffDelayMs(unprocessedAttempt);
1715
+ this.logger.warn('DynamoDB retrying unprocessed batch items', { operation: 'createMany', tableKey, itemCount: unprocessedItems.length, attempt: unprocessedAttempt + 1, delayMs });
1716
+ await sleep(delayMs);
1741
1717
  unprocessedAttempt += 1;
1742
1718
  normalizedItems.push(...unprocessedItems
1743
1719
  .map(item => item.PutRequest?.Item)
@@ -1747,6 +1723,7 @@ export default class DynamoDBService {
1747
1723
  unprocessedAttempt = 0;
1748
1724
  }
1749
1725
  }
1726
+ this.logger.debug('DynamoDB createMany completed', { tableKey, itemCount: result.numberOfItems });
1750
1727
  return result;
1751
1728
  }
1752
1729
  async update(tableKey, primaryKey, commands, condition = null, opts = {}, camelOrSnake = 'camel') {
@@ -1822,10 +1799,7 @@ export default class DynamoDBService {
1822
1799
  }
1823
1800
  ExpressionAttributeValues[attributeValueVariable] = value;
1824
1801
  });
1825
- this.logger.debug('Update Expression: %s', UpdateExpression);
1826
- this.logger.debug('Condition Expression: %s', ConditionExpression);
1827
- this.logger.debug('Expression Attribute Names: %o', ExpressionAttributeNames);
1828
- this.logger.debug('Expression Attribute Values: %o', ExpressionAttributeValues);
1802
+ this.logger.debug('DynamoDB request started', { operation: 'update', tableKey });
1829
1803
  const updateCommand = new UpdateCommand({
1830
1804
  TableName: table.name,
1831
1805
  Key: keys,
@@ -1859,29 +1833,17 @@ export default class DynamoDBService {
1859
1833
  catch (e) {
1860
1834
  const error = e;
1861
1835
  if (error.name === 'ValidationException') {
1862
- this.logger.error('The request parameters are invalid with error: %s', error.message, {
1863
- tableKey,
1864
- primaryKey,
1865
- commands,
1866
- condition,
1867
- opts,
1868
- });
1836
+ this.logger.error('The request parameters are invalid', { errorName: error.name, tableKey });
1869
1837
  throw new DynamoDBError(400, {
1870
1838
  message: 'Invalid request parameters',
1871
1839
  cause: e,
1872
1840
  });
1873
1841
  }
1874
1842
  else if (error.name === 'ConditionalCheckFailedException') {
1875
- this.logger.debug('Condition failed to update item %s', primaryKey.toString());
1843
+ this.logger.debug('DynamoDB update skipped: condition not met', { tableKey });
1876
1844
  }
1877
1845
  else {
1878
- this.logger.error('Failed to update item with error: %s', error.message, {
1879
- tableKey,
1880
- primaryKey,
1881
- commands,
1882
- condition,
1883
- opts,
1884
- });
1846
+ this.logger.error('Failed to update item', { errorName: error.name, tableKey });
1885
1847
  throw new DynamoDBError(500, {
1886
1848
  message: 'Failed to update item',
1887
1849
  cause: e,
@@ -1907,6 +1869,7 @@ export default class DynamoDBService {
1907
1869
  result.item = newItem;
1908
1870
  }
1909
1871
  }
1872
+ this.logger.debug('DynamoDB update completed', { tableKey, updatedOrCreated: result.updatedOrCreated });
1910
1873
  return result;
1911
1874
  }
1912
1875
  async deleteOne(tableKey, primaryKey, condition = null, opts = {}, camelOrSnake = 'camel') {
@@ -1942,6 +1905,7 @@ export default class DynamoDBService {
1942
1905
  }
1943
1906
  ConditionExpression = condition.expression;
1944
1907
  }
1908
+ this.logger.debug('DynamoDB request started', { operation: 'deleteOne', tableKey });
1945
1909
  const deleteCommand = new DeleteCommand({
1946
1910
  TableName: table.name,
1947
1911
  Key: keys,
@@ -1969,27 +1933,17 @@ export default class DynamoDBService {
1969
1933
  catch (e) {
1970
1934
  const error = e;
1971
1935
  if (error.name === 'ValidationException') {
1972
- this.logger.error('The request parameters are invalid with error: %s', error.message, {
1973
- tableKey,
1974
- primaryKey,
1975
- condition,
1976
- opts,
1977
- });
1936
+ this.logger.error('The request parameters are invalid', { errorName: error.name, tableKey });
1978
1937
  throw new DynamoDBError(400, {
1979
1938
  message: 'Invalid request parameters',
1980
1939
  cause: e,
1981
1940
  });
1982
1941
  }
1983
1942
  else if (error.name === 'ConditionalCheckFailedException') {
1984
- this.logger.debug('Condition failed to delete item %s', primaryKey.toString());
1943
+ this.logger.debug('DynamoDB delete skipped: condition not met', { tableKey });
1985
1944
  }
1986
1945
  else {
1987
- this.logger.error('Failed to delete item with error: %s', error.message, {
1988
- tableKey,
1989
- primaryKey,
1990
- condition,
1991
- opts,
1992
- });
1946
+ this.logger.error('Failed to delete item', { errorName: error.name, tableKey });
1993
1947
  throw new DynamoDBError(500, {
1994
1948
  message: 'Failed to delete item',
1995
1949
  cause: e,
@@ -2015,6 +1969,7 @@ export default class DynamoDBService {
2015
1969
  result.item = newItem;
2016
1970
  }
2017
1971
  }
1972
+ this.logger.debug('DynamoDB deleteOne completed', { tableKey, deleted: result.deleted });
2018
1973
  return result;
2019
1974
  }
2020
1975
  async deleteMany(tableKey, primaryKeys, opts = {}) {
@@ -2039,6 +1994,7 @@ export default class DynamoDBService {
2039
1994
  let unprocessedAttempt = 0;
2040
1995
  while (keys.length > 0) {
2041
1996
  const batch = keys.splice(0, 25);
1997
+ this.logger.debug('DynamoDB batch request started', { operation: 'deleteMany', tableKey, itemCount: batch.length });
2042
1998
  const batchWriteCommand = new BatchWriteCommand({
2043
1999
  RequestItems: {
2044
2000
  [table.name]: batch.map(key => ({
@@ -2052,11 +2008,7 @@ export default class DynamoDBService {
2052
2008
  const { UnprocessedItems } = await this.ddbDocClient
2053
2009
  .send(batchWriteCommand)
2054
2010
  .catch((e) => {
2055
- this.logger.error('Failed to delete items with error: %s', e.message, {
2056
- tableKey,
2057
- keys: batch,
2058
- opts,
2059
- });
2011
+ this.logger.error('Failed to delete items', { errorName: e.name, tableKey });
2060
2012
  throw new DynamoDBError(500, {
2061
2013
  message: 'Failed to delete items',
2062
2014
  cause: e,
@@ -2065,12 +2017,15 @@ export default class DynamoDBService {
2065
2017
  const unprocessedItems = UnprocessedItems?.[table.name];
2066
2018
  if (unprocessedItems !== undefined && unprocessedItems.length > 0) {
2067
2019
  if (unprocessedAttempt >= MAX_UNPROCESSED_ATTEMPTS) {
2020
+ this.logger.error('DynamoDB batch retry limit reached', { operation: 'deleteMany', tableKey, retryCount: unprocessedAttempt });
2068
2021
  throw new DynamoDBError(500, {
2069
2022
  message: 'Failed to delete items after multiple attempts',
2070
2023
  cause: new Error('Too many unprocessed items'),
2071
2024
  });
2072
2025
  }
2073
- await sleep(backoffDelayMs(unprocessedAttempt));
2026
+ const delayMs = backoffDelayMs(unprocessedAttempt);
2027
+ this.logger.warn('DynamoDB retrying unprocessed batch items', { operation: 'deleteMany', tableKey, itemCount: unprocessedItems.length, attempt: unprocessedAttempt + 1, delayMs });
2028
+ await sleep(delayMs);
2074
2029
  unprocessedAttempt += 1;
2075
2030
  keys.push(...unprocessedItems
2076
2031
  .map(item => item.DeleteRequest?.Key)
@@ -2080,6 +2035,7 @@ export default class DynamoDBService {
2080
2035
  unprocessedAttempt = 0;
2081
2036
  }
2082
2037
  }
2038
+ this.logger.debug('DynamoDB deleteMany completed', { tableKey, itemCount: result.numberOfItems });
2083
2039
  return result;
2084
2040
  }
2085
2041
  transaction(opts) {