Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Count limits #86

Merged
merged 6 commits into from
Aug 29, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Changelog
=========

UNRELEASED
dimaqq marked this conversation as resolved.
Show resolved Hide resolved
----------

Release Date: UNRELEASED

* Added the `limit` parameter to :py:meth:`aiodynamo.client.Client.count`

21.8
----

Expand Down
11 changes: 9 additions & 2 deletions src/aiodynamo/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,15 @@ async def count(
start_key: Optional[Dict[str, Any]] = None,
filter_expression: Optional[Condition] = None,
index: Optional[str] = None,
limit: Optional[int] = None,
) -> int:
return await self.client.count(
self.name,
key_condition,
start_key=start_key,
filter_expression=filter_expression,
index=index,
limit=limit,
)

async def update_item(
Expand Down Expand Up @@ -745,6 +747,7 @@ async def count(
start_key: Optional[Dict[str, Any]] = None,
filter_expression: Optional[Condition] = None,
index: Optional[str] = None,
limit: Optional[int] = None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation needs to be updated :)

) -> int:
params = Parameters()

Expand All @@ -764,7 +767,7 @@ async def count(
payload.update(params.to_request_payload())

count_sum = 0
async for result in self._depaginate("Query", payload):
async for result in self._depaginate("Query", payload, limit):
count_sum += result["Count"]
return count_sum

Expand Down Expand Up @@ -907,6 +910,7 @@ async def _depaginate(
task: Optional[asyncio.Task[Dict[str, Any]]] = asyncio.create_task(
self.send_request(action=action, payload=payload)
)
is_count = payload.get("Select") == "COUNT"
try:
while task:
result = await task
Expand All @@ -919,7 +923,10 @@ async def _depaginate(
task = None
else:
if limit is not None:
limit -= len(result["Items"])
consumed: int = (
result["Count"] if is_count else len(result["Items"])
)
limit -= consumed
if limit > 0:
payload["Limit"] = limit
else:
Expand Down
14 changes: 14 additions & 0 deletions tests/integration/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,20 @@ async def test_count(client: Client, table: TableName):
)


async def test_count_with_limit(client: Client, table: TableName):
hk = HashKey("h", "k")
assert await client.count(table, hk, limit=1) == 0
await client.put_item(table, {"h": "k", "r": "0"})
for i in range(1, 20):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the reason for repeating this 20 times?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to call it more than a few times to ensure the limit parameter was being correctly applied. The number 20 is arbitrary.

Copy link
Contributor

@ojii ojii Aug 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that idea, but I think the loop adds little value. Instead, how about testing these scenarios:

  • 1 item table, limit 1 (tests a full count despite limit)
  • 2 item table, limit 1 (tests a single page partial count)
  • table above page size, limit larger than page size but not all items (tests a multi page partial count)

There's lots of other potential scenarios, but I feel like these are the most important. The main point I'm trying to suggest is to test specific scenarios, rather than the same one 20 times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, but what is the page size? I know from experience on our production DynamoDB instances some tables would return around 10k items using a single query. I think it depends on the sizes of the items?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm I see

async def test_query_with_limit(client: Client, high_throughput_table: TableName):
already dealing with pagination, maybe I can use this approach for the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ojii Test reworked!

assert await client.count(table, hk, limit=30) == i
assert await client.count(table, hk, limit=i) == i

await client.put_item(table, {"h": "k", "r": str(i)})

assert await client.count(table, hk, limit=i) == i
assert await client.count(table, hk, limit=i + 1) == i + 1


async def test_update_item(client: Client, table: TableName):
item = {
"h": "hkv",
Expand Down