skip to Main Content

I created a Lambda function that connects to my DynamoDB table and gets the item view_count and then sets view_count += 1 and then updates the view_count item and returns view_count as a string.

When I open the Function URL the first time, it increments view_count by 1 and outputs 1 which is what I want. But whenever I refresh the page (to simulate another visitor to the website) it always adds 2 more to view_count instead of 1 more. The item in the table also always shows one more than what is displayed in the function URL.

My Lambda code:

import boto3
import json
def lambda_handler(event, context):
    # Connect to DynamoDB
    dynamodb = boto3.resource("dynamodb")
    table = dynamodb.Table("Table-CRC")

    # Get the current view count
    response = table.get_item(Key={"id": "1"})
    item = response.get("Item", {})
    view_count = item.get("view_count", 0)

    # Increment the view count
    view_count += 1

    # Update the view count in DynamoDB
    table.put_item(Item={"id": "1", "view_count": view_count})
  
   # Return the updated view count
    return {
        "statusCode": 200,
        "body": str(view_count),
        "headers": {
            'Content-Type': 'application/json',
        }
    }

Original setting of the item:

Original setting of the item:

First load of the URL:

First load of the URL:

What the item changed to:

What the item changed to:

Second load of the URL (hitting refresh):

Second load of the URL (hitting refresh):

What the item changed to after that:

What the item changed to after that:

I tried putting the view_count in the table.get_item Key statement and removing the two lines under it but that led to an Internal Server Error when I tried to access the Function URL so I think that’s bad code.

2

Answers


  1. import boto3
    from botocore.exceptions import ClientError
    
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table("Table-CRC")
    
    try:
        response = table.update_item(
            Key={partition_key: partition_value, sort_key: sort_value}, # Key={id: "1"}
            UpdateExpression="SET view_count = view_count + :val",
            ExpressionAttributeValues={':val': 1},
            ReturnValues="UPDATED_NEW"
        )
        print("Increment succeeded.")
    except ClientError as e:
        print("Error:", e.response['Error']['Message'])
    
    Login or Signup to reply.
  2. Your actually not updating anything, you are overwriting the item everytime by using put_item. You want to use update_item which will ensure no issue with race conditions.

    import boto3
    import json
    # Connect to DynamoDB
    dynamodb = boto3.resource("dynamodb")
    table = dynamodb.Table("Table-CRC")
    def lambda_handler(event, context):
    
       res = table.update_item(
            Key={"Id": "1"},
            UpdateExpression="SET view_count = view_count + :val",
            ExpressionAttributeValues={':val': 1},
            ReturnValues="UPDATED_NEW"
        )
      
       # Return the updated view count
        return {
            "statusCode": 200,
            "body": str(res['Item']['view_count']),
            "headers": {
                'Content-Type': 'application/json',
            }
        }
    

    Some things to note about the code:

    We no longer need to do get_item as DynamoDB can increment a number for us, this helps us avoid concurrency issues. We don’t set any conditions in our update, as I assume over/under counting can be tolerated for your use-case where your counting webpage hits.

    We use DynamoDB update_item which updates the item and not overwrites it, which again can help any concurrency issues. We return the new values, which returns the item after the update completed so you can return the current value accurately to your client.

    We moved the DynamoDB initialization outside of the Lambda handler which will improve performance of your Lambda and can also reduce costs for KMS if you use encryption keys.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search