DynamoDB Tables
Amazon DynamoDB is a serverless NoSQL key-value and document database that delivers single-digit millisecond performance at any scale. It's a fully managed, multi-active, durable database that is replicated across multiple Availability Zones.
Stacktape's DynamoDbTable
resource provides a simple abstraction for provisioning and configuring Amazon DynamoDB tables.
When to use DynamoDB
Advantages
- Performance at scale: DynamoDB provides consistent, single-digit millisecond response times at any scale. You can build applications with virtually unlimited throughput and storage.
- Auto-scalable: DynamoDB supports auto-scaling of both read/write capacity and storage, so you don't have to worry about capacity planning.
- Fine-grained access control: You can manage access to your DynamoDB tables using IAM roles and policies.
- Connection-less: Your application connects to DynamoDB via an API, so you don't have to manage a connection pool.
- Pay-per-request pricing: DynamoDB has a "pay-as-you-go" pricing model. If there's no load on your database, it's essentially free.
Disadvantages
- Unfamiliar data modeling: If you're used to relational databases, DynamoDB's NoSQL data modeling can take some getting used to.
- Proprietary: DynamoDB is a proprietary AWS database, which can lead to vendor lock-in.
- Not as feature-rich as other databases: DynamoDB is more of a data store than a fully-featured database like Postgres or MongoDB. For a detailed comparison, refer to this MongoDB vs. DynamoDB article (which is arguably biased).
Basic Usage
The following example defines a simple DynamoDB table.
resources:myDynamoDbTable:type: dynamo-db-tableproperties:primaryKey:partitionKey:name: idtype: stringmyFunction:type: functionproperties:packaging:type: stacktape-lambda-buildpackproperties:entryfilePath: path/to/my/lambda.tsenvironment:- name: TABLE_NAMEvalue: $ResourceParam('myDynamoDbTable', 'name')connectTo:- myDynamoTable
You can connect a Lambda function to the table to read and write data:
import { DynamoDB } from '@aws-sdk/client-dynamodb';const dynamoDb = new DynamoDB({});export default async (event, context) => {// Put item to the tableawait dynamoDb.putItem({Item: { id: { S: 'my_id_1' }, writeTimestamp: { S: new Date().toLocaleTimeString() } },TableName: process.env.TABLE_NAME});// Get item from the tableconst item = await dynamoDb.getItem({Key: { id: { S: 'my_id_1' } },TableName: process.env.TABLE_NAME});};
Primary Key
- Primary key uniquely identifies each item in the table
- Two different kinds of primary keys are supported:
- simple primary key - you only specify
partitionKey
- composite primary key - you specify both
partitionKey
andsortKey
- simple primary key - you only specify
- Primary key specification cannot be modified during updates (after the table is created).
- The attribute must be top-level (not nested) field.
- Each document saved to the table must contain the primary key attribute(s).
- To learn more about primary keys, refer to AWS docs
Every item in a DynamoDB table is uniquely identified by a primary key. There are two types of primary keys:
- Partition key: A simple primary key, composed of one attribute. DynamoDB uses the partition key's value as input to an internal hash function. The output from the hash function determines the partition (physical storage internal to DynamoDB) in which the item will be stored.
- Partition key and sort key (composite primary key): All items with the same partition key value are stored together, in sorted order by the sort key value.
This example shows a table with a composite primary key:
resources:myDynamoTable:type: dynamo-db-tableproperties:primaryKey:partitionKey:name: this_attribute_will_be_partition_keytype: stringsortKey:name: this_attribute_will_be_sort_keytype: number
Provisioned Throughput
- When you specify
provisionedThroughput
, the table will run in the provisioned mode and you need to specify read and write throughput for your table. - This can give you cost predictability and lower costs, but your table might not be able to handle an unpredictable load.
- If
provisionedThroughput
is not configured, the table runs and is billed in an on-demand mode. This means you are only paying for what you use. - To learn more about differences between provisioned and on-demand modes, refer to AWS docs
DynamoDB has two capacity modes for processing reads and writes on your tables: on-demand and provisioned.
- On-demand: This is the default mode. You pay per request, which is suitable for unpredictable workloads.
- Provisioned: You specify the number of reads and writes per second that you require for your application. This is best for predictable traffic.
You can also configure auto-scaling for provisioned throughput to automatically adjust your table's capacity based on traffic.
resources:myDynamoDbTable:type: dynamo-db-tableproperties:primaryKey:partitionKey:name: this_attribute_will_be_idtype: stringprovisionedThroughput:readUnits: 4writeUnits: 4
Throughput Scaling
- Even in provisioned mode, you can configure throughput scaling based on load.
- The table throughput scales up or down once the specified thresholds are met.
- Compared to on-demand mode (default, if you don't specify
provisionedThroughput
), you have more control of how your table will scale and can save costs. However, your table might not be able to scale enough for an unpredictable load. - To learn more, refer to this detailed AWS article
The following example shows how to configure auto-scaling for read and write capacity.
resources:myDynamoDbTable:type: dynamo-db-tableproperties:primaryKey:partitionKey:name: this_attribute_will_be_idtype: stringprovisionedThroughput:readUnits: 4writeUnits: 4readScaling:minUnits: 4maxUnits: 10keepUtilizationUnder: 80
Point-in-Time Recovery
- Point-in-time recovery enables you to restore table to any point in time during the last 35 days
- The point-in-time recovery process always restores the data to a new table. You can restore data to the same table.
- Enabling point-in-time recovery can result in additional charges. To learn more, refer to AWS DynamoDB pricing
Point-in-time recovery (PITR) helps protect your tables from accidental write or delete operations. With PITR, you can restore that table to any point in time during the last 35 days.
resources:myDynamoDbTable:type: dynamo-db-tableproperties:primaryKey:partitionKey:name: this_attribute_will_be_idtype: stringenablePointInTimeRecovery: true
Item-Change Streaming
You can capture a time-ordered sequence of item-level modifications in any DynamoDB table and store this information in a log for up to 24 hours. Applications can access this log and view the data items as they appeared before and after they were modified, in near-real time. This is called DynamoDB Streams.
Streams can be consumed by functions and batch jobs.
You must configure the stream type, which determines what information is written to the stream when an item in the table is modified. Allowed values are:
KEYS_ONLY
: Only the key attributes of the modified item.NEW_IMAGE
: The entire item as it appeared after it was modified.OLD_IMAGE
: The entire item as it appeared before it was modified.NEW_AND_OLD_IMAGES
: Both the new and the old images of the item.
resources:myDynamoDbTable:type: dynamo-db-tableproperties:primaryKey:partitionKey:name: this_attribute_will_be_idtype: stringstreamType: NEW_AND_OLD_IMAGES
Access Control
DynamoDB uses IAM to control who can access the table. To interact with a DynamoDB table, your compute resources must have sufficient permissions. You can grant these permissions in two ways:
- List the table in the
connectTo
array of your compute resource. Stacktape will automatically grant the necessary permissions. - Configure
iamRoleStatements
on your compute resource to define a custom policy.
Using connectTo
is the recommended approach. Here's an example:
resources:myDynamoDbTable:type: dynamo-db-tableproperties:primaryKey:partitionKey:name: idtype: stringmyFunction:type: functionproperties:packaging:type: stacktape-lambda-buildpackproperties:entryfilePath: path/to/my/lambda.tsenvironment:- name: TABLE_NAMEvalue: $ResourceParam('myDynamoDbTable', 'name')connectTo:- myDynamoDbTable
If you need more fine-grained control, you can use iamRoleStatements
:
resources:myDynamoDbTable:type: dynamo-db-tableproperties:primaryKey:partitionKey:name: idtype: stringmyFunction:type: functionproperties:packaging:type: stacktape-lambda-buildpackproperties:entryfilePath: path/to/my/lambda.tsenvironment:- name: TABLE_NAMEvalue: $ResourceParam('myDynamoDbTable', 'name')iamRoleStatements:- Resource:- $ResourceParam('myDynamoDbTable', 'Arn')Effect: 'Allow'Action:- 'dynamodb:Get*'- 'dynamodb:Query'- 'dynamodb:Scan'- 'dynamodb:Delete*'- 'dynamodb:Update*'- 'dynamodb:PutItem'
Pricing
You are charged for:
- Read and Write operations:
- PROVISIONED mode:
- You configure how much read/write capacity table has at any moment:
- WCU (write capacity unit) - $0.00065 - $0.000975 per WCU per hour
- RCU (read capacity units - $0.00013 - $0.000195 per RCU per hour
- To learn more, refer to AWS pricing page
- ON DEMAND mode
- Pay as you go
- $1.25 - $1.875 per million write request units
- $0.25- $0.375 per million read request units
- To learn more, refer to AWS pricing page
- PROVISIONED mode:
- Storage:
- First 25 GB stored per month is free
- $0.25 - $0.375 per GB-month thereafter
- Continuous backup:
- Applies when point-in-time-recovery is enabled
- $0.20 -$0.30 per GB-month
- Data transfer:
- IN transfer: free
- OUT to AWS services in the same region: free
- OUT to internet: first 1 GB free, then $0.09 -$0.15 per GB
FREE TIER (eligible for first 12 months):
- 25 GB of Storage
- 25 provisioned Write Capacity Units (WCU)
- 25 provisioned Read Capacity Units (RCU)
- Enough to handle up to ~200M requests per month.
Referenceable parameters
The following parameters can be easily referenced using $ResourceParam directive directive.
To learn more about referencing parameters, refer to referencing parameters.
AWS (physical) name of the table
- Usage:
$ResourceParam('<<resource-name>>', 'name')
Arn of the table
- Usage:
$ResourceParam('<<resource-name>>', 'arn')
Arn of DynamoDb stream (available only if
streamType
is configured)- Usage:
$ResourceParam('<<resource-name>>', 'streamArn')