Repository navigation
The crate moves to dynoxide-rs 3.1.0 for the new server::serve_with_shutdown.
Everything else a user installs carries 1.4.0. Most of the release brings
answers in line with DynamoDB, so some requests answer differently: see
Behaviour changes and docs/compatibility-summary.md.
Security
rustls-webpkimoves to 0.103.16 for RUSTSEC-2026-0382. Earlier
versions could accept a certificate chain through one of the distrusted
emSign intermediates, whose private keys are now public.rustlsmoves to
0.23.46 alongside it for RUSTSEC-2026-0346 to RUSTSEC-2026-0349. Both reach
the tree through dev-dependencies only and never shipped in a released
artefact.
Behaviour changes
Database::scanandDatabase::querycheck a request built in Rust as the
HTTP path checks it, so they refuse requests they used to run: aLimitof
0 on either, and onDatabase::scanaSelectorReturnConsumedCapacity
outside its enum and aSegmentabove 999999. Scan's member errors now come
fromDatabase::scanrather than from decoding the request, so a
ScanRequestwith aSelectofBADdecodes.- For a request built in Rust, an empty
UpdateExpressioncomes back as
DynoxideError::EnvelopedValidationrather thanValidationException; the
message is unchanged. dynoxide mcprefuses--max-items 0as it starts, and itsscanand
querytools refuse alimitof 0, as DynamoDB refuses aLimitof 0.
Added
server::serve_with_shutdownstops an embedded HTTP server cleanly. It
serves on a listener you bind until a future you supply resolves, then stops
accepting, closes idle keep-alive connections, lets running requests finish
and returns. Stopping a server you had started yourself used to mean
aborting its task, which left the connection tasks holding the database, so
a file-backed database's files stayed open (and could not be deleted on
Windows).startnow runs on it too, with its Ctrl-C and SIGTERM handling
unchanged.dynoxide importreads JSON Lines files named.jsonland.jsonl.gz, in a DynamoDB Export directory (<dir>/<TableName>/data/) or a flat directory of those files, alongside the.jsonand.json.gzparts it already accepted (#250, thanks @samchungy).
Fixed
- A
SearchVectorsRequestbuilt in Rust now meets theIndexName,
SearchVectorandTopKchecks. AnIndexNameshorter than three
characters, an emptySearchVectoror aTopKbelow 1 used to get past
Database::search_vectorsand fail later with whatever came next. They now
get the same validation message as the same request sent over HTTP, in the
same order. ATopKbelow 1 now gets the1 validation error detected:
form it has over HTTP rather than the bare out-of-range message;TopKabove
100 keeps the bare message. - Five request-validation messages now match DynamoDB when more than one
thing is wrong. UpdateItem with noTableNamekeeps that error beside a
badReturnValues, and, when the key is missing too, beside a bad
ReturnConsumedCapacityorReturnItemCollectionMetrics, in the order
DynamoDB lists them. Query with
noTableNameand aLimitof 0 reports both, with a badSelectfirst.
BatchGetItem reports a malformed table name ahead of the total key count.
DeleteItem treats aReturnValuesofALL_NEWorUPDATED_NEWas a request
member: it is joined after the other member errors and reported ahead of any
expression error. UpdateItem puts an emptyUpdateExpressionin the
1 validation error detected:envelope and reports an empty
ConditionExpressionbefore it. UpdateItemand PartiQLUPDATErefuse aSET,REMOVE,ADDorDELETEwhose path has a missing or wrong-typed parent:The document path provided in the update expression is invalid for update. PartiQLSET zz.y = 1no longer createszz.- Every clause of an update reads the item as it was before the update, in
UpdateItemand PartiQL:REMOVE l[0], l[1]removes both elements, andSET n = 7, s = n + 1givessthe oldnplus one. - PartiQL
UPDATErefuses overlapping paths (Two document paths overlap with each other) and takes one path perREMOVEkeyword, soREMOVE a, bis a syntax error. SETarithmetic andlist_appendreturn DynamoDB's errors for a missing attribute, a stored value of the wrong type and a value of the wrong type. A PartiQLSET n = zz + 1on a missingzzis refused, not read as zero.ADDandDELETEname the operand type in full with atypeSet; the others use the short code (S,N,L).- PartiQL
+,-andlist_appendtake an attribute on either side, and a quoted string is a string. - An update that appends past the end of a list reads every other index on that list as the list was: an
ADD,DELETEorREMOVEat an index the list did not reach leaves the appended element alone, and the appends land in index order, PartiQL's included. ADELETEthat empties a set held in a list element takes the element out at its original index. Scanchecks its request members as the newer validation layer does, which 20 of 33 regions now run:Limit,Segment,TotalSegmentsand an emptyTableNamecome first in the newer wording, then the table-name format andReturnConsumedCapacity, thenSelect, with anExclusiveStartKeymismatch answered asThe provided starting key is invalid(#254).ScanrefusesSelectofCOUNT,ALL_ATTRIBUTESorALL_PROJECTED_ATTRIBUTESbesideAttributesToGet, and answers an empty attribute name in anExclusiveStartKeyor an expression value bare.Scanwords a bad number or a repeated set member in an attribute value as the newer layer does, with noExpressionAttributeValues contains invalid valuewrapper.ScanreadsLimit,SegmentandTotalSegmentsas 32-bit integers that saturate and drop a fraction, soLimit4294967296 is accepted and a negativeTotalSegmentsis a validation error rather than a decoding one. It refuses aSegmentabove 999999, checksSelectofALL_ATTRIBUTESagainst a keys-only index ahead of theExclusiveStartKey, and on a table with indexes reports anExclusiveStartKeyattribute of the wrong type as an index key mismatch.QueryrefusesSelectofCOUNT,ALL_ATTRIBUTESorALL_PROJECTED_ATTRIBUTESbesideAttributesToGet, puts itsCannot specify ...andALL_PROJECTED_ATTRIBUTESmessages in the1 validation error detected:envelope, readsLimitasScandoes, and answers an index key of the wrong type in anExclusiveStartKeywithThe provided starting key is invalid. With noTableNameit reports a badReturnConsumedCapacitybeside the missing name.- A parallel
Scanof a table withTotalSegmentsabove 4096 returns each item once. The segments past the last hash bucket used to return almost every item. - A parallel
Scanthat deletes each page as it goes sees its whole segment. Once the item itsExclusiveStartKeynamed was deleted, the next page used to come back empty, with noLastEvaluatedKey.