-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Open
Labels
bugThis issue is a confirmed bug.This issue is a confirmed bug.documentationThis is a problem with documentation.This is a problem with documentation.dynamodbp2This is a standard priority issueThis is a standard priority issuepagination
Description
tldr: The pagination token returned by dynamodb paginators doesn't match the documentation, and cannot be passed in as a starting point for pagination.
Docs Issue
The docs for dynamodb paginators say each page contains NextToken
. But it's actually LastEvaluatedKey
.
Steps to reproduce:
import boto3
import datetime as dt
table_name = 'test-token'
client = boto3.client('dynamodb')
try:
client.create_table(
AttributeDefinitions=[
{
'AttributeName': 'h',
'AttributeType': 'N'
},
],
TableName=table_name,
KeySchema=[
{
'AttributeName': 'h',
'KeyType': 'HASH'
},
],
BillingMode='PAY_PER_REQUEST',
Tags=[
{
'Key': 'delete after',
'Value': str(dt.date.today())
},
{
'Key': 'person',
'Value': 'matt'
}
],
)
except client.exceptions.ResourceInUseException:
pass
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(table_name)
print("Waiting for table to exist")
table.wait_until_exists()
page_size=2
num_items = page_size*3+1
print("Writing data")
for i in range(num_items):
table.put_item(
Item={
'h': i
},
)
print("Reading data")
paginator = client.get_paginator('scan')
response_iterator = paginator.paginate(
TableName=table_name,
PaginationConfig={
'PageSize': page_size,
}
)
for (i, page) in enumerate(response_iterator):
print(page.keys())
print(f"Page {i} has {len(page.get('Items', []))} items, NextToken={page.get('NextToken')}")
$ python3 main.py
Waiting for table to exist
Writing data
Reading data
dict_keys(['Items', 'Count', 'ScannedCount', 'LastEvaluatedKey', 'ResponseMetadata'])
Page 0 has 2 items, NextToken=None
dict_keys(['Items', 'Count', 'ScannedCount', 'LastEvaluatedKey', 'ResponseMetadata'])
Page 1 has 2 items, NextToken=None
dict_keys(['Items', 'Count', 'ScannedCount', 'LastEvaluatedKey', 'ResponseMetadata'])
Page 2 has 2 items, NextToken=None
dict_keys(['Items', 'Count', 'ScannedCount', 'ResponseMetadata'])
Page 3 has 1 items, NextToken=None
Code Issue
If you try to take this LastEvaluatedKey
and pass it to the StartingToken
field of the paginator, you get an error. Because LastEvaluatedKey
is not a string, it's a dict.
import boto3
import datetime as dt
from pprint import pprint
table_name = 'test-token'
client = boto3.client('dynamodb')
try:
client.create_table(
AttributeDefinitions=[
{
'AttributeName': 'h',
'AttributeType': 'N'
},
],
TableName=table_name,
KeySchema=[
{
'AttributeName': 'h',
'KeyType': 'HASH'
},
],
BillingMode='PAY_PER_REQUEST',
Tags=[
{
'Key': 'delete after',
'Value': str(dt.date.today())
},
{
'Key': 'person',
'Value': 'matt'
}
],
)
except client.exceptions.ResourceInUseException:
pass
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(table_name)
print("Waiting for table to exist")
table.wait_until_exists()
page_size=2
num_items = page_size*3+1
print("Writing data")
for i in range(num_items):
table.put_item(
Item={
'h': i
},
)
print("Reading data")
paginator = client.get_paginator('scan')
response_iterator = paginator.paginate(
TableName=table_name,
PaginationConfig={
'PageSize': page_size,
'StartingToken': {'h': {'N': '1'}}
}
)
for (i, page) in enumerate(response_iterator):
pprint(page)
$ python3 main.py
Waiting for table to exist
Writing data
Reading data
Traceback (most recent call last):
File "main.py", line 66, in <module>
for (i, page) in enumerate(response_iterator):
File "/home/ec2-user/.pyenv/versions/3.8.11/lib/python3.8/site-packages/botocore/paginate.py", line 261, in __iter__
next_token = self._parse_starting_token()[0]
File "/home/ec2-user/.pyenv/versions/3.8.11/lib/python3.8/site-packages/botocore/paginate.py", line 533, in _parse_starting_token
next_token = self._token_decoder.decode(next_token)
File "/home/ec2-user/.pyenv/versions/3.8.11/lib/python3.8/site-packages/botocore/paginate.py", line 124, in decode
json_string = base64.b64decode(token.encode('utf-8')).decode('utf-8')
AttributeError: 'dict' object has no attribute 'encode'
If I add json.dumps()
to the key, I get:
Traceback (most recent call last):
File "main.py", line 67, in <module>
for (i, page) in enumerate(response_iterator):
File "/home/ec2-user/.pyenv/versions/3.8.11/lib/python3.8/site-packages/botocore/paginate.py", line 269, in __iter__
response = self._make_request(current_kwargs)
File "/home/ec2-user/.pyenv/versions/3.8.11/lib/python3.8/site-packages/botocore/paginate.py", line 357, in _make_request
return self._method(**current_kwargs)
File "/home/ec2-user/.pyenv/versions/3.8.11/lib/python3.8/site-packages/botocore/client.py", line 530, in _api_call
return self._make_api_call(operation_name, kwargs)
File "/home/ec2-user/.pyenv/versions/3.8.11/lib/python3.8/site-packages/botocore/client.py", line 919, in _make_api_call
request_dict = self._convert_to_request_dict(
File "/home/ec2-user/.pyenv/versions/3.8.11/lib/python3.8/site-packages/botocore/client.py", line 990, in _convert_to_request_dict
request_dict = self._serializer.serialize_to_request(
File "/home/ec2-user/.pyenv/versions/3.8.11/lib/python3.8/site-packages/botocore/validate.py", line 381, in serialize_to_request
raise ParamValidationError(report=report.generate_report())
botocore.exceptions.ParamValidationError: Parameter validation failed:
Invalid type for parameter ExclusiveStartKey, value: {"h": {"N": "1"}}, type: <class 'str'>, valid types: <class 'dict'>
Links
Metadata
Metadata
Assignees
Labels
bugThis issue is a confirmed bug.This issue is a confirmed bug.documentationThis is a problem with documentation.This is a problem with documentation.dynamodbp2This is a standard priority issueThis is a standard priority issuepagination