Dynoxide 1.6.0 release notes
Dynoxide 1.6.0 went out on Saturday night and I forgot to write it up, so this is a couple of days late. Most of the release is about PartiQL, and specifically about how much a SELECT reads.
PartiQL reads what DynamoDB reads
Take a query for two orders in one customer's partition:
SELECT * FROM orders WHERE pk = 'customer#42' AND sk IN ['order#1', 'order#3']
DynamoDB treats that as two key lookups. It reads those two items and charges a read for each one, whether the item exists or not. Dynoxide read the whole partition instead and charged for all of it. The result set was right, so nothing failed, but the consumed capacity was whatever the partition happened to weigh. On a customer with a few hundred orders that's a number DynamoDB would never give you.
The same was true for an OR across sort keys (sk = 'order#1' OR sk = 'order#3'). And an OR where every branch named the same partition key was worse: it scanned the whole table, charging for rows in other partitions and counting them towards Limit.
All of those now read only the keys they name, on tables and on both kinds of secondary index. Pagination follows along. A Limit read that finishes on the last key doesn't hand back a NextToken, so fetching two keys with Limit: 1 takes two pages, as it does on DynamoDB, where it used to take three.
I care about this more than most fixes because a lot of what I use Dynoxide for is working out what something will cost before it goes anywhere near AWS (conditional transactions, for example). If the capacity figures are off, so is everything built on them.
Thanks to @jay23606, who raised and fixed the table-scan case.
A handful of PartiQL queries that DynamoDB accepts and Dynoxide didn't (or the other way round) got sorted. ORDER BY works when OR branches share a partition, big OR filters with forty pairs of conditions run instead of failing, and a NOT over a key attribute in the wrong place gets DynamoDB's error. The release notes have the specifics.
Numbers in the wrong place
The other chunk of work is about what happens when a request carries an odd number. Send ExecuteStatement a Limit of 1.5 and DynamoDB quietly truncates it to 1. Dynoxide refused it with an error from serde, the JSON library underneath, which isn't a message anyone's code expects to see. 1e400 as a Scan Segment got a bare SerializationException, and a number where TableName should be got a different message from the one DynamoDB gives, which depends on how you wrote the number (NUMBER_VALUE or DECIMAL_VALUE).
None of it matters much day to day. It matters if you've got tests asserting on validation errors. Each of these was captured against DynamoDB in eu-west-2 and us-east-1 last week, and Dynoxide now gives the same answer.
The one that might break something
Expressions now refuse attribute names starting with an underscore unless they go through ExpressionAttributeNames, as DynamoDB does. Dynoxide used to let _version through in a filter or update expression, and anything relying on that would have worked locally and failed for real. This covers import rules too, so a match expression naming one needs an alias in its names.
If your tests assert on consumed capacity or page counts for PartiQL reads, some of those will also move. The new numbers are DynamoDB's.
Also in this release
@alfaproject fixed UpdateItem with ReturnValues: ALL_OLD on an item that didn't exist yet. It returned the key attributes as the "old" image, where DynamoDB returns nothing (#292).
There's also a zstd bump for four RustSec advisories. None of them reach the code Dynoxide uses zstd for, but it's built into the binary, so the update ships anyway.
The crate is dynoxide-rs 3.3.0 and everything else is 1.6.0. npm install --save-dev dynoxide@1.6.0 if you're coming from npm. The release notes have the full list.