The Dynoxide hexagonal logo centred on a near-black background

Dynoxide 1.4.0: clean shutdowns and JSON Lines import

Dynoxide 1.4.0 is a smaller release than 1.3.0. There's a community contribution, a new way to stop an embedded server, and another round of fixes that bring updates and scans closer to DynamoDB.

JSON Lines import, thanks to Sam Chung

Sam Chung (@samchungy) was testing import, noticed that the .json files it expects are really JSON Lines, and renamed them to .jsonl to get editor support, then added support for them.

dynoxide import now reads .jsonl and .jsonl.gz as well as .json and .json.gz. The files can sit in a DynamoDB export layout (<dir>/<TableName>/data/) or in a flat directory.

Stopping an embedded server

If you run Dynoxide's HTTP server inside your own Rust code, the only way to stop it used to be aborting its task. That left connection tasks holding the database, so a file-backed database's files stayed open, and Windows won't delete an open file.

dynoxide-rs 3.1.0 adds server::serve_with_shutdown, which takes a future as its stop signal. Once that completes, the server stops taking new connections, closes idle keep-alive ones, waits for running requests and returns:

let (stop, stopped) = tokio::sync::oneshot::channel::<()>();
let server = tokio::spawn(dynoxide::server::serve_with_shutdown(listener, db, async {
    let _ = stopped.await;
}));

// ... run your tests against the server ...

let _ = stop.send(());
server.await??;

The last connection can let go of the database a moment after the call returns, so delete files with a short retry. The doc comment covers that and a couple of other caveats. dynoxide start runs on this now too, with Ctrl-C and SIGTERM working as before.

Closer to DynamoDB

Most of the rest is parity work on UpdateItem, PartiQL UPDATE, Scan and Query. The changes most likely to show up in a test:

  • An update path needs its parent. PartiQL SET zz.y = 1 used to create zz. It's now refused, as DynamoDB refuses it.
  • Every clause reads the item as it was. REMOVE l[0], l[1] removes both elements, and SET n = 7, s = n + 1 gives s the old n plus one.
  • A missing attribute isn't zero. Arithmetic and list_append on a missing or wrong-typed operand now return DynamoDB's error.
  • Scan validation matches the newer DynamoDB. The validation rollout I caught in June has reached 20 of the 33 AWS regions, and Dynoxide's Scan now follows its error order and wording (#254).

There were also two parallel Scan bugs that returned the wrong items:

  • TotalSegments above 4,096 returned almost every item from the segments past the last hash bucket. Each item now comes back once.
  • A scan that deletes each page as it goes could get an empty page with no LastEvaluatedKey once its start key's item had been deleted. It now sees its whole segment.

The release notes also list rustls and rustls-webpki updates for published advisories. Those only reach Dynoxide through test dependencies, so nothing shipped is affected.

Before upgrading

  • The Rust API validates like the HTTP one. Database::scan and Database::query now refuse a Limit of 0, and scan refuses an out-of-range Select, ReturnConsumedCapacity or Segment. An empty UpdateExpression comes back as DynoxideError::EnvelopedValidation rather than ValidationException, with the same message.
  • dynoxide mcp refuses --max-items 0 at startup, and its scan and query tools refuse a limit of 0.
  • The crate is 3.1.0. The product is 1.4.0, and dynoxide-rs = "3.1" picks up the new function.

The 1.4.0 release notes have the full list. For an npm project, npm install --save-dev dynoxide@1.4.0 pins this release.