0
0
DynamodbHow-ToBeginner · 4 min read

How to Implement a Leaderboard Using DynamoDB

To implement a leaderboard in DynamoDB, use a table with a partition key for the leaderboard category and a sort key for the score (stored as a negative number for descending order). Query the table with ScanIndexForward=true to get top scores efficiently.
📐

Syntax

A DynamoDB leaderboard table typically uses:

  • Partition Key: Identifies the leaderboard group (e.g., game or category).
  • Sort Key: Stores the score as a negative number to sort descending.
  • Attributes: Store user ID, timestamp, or other metadata.

Query syntax example to get top scores:

Query({
  KeyConditionExpression: 'LeaderboardId = :id',
  ExpressionAttributeValues: { ':id': 'game1' },
  ScanIndexForward: true, // ascending order on negative scores
  Limit: 10
})
sql
Query({
  KeyConditionExpression: 'LeaderboardId = :id',
  ExpressionAttributeValues: { ':id': 'game1' },
  ScanIndexForward: true, // ascending order on negative scores
  Limit: 10
})
💻

Example

This example creates a DynamoDB table for a leaderboard and inserts sample scores. It then queries the top 3 scores in descending order.

python
import boto3
from boto3.dynamodb.conditions import Key

# Initialize DynamoDB resource
dynamodb = boto3.resource('dynamodb', region_name='us-west-2')

# Create table (run once)
try:
    table = dynamodb.create_table(
        TableName='Leaderboard',
        KeySchema=[
            {'AttributeName': 'LeaderboardId', 'KeyType': 'HASH'},  # Partition key
            {'AttributeName': 'NegativeScore', 'KeyType': 'RANGE'}  # Sort key
        ],
        AttributeDefinitions=[
            {'AttributeName': 'LeaderboardId', 'AttributeType': 'S'},
            {'AttributeName': 'NegativeScore', 'AttributeType': 'N'}
        ],
        ProvisionedThroughput={'ReadCapacityUnits': 5, 'WriteCapacityUnits': 5}
    )
    table.wait_until_exists()
except dynamodb.meta.client.exceptions.ResourceInUseException:
    table = dynamodb.Table('Leaderboard')

# Insert sample data
items = [
    {'LeaderboardId': 'game1', 'NegativeScore': -150, 'UserId': 'userA'},
    {'LeaderboardId': 'game1', 'NegativeScore': -300, 'UserId': 'userB'},
    {'LeaderboardId': 'game1', 'NegativeScore': -200, 'UserId': 'userC'},
    {'LeaderboardId': 'game1', 'NegativeScore': -100, 'UserId': 'userD'}
]
for item in items:
    table.put_item(Item=item)

# Query top 3 scores
response = table.query(
    KeyConditionExpression=Key('LeaderboardId').eq('game1'),
    ScanIndexForward=True,  # Because scores are negative, ascending order gives descending scores
    Limit=3
)

for rank, item in enumerate(response['Items'], start=1):
    print(f"Rank {rank}: User {item['UserId']} with score {-item['NegativeScore']}")
Output
Rank 1: User userB with score 300 Rank 2: User userC with score 200 Rank 3: User userA with score 150
⚠️

Common Pitfalls

Common mistakes when implementing a leaderboard in DynamoDB include:

  • Not using a sort key to order scores, which makes querying top scores inefficient.
  • Storing scores as positive numbers and trying to sort descending, which DynamoDB does not support natively.
  • Using Scan instead of Query, causing slow and costly operations.
  • Not limiting query results, leading to large data transfers.

Correct approach is to store scores as negative numbers in the sort key and use Query with ScanIndexForward=true to get descending order.

sql
/* Wrong: Storing positive scores and trying to sort descending */
Query({
  KeyConditionExpression: 'LeaderboardId = :id',
  ExpressionAttributeValues: { ':id': 'game1' },
  ScanIndexForward: false, // descending order
  Limit: 10
})

/* Right: Store negative scores and sort ascending */
Query({
  KeyConditionExpression: 'LeaderboardId = :id',
  ExpressionAttributeValues: { ':id': 'game1' },
  ScanIndexForward: true, // ascending order on negative scores
  Limit: 10
})
📊

Quick Reference

ConceptDescriptionExample
Partition KeyGroups leaderboard entries (e.g., game ID)'LeaderboardId' = 'game1'
Sort KeyStores negative score for descending order'NegativeScore' = -300
QueryFetch top scores efficientlyQuery with ScanIndexForward=true and Limit=10
Avoid ScanUse Query to reduce cost and latencyDo not use Scan for leaderboard queries
Score StorageStore scores as negative numbersScore 300 stored as -300

Key Takeaways

Use a partition key for leaderboard grouping and a sort key with negative scores for sorting.
Query with ScanIndexForward=true to get top scores in descending order efficiently.
Avoid Scan operations; always use Query with proper keys and limits.
Store scores as negative numbers to leverage DynamoDB's ascending sort order for descending scores.
Limit query results to improve performance and reduce costs.