[AIT-30] BatchContext and batch API on PathObject and Instance#471
[AIT-30] BatchContext and batch API on PathObject and Instance#471lawrence-forooghian wants to merge 1 commit into
Conversation
035aef9 to
babc296
Compare
| - `(RTPO21b)` Returns a stream or iterable that yields `PathObjectSubscriptionEvent` objects, using the idiomatic construct for the language (e.g. async iterators, channels, flows, or async sequences) | ||
| - `(RTPO21c)` Internally wraps `PathObject#subscribe` ([RTPO19](#RTPO19)), converting the callback-based subscription into the appropriate streaming or iterable pattern | ||
| - `(RTPO22)` `PathObject#batch` function: | ||
| - `(RTPO22a)` Expects a synchronous function `fn` that receives a `BatchContext` as its argument |
There was a problem hiding this comment.
| - `(RTPO22a)` Expects a synchronous function `fn` that receives a `BatchContext` as its argument | |
| - `(RTPO22a)` Accepts a synchronous anonymous function as a argument. This anonymous function is called with `BatchContext` as its argument internally. |
There was a problem hiding this comment.
https://ably.com/docs/liveobjects/concepts/path-object#batch-multiple-updates
Similarly, other spec points might not be explicit, will do a full review soon
There was a problem hiding this comment.
Pull request overview
This PR extends the LiveObjects path-based API specification by introducing a batching mechanism for object mutations, enabling multiple map/counter operations to be queued and then published atomically as a single channel message.
Changes:
- Adds
PathObject#batchandInstance#batchspec points, describing lifecycle and permission requirements. - Introduces the
BatchContext/ internalRootBatchContextspecification for queuing and flushing batched operations. - Reserves the new
RTBCspec point prefix in the main features index.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| specifications/objects-features.md | Specifies batch APIs for PathObject/Instance and defines BatchContext + internal batching flush mechanics. |
| specifications/features.md | Updates reserved spec point prefixes to include RTBC. |
Comments suppressed due to low confidence (1)
specifications/objects-features.md:1004
- RTINS19 similarly specifies flush/close only after
fnreturns. Please define behavior whenfnthrows (and, where applicable, when an async function is passed): queued operations should not be flushed/published, but the RootBatchContext should still be closed so the BatchContext and any children become unusable and consistently throw the “batch is closed” error.
- `(RTINS19d)` Creates a `RootBatchContext` ([RTBC16](#RTBC16)) wrapping this `Instance`
- `(RTINS19e)` Executes `fn`, passing the `BatchContext` as argument
- `(RTINS19f)` After `fn` returns, flushes the `RootBatchContext` ([RTBC16d](#RTBC16d)) to publish all queued operations atomically
- `(RTINS19g)` The `RootBatchContext` is closed after flush completes, regardless of success or failure
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - `(RTPO22)` `PathObject#batch` function: | ||
| - `(RTPO22a)` Expects a synchronous function `fn` that receives a `BatchContext` as its argument | ||
| - `(RTPO22b)` Requires the `OBJECT_PUBLISH` channel mode to be granted per [RTO2](#RTO2) | ||
| - `(RTPO22c)` Resolves the path to a `LiveObject` using the internal path resolution procedure. If the path does not resolve to a `LiveObject`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007 |
| - `(RTPO22f)` After `fn` returns, flushes the `RootBatchContext` ([RTBC16d](#RTBC16d)) to publish all queued operations atomically | ||
| - `(RTPO22g)` The `RootBatchContext` is closed after flush completes, regardless of success or failure |
| - `(RTBC4)` `BatchContext#get` function: | ||
| - `(RTBC4a)` Expects a `key` `String` argument | ||
| - `(RTBC4b)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000 | ||
| - `(RTBC4c)` Delegates to `Instance#get` ([RTINS5](#RTINS5)) on the underlying `Instance`. If the result is undefined, returns undefined |
| - `(RTBC14a1)` `amount` `Number` (optional) - the amount by which to increment the counter value. Defaults to 1 | ||
| - `(RTBC14b)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000 | ||
| - `(RTBC14c)` If the wrapped value is not a `LiveCounter`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007 | ||
| - `(RTBC14d)` Queues a message constructor on the `RootBatchContext` that, when executed, creates an `ObjectMessage` for a `COUNTER_INC` operation in the same manner as `LiveCounter#increment` ([RTLC12](#RTLC12)) |
| - `(RTBC16a)` Maintains an internal `wrappedInstances` map that memoizes `BatchContext` wrappers by `objectId` | ||
| - `(RTBC16b)` Maintains an internal `queuedMessageConstructors` list of deferred message constructor functions. Some `ObjectMessages` require asynchronous I/O during construction (e.g. generating an `objectId` for nested value types), so message constructors are queued during synchronous batch method calls and executed on flush | ||
| - `(RTBC16c)` `wrapInstance` function: wraps an `Instance` in a `BatchContext`. If the `Instance` has an `objectId` and a wrapper for that `objectId` already exists in `wrappedInstances`, the existing wrapper is returned. Otherwise, a new `BatchContext` is created and stored in `wrappedInstances` | ||
| - `(RTBC16d)` `flush` function: closes the batch context, executes all queued message constructors, flattens the resulting `ObjectMessages` into a single array, and publishes them using `RealtimeObject#publish` ([RTO15](#RTO15)). If there are no queued messages, no publish is performed |
| - `(RTBC15a1)` `amount` `Number` (optional) - the amount by which to decrement the counter value. Defaults to 1 | ||
| - `(RTBC15b)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000 | ||
| - `(RTBC15c)` If the wrapped value is not a `LiveCounter`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007 | ||
| - `(RTBC15d)` Delegates to `BatchContext#increment` ([RTBC14](#RTBC14)) with the negated `amount` |
Review against ably-js implementationI've cross-checked this PR against the ably-js implementation at 🔴 Critical:
|
Follow-up: additional gaps surfaced after deeper cross-checkFollowing the initial review above, a closer pass over 🟠 Per-method write-API checks on
|
Note: This PR is based on #427.
Split from #427 to reduce the review burden of that PR (since we don't need to do the batch API for the initial implementation of the path-based API).