Skip to content

Commit 0d10134

Browse files
sh1n1chi8ackerjeskew
authored andcommitted
Add support on-demand capacity mode (awslabs#141)
* Add support on-demand capacity mode * Update packages/dynamodb-data-mapper/src/namedParameters/CreateTableOptions.ts Co-Authored-By: sh1n1chi8acker <[email protected]> * Address comments by @jeskew
1 parent 81611c0 commit 0d10134

File tree

4 files changed

+206
-17
lines changed

4 files changed

+206
-17
lines changed

packages/dynamodb-data-mapper/src/DataMapper.spec.ts

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,34 @@ describe('DataMapper', () => {
881881
]);
882882
});
883883

884+
it('should create new table with on-demand capacity mode', async () => {
885+
await mapper.createTable(Item, {
886+
billingMode: 'PAY_PER_REQUEST',
887+
});
888+
889+
expect(mockDynamoDbClient.createTable.mock.calls).toEqual([
890+
[
891+
{
892+
TableName: 'foo',
893+
AttributeDefinitions: [
894+
{
895+
AttributeName: 'id',
896+
AttributeType: 'S'
897+
}
898+
],
899+
KeySchema: [
900+
{
901+
AttributeName: 'id',
902+
KeyType: 'HASH',
903+
}
904+
],
905+
BillingMode: 'PAY_PER_REQUEST',
906+
StreamSpecification: { StreamEnabled: false },
907+
},
908+
]
909+
]);
910+
});
911+
884912
describe('index keys', () => {
885913
class IndexedItem {
886914
get [DynamoDbTable]() { return 'foo' }
@@ -1074,6 +1102,130 @@ describe('DataMapper', () => {
10741102
]);
10751103
});
10761104

1105+
it('should identify and report index keys with on-demand capacity mode', async () => {
1106+
await mapper.createTable(IndexedItem, {
1107+
billingMode: 'PAY_PER_REQUEST',
1108+
indexOptions: {
1109+
binaryIndex: {
1110+
type: 'global',
1111+
projection: ['createdBy', 'createdAt'],
1112+
},
1113+
chronological: {
1114+
type: 'global',
1115+
projection: 'all',
1116+
},
1117+
globalIndex: {
1118+
type: 'global',
1119+
projection: 'all',
1120+
},
1121+
localIndex: {
1122+
type: 'local',
1123+
projection: 'keys',
1124+
},
1125+
}
1126+
});
1127+
1128+
expect(mockDynamoDbClient.createTable.mock.calls).toEqual([
1129+
[
1130+
{
1131+
AttributeDefinitions: [
1132+
{
1133+
AttributeName: 'partitionKey',
1134+
AttributeType: 'N'
1135+
},
1136+
{
1137+
AttributeName: 'timestamp',
1138+
AttributeType: 'N'
1139+
},
1140+
{
1141+
AttributeName: 'creator',
1142+
AttributeType: 'S'
1143+
},
1144+
{
1145+
AttributeName: 'binaryKey',
1146+
AttributeType: 'B'
1147+
},
1148+
{
1149+
AttributeName: 'customKey',
1150+
AttributeType: 'S'
1151+
},
1152+
],
1153+
GlobalSecondaryIndexes: [
1154+
{
1155+
IndexName: 'chronological',
1156+
KeySchema: [
1157+
{
1158+
AttributeName: 'timestamp',
1159+
KeyType: 'HASH',
1160+
},
1161+
],
1162+
Projection: { ProjectionType: 'ALL' },
1163+
},
1164+
{
1165+
IndexName: 'globalIndex',
1166+
KeySchema: [
1167+
{
1168+
AttributeName: 'creator',
1169+
KeyType: 'HASH',
1170+
},
1171+
{
1172+
AttributeName: 'timestamp',
1173+
KeyType: 'RANGE',
1174+
},
1175+
],
1176+
Projection: { ProjectionType: 'ALL' },
1177+
},
1178+
{
1179+
IndexName: 'binaryIndex',
1180+
KeySchema: [
1181+
{
1182+
AttributeName: 'binaryKey',
1183+
KeyType: 'HASH',
1184+
},
1185+
{
1186+
AttributeName: 'customKey',
1187+
KeyType: 'RANGE',
1188+
},
1189+
],
1190+
Projection: {
1191+
ProjectionType: 'INCLUDE',
1192+
NonKeyAttributes: [
1193+
'creator',
1194+
'timestamp',
1195+
],
1196+
},
1197+
},
1198+
],
1199+
KeySchema: [
1200+
{
1201+
AttributeName: 'partitionKey',
1202+
KeyType: 'HASH',
1203+
},
1204+
{
1205+
AttributeName: 'timestamp',
1206+
KeyType: 'RANGE',
1207+
},
1208+
],
1209+
LocalSecondaryIndexes: [
1210+
{
1211+
IndexName: 'localIndex',
1212+
KeySchema: [
1213+
{
1214+
AttributeName: 'creator',
1215+
KeyType: 'RANGE',
1216+
},
1217+
],
1218+
Projection: { ProjectionType: 'KEYS_ONLY' },
1219+
},
1220+
],
1221+
BillingMode: 'PAY_PER_REQUEST',
1222+
StreamSpecification: { StreamEnabled: false },
1223+
TableName: 'foo',
1224+
},
1225+
],
1226+
]);
1227+
});
1228+
10771229
it(
10781230
'should throw if no options were provided for a modeled index',
10791231
async () => {

packages/dynamodb-data-mapper/src/DataMapper.ts

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ import {
8383
KeySchemaElement,
8484
LocalSecondaryIndexList,
8585
Projection,
86+
ProvisionedThroughput,
8687
PutItemInput,
8788
UpdateItemInput,
8889
} from 'aws-sdk/clients/dynamodb';
@@ -275,26 +276,32 @@ export class DataMapper {
275276
*/
276277
async createTable(
277278
valueConstructor: ZeroArgumentsConstructor<any>,
278-
{
279-
readCapacityUnits,
280-
streamViewType = 'NONE',
281-
writeCapacityUnits,
282-
indexOptions = {},
283-
}: CreateTableOptions
279+
options: CreateTableOptions
284280
) {
285281
const schema = getSchema(valueConstructor.prototype);
286282
const { attributes, indexKeys, tableKeys } = keysFromSchema(schema);
287283
const TableName = this.getTableName(valueConstructor.prototype);
288284

285+
let throughput: { ProvisionedThroughput?: ProvisionedThroughput } = {};
286+
if (options.billingMode !== 'PAY_PER_REQUEST') {
287+
throughput = {
288+
...provisionedThroughput(options.readCapacityUnits, options.writeCapacityUnits),
289+
};
290+
}
291+
292+
const {
293+
streamViewType = 'NONE',
294+
indexOptions = {},
295+
billingMode,
296+
} = options;
297+
289298
const {
290299
TableDescription: {TableStatus} = {TableStatus: 'CREATING'}
291300
} = await this.client.createTable({
292301
...indexDefinitions(indexKeys, indexOptions, schema),
293302
TableName,
294-
ProvisionedThroughput: {
295-
ReadCapacityUnits: readCapacityUnits,
296-
WriteCapacityUnits: writeCapacityUnits,
297-
},
303+
...throughput,
304+
BillingMode: billingMode,
298305
AttributeDefinitions: attributeDefinitionList(attributes),
299306
KeySchema: keyTypesToElementList(tableKeys),
300307
StreamSpecification: streamViewType === 'NONE'
@@ -1281,10 +1288,7 @@ function indexDefinitions(
12811288
} else {
12821289
globalIndices.push({
12831290
...indexInfo,
1284-
ProvisionedThroughput: {
1285-
ReadCapacityUnits: indexOptions.readCapacityUnits,
1286-
WriteCapacityUnits: indexOptions.writeCapacityUnits,
1287-
},
1291+
...provisionedThroughput(indexOptions.readCapacityUnits, indexOptions.writeCapacityUnits),
12881292
});
12891293
}
12901294
}
@@ -1351,3 +1355,23 @@ function keyTypesToElementList(keys: KeyTypeMap): Array<KeySchemaElement> {
13511355

13521356
return elementList;
13531357
}
1358+
1359+
function provisionedThroughput(
1360+
readCapacityUnits?: number,
1361+
writeCapacityUnits?: number
1362+
): {
1363+
ProvisionedThroughput?: ProvisionedThroughput
1364+
} {
1365+
let capacityUnits;
1366+
if (typeof readCapacityUnits === 'number' && typeof writeCapacityUnits === 'number') {
1367+
capacityUnits = {
1368+
ReadCapacityUnits: readCapacityUnits,
1369+
WriteCapacityUnits: writeCapacityUnits,
1370+
};
1371+
}
1372+
1373+
return {
1374+
...(capacityUnits && { ProvisionedThroughput: capacityUnits })
1375+
};
1376+
}
1377+

packages/dynamodb-data-mapper/src/namedParameters/CreateTableOptions.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
import { ProvisionedThroughput } from './ProvisionedThroughput';
22
import { PerIndexOptions } from './SecondaryIndexOptions';
33

4-
export interface CreateTableOptions extends ProvisionedThroughput {
4+
interface BaseCreateTableOptions {
55
streamViewType?: StreamViewType;
66
indexOptions?: PerIndexOptions;
7+
billingMode?: BillingMode;
78
}
89

10+
export interface ProvisionedCreateTableOptions extends ProvisionedThroughput, BaseCreateTableOptions {
11+
billingMode?: 'PROVISIONED';
12+
}
13+
14+
export interface OnDemandCreateTableOptions extends BaseCreateTableOptions {
15+
billingMode: 'PAY_PER_REQUEST';
16+
}
17+
18+
export type CreateTableOptions = ProvisionedCreateTableOptions | OnDemandCreateTableOptions;
19+
20+
export type BillingMode = 'PROVISIONED' | 'PAY_PER_REQUEST';
21+
922
export type StreamViewType =
1023
'NEW_IMAGE' |
1124
'OLD_IMAGE' |
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export interface ProvisionedThroughput {
2-
readCapacityUnits: number;
3-
writeCapacityUnits: number;
2+
readCapacityUnits?: number;
3+
writeCapacityUnits?: number;
44
}

0 commit comments

Comments
 (0)