Skip to content

Commit

Permalink
Merge pull request aws-samples#152 from mkamioner/python-conditional-…
Browse files Browse the repository at this point in the history
…samples

Python conditional samples
  • Loading branch information
tebanieo authored Oct 7, 2024
2 parents cadd904 + 0202586 commit a853f95
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,2 +1,48 @@
import boto3, json
from boto3.dynamodb.conditions import Key
from __future__ import print_function # Python 2/3 compatibility
import boto3
from boto3.dynamodb.conditions import Attr
from botocore.exceptions import ClientError

dynamodb = boto3.resource("dynamodb", region_name="us-west-2")
table = dynamodb.Table("RetailDatabase")


def create_item_if_not_exist(created_at):
try:
table.put_item(
Item={
"pk": "jim.bob@somewhere.com",
"sk": "metadata",
"name": "Jim Bob",
"first_name": "Jim",
"last_name": "Bob",
"created_at": created_at,
},
# If this item exists, then an item exists with this pk and/or sk so we should fail if
# we see a matching item with this pk.
ConditionExpression=Attr("pk").not_exists(),
)
except dynamodb.meta.client.exceptions.ConditionalCheckFailedException:
print("PutItem failed since the item already exists")
return False

print("PutItem succeeded")
return True


def get_created_at(email):
response = table.get_item(
Key={"pk": email, "sk": "metadata"},
AttributesToGet=["created_at"],
)
return response.get("Item", {}).get("created_at")


print("The first PutItem should succeed because the item does not exist")
assert create_item_if_not_exist(100)

print("The same PutItem command should now fail because the item already exists")
assert not create_item_if_not_exist(200)

assert get_created_at("jim.bob@somewhere.com") == 100
print("As expected, The second PutItem command failed and the data was not overwritten")
Original file line number Diff line number Diff line change
@@ -1,2 +1,105 @@
import boto3, json
from boto3.dynamodb.conditions import Key
from __future__ import print_function # Python 2/3 compatibility
import boto3
from boto3.dynamodb.conditions import Attr
from botocore.exceptions import ClientError

dynamodb = boto3.resource("dynamodb", region_name="us-west-2")
table = dynamodb.Table("RetailDatabase")


class ConditionalCheckFailedError(Exception):
"""
An error indicating that a DynamoDB conditional check failed.
Wrapped as a separate error for readability.
"""


def delete_item(email):
response = table.delete_item(
Key={
"pk": email,
"sk": "metadata",
},
)


def create_user(email, name):
initial_change_version = 0

try:
table.put_item(
Item={
"pk": email,
"sk": "metadata",
"name": name,
"change_version": initial_change_version,
},
ConditionExpression=Attr("pk").not_exists(),
)
except dynamodb.meta.client.exceptions.ConditionalCheckFailedException as e:
raise ConditionalCheckFailedError() from e

return initial_change_version


def update_name(email, name, last_change_version):
try:
response = table.update_item(
Key={"pk": email, "sk": "metadata"},
UpdateExpression="SET #n = :nm, #cv = #cv + :one",
ExpressionAttributeNames={"#n": "name", "#cv": "change_version"},
ExpressionAttributeValues={":nm": name, ":one": 1},
ReturnValues="UPDATED_NEW",
ConditionExpression=Attr("change_version").eq(last_change_version),
)
except dynamodb.meta.client.exceptions.ConditionalCheckFailedException as e:
raise ConditionalCheckFailedError() from e

return int(response.get("Attributes", {}).get("change_version"))


def get_user(email):
response = table.get_item(
Key={"pk": email, "sk": "metadata"},
AttributesToGet=["name", "change_version"],
)
return {
"email": email,
"name": response["Item"]["name"],
"change_version": int(response["Item"]["change_version"]),
}


def main():
delete_item("jim.bob@somewhere.com")

print("Creating an item with change_version = 0")
last_change_version = create_user("jim.bob@somewhere.com", "Jim Bob")
current_user = get_user("jim.bob@somewhere.com")
print("current_user = ", current_user)

print("Change the user's name")
last_change_version = update_name(
"jim.bob@somewhere.com", "Jim Roberts", last_change_version
)
current_user = get_user("jim.bob@somewhere.com")
print("current_user = ", current_user)

print(
"Try to update the name with an old change_version imitating a race condition"
)
try:
last_change_version = update_name(
"jim.bob@somewhere.com", "Jonathan Roberts", last_change_version - 1
)
except ConditionalCheckFailedError:
print("Yup, this failed as expected, the user information did not change")
else:
raise RuntimeError("This should have failed")

current_user = get_user("jim.bob@somewhere.com")
print("current_user = ", current_user)


if __name__ == "__main__":
main()

0 comments on commit a853f95

Please sign in to comment.