The following code writes three records to DynamoDB. Create another file called dynamo_modify_items.py with the following Python code:
from boto3 import resource class DynamoRepository: def __init__(self, target_dynamo_table, region='eu-west-1'): self.dynamodb = resource(service_name='dynamodb',
region_name=region) self.target_dynamo_table = target_dynamo_table self.table = self.dynamodb.Table(self.target_dynamo_table) def update_dynamo_event_counter(self, event_name,
event_datetime, event_count=1): return self.table.update_item( Key={ 'EventId': event_name, 'EventDay': event_datetime }, ExpressionAttributeValues={":eventCount": event_count}, UpdateExpression="ADD EventCount :eventCount") def main(): table_name = 'user-visits' dynamo_repo = DynamoRepository(table_name) print(dynamo_repo.update_dynamo_event_counter('324', 20171001)) print(dynamo_repo.update_dynamo_event_counter('324', 20171001, 2)) print(dynamo_repo.update_dynamo_event_counter('324', 20171002, 5)) if __name__ == '__main__': main()
Here, we use Boto3's resource(), which is a higher-level service resource with the repository pattern. We abstract all the DynamoDB-related code in the DynamoRepository() class that instantiates as dynamo_repo with table_name. self.dynamodb.Table() creates a table resource based on table_name. That will be used later on when calling update_dynamo_event_counter() to update DynamoDB records.
In self.table.update_item(), I first declare a variable called eventCount using ExpressionAttributeValues. I'm using this in the DynamoDB advanced Update Expressions (https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html), which is one of my favorite features in DynamoDB. Why? Because not all NoSQL databases can do something similar without having something like a semaphore lock and having the clients do a retry. It performs the following three actions in one atomic statement, while circumventing possible concurrency violations at the cost of eventual consistency:
- Reads records matching the given EventId=event_name and EventDay=event_datetime
- Creates a new item if it doesn't exist, setting EventCount=1
- If it does already exist, then it increments EventCount by event_count
The first function calls dynamo_repo.update_dynamo_event_counter('324', 20171001), sets EventCount to 1; the second function call, dynamo_repo.update_dynamo_event_counter('324', 20171001, 2), increments EventCount by 2, so that it's now 3. The third function call adds a new record, as the EventCount or primary key, is different.