Skip to content

Commit 3608895

Browse files
VeskeRttypic
authored andcommitted
BatchContext and batch API on PathObject and Instance
1 parent 4ce9e5d commit 3608895

2 files changed

Lines changed: 104 additions & 1 deletion

File tree

specifications/features.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,7 @@ The threading and/or asynchronous model for each realtime library will vary by l
936936

937937
### RealtimeObject {#realtime-objects}
938938

939-
Reserved for `RealtimeObject` feature specification, see [objects-features](../objects-features). Reserved spec points: `RTO`, `RTLO`, `RTLC`, `RTLM`, `RTPO`, `RTINS`, `RTLCV`, `RTLMV`
939+
Reserved for `RealtimeObject` feature specification, see [objects-features](../objects-features). Reserved spec points: `RTO`, `RTLO`, `RTLC`, `RTLM`, `RTPO`, `RTINS`, `RTLCV`, `RTLMV`, `RTBC`
940940

941941
### RealtimeAnnotations {#realtime-annotations}
942942

specifications/objects-features.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,14 @@ A `PathObject` is obtained from `RealtimeObject#get` ([RTO23](#RTO23)), which re
931931
- `(RTPO21a1)` `options` `PathObjectSubscriptionOptions` (optional) - same options as `PathObject#subscribe` ([RTPO19b](#RTPO19b))
932932
- `(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)
933933
- `(RTPO21c)` Internally wraps `PathObject#subscribe` ([RTPO19](#RTPO19)), converting the callback-based subscription into the appropriate streaming or iterable pattern
934+
- `(RTPO22)` `PathObject#batch` function:
935+
- `(RTPO22a)` Expects a synchronous function `fn` that receives a `BatchContext` as its argument
936+
- `(RTPO22b)` Requires the `OBJECT_PUBLISH` channel mode to be granted per [RTO2](#RTO2)
937+
- `(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
938+
- `(RTPO22d)` Creates a `RootBatchContext` ([RTBC16](#RTBC16)) wrapping the resolved `Instance`
939+
- `(RTPO22e)` Executes `fn`, passing the `BatchContext` as argument
940+
- `(RTPO22f)` After `fn` returns, flushes the `RootBatchContext` ([RTBC16d](#RTBC16d)) to publish all queued operations atomically
941+
- `(RTPO22g)` The `RootBatchContext` is closed after flush completes, regardless of success or failure
934942

935943
### Instance
936944

@@ -1005,6 +1013,84 @@ An `Instance` holds a direct reference to a specific resolved `LiveObject` or pr
10051013
- `(RTINS18a)` If the wrapped value is not a `LiveObject`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007
10061014
- `(RTINS18b)` Returns a stream or iterable that yields `InstanceSubscriptionEvent` objects, using the idiomatic construct for the language (e.g. async iterators, channels, flows, or async sequences)
10071015
- `(RTINS18c)` Internally wraps `Instance#subscribe` ([RTINS16](#RTINS16)), converting the callback-based subscription into the appropriate streaming or iterable pattern
1016+
- `(RTINS19)` `Instance#batch` function:
1017+
- `(RTINS19a)` Expects a synchronous function `fn` that receives a `BatchContext` as its argument
1018+
- `(RTINS19b)` Requires the `OBJECT_PUBLISH` channel mode to be granted per [RTO2](#RTO2)
1019+
- `(RTINS19c)` If the wrapped value is not a `LiveObject`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007
1020+
- `(RTINS19d)` Creates a `RootBatchContext` ([RTBC16](#RTBC16)) wrapping this `Instance`
1021+
- `(RTINS19e)` Executes `fn`, passing the `BatchContext` as argument
1022+
- `(RTINS19f)` After `fn` returns, flushes the `RootBatchContext` ([RTBC16d](#RTBC16d)) to publish all queued operations atomically
1023+
- `(RTINS19g)` The `RootBatchContext` is closed after flush completes, regardless of success or failure
1024+
1025+
### BatchContext
1026+
1027+
A `BatchContext` wraps an `Instance` with synchronous write methods that queue operations instead of sending them immediately. All queued operations are published atomically as a single channel message when the batch function returns. This allows multiple mutations to be grouped into a single publish call.
1028+
1029+
- `(RTBC1)` The `BatchContext` class provides a synchronous write interface for grouping multiple mutations into a single atomic publish
1030+
- `(RTBC1a)` A specific SDK implementation may choose to expose a subset of the methods available on the `BatchContext` class based on the known underlying type, in a similar manner to `Instance` ([RTINS1a](#RTINS1a))
1031+
- `(RTBC2)` `BatchContext` has the following internal properties:
1032+
- `(RTBC2a)` `instance` - the underlying `Instance` that this `BatchContext` wraps
1033+
- `(RTBC2b)` `rootContext` - a reference to the `RootBatchContext` ([RTBC16](#RTBC16)) that manages the batch operation
1034+
- `(RTBC3)` `BatchContext#id` property:
1035+
- `(RTBC3a)` Returns the `objectId` of the underlying `Instance` ([RTINS3](#RTINS3))
1036+
- `(RTBC3b)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000, indicating that the batch is closed
1037+
- `(RTBC4)` `BatchContext#get` function:
1038+
- `(RTBC4a)` Expects a `key` `String` argument
1039+
- `(RTBC4b)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1040+
- `(RTBC4c)` Delegates to `Instance#get` ([RTINS5](#RTINS5)) on the underlying `Instance`. If the result is undefined, returns undefined
1041+
- `(RTBC4d)` Otherwise, wraps the resulting `Instance` in a `BatchContext` via the `RootBatchContext#wrapInstance` ([RTBC16c](#RTBC16c)) and returns it
1042+
- `(RTBC5)` `BatchContext#value` function:
1043+
- `(RTBC5a)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1044+
- `(RTBC5b)` Delegates to `Instance#value` ([RTINS4](#RTINS4)) on the underlying `Instance`
1045+
- `(RTBC6)` `BatchContext#entries` function:
1046+
- `(RTBC6a)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1047+
- `(RTBC6b)` Delegates to `Instance#entries` ([RTINS6](#RTINS6)) on the underlying `Instance`, wrapping each yielded `Instance` value in a `BatchContext` via `RootBatchContext#wrapInstance` ([RTBC16c](#RTBC16c))
1048+
- `(RTBC6c)` Yields `[String, BatchContext]` pairs
1049+
- `(RTBC7)` `BatchContext#keys` function:
1050+
- `(RTBC7a)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1051+
- `(RTBC7b)` Delegates to `Instance#keys` ([RTINS7](#RTINS7)) on the underlying `Instance`
1052+
- `(RTBC8)` `BatchContext#values` function:
1053+
- `(RTBC8a)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1054+
- `(RTBC8b)` Delegates to `Instance#values` ([RTINS8](#RTINS8)) on the underlying `Instance`, wrapping each yielded `Instance` in a `BatchContext` via `RootBatchContext#wrapInstance` ([RTBC16c](#RTBC16c))
1055+
- `(RTBC9)` `BatchContext#size` function:
1056+
- `(RTBC9a)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1057+
- `(RTBC9b)` Delegates to `Instance#size` ([RTINS9](#RTINS9)) on the underlying `Instance`
1058+
- `(RTBC10)` `BatchContext#compact` function:
1059+
- `(RTBC10a)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1060+
- `(RTBC10b)` Delegates to `Instance#compact` ([RTINS10](#RTINS10)) on the underlying `Instance`
1061+
- `(RTBC11)` `BatchContext#compactJson` function:
1062+
- `(RTBC11a)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1063+
- `(RTBC11b)` Delegates to `Instance#compactJson` ([RTINS11](#RTINS11)) on the underlying `Instance`
1064+
- `(RTBC12)` `BatchContext#set` function. This method is synchronous:
1065+
- `(RTBC12a)` Expects the following arguments:
1066+
- `(RTBC12a1)` `key` `String` - the key to set the value for
1067+
- `(RTBC12a2)` `value` `Boolean | Binary | Number | String | JsonArray | JsonObject | LiveCounterValueType | LiveMapValueType` - the value to assign to the key
1068+
- `(RTBC12b)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1069+
- `(RTBC12c)` If the wrapped value is not a `LiveMap`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007
1070+
- `(RTBC12d)` Queues a message constructor on the `RootBatchContext` that, when executed, creates `ObjectMessages` for a `MAP_SET` operation in the same manner as `LiveMap#set` ([RTLM20e](#RTLM20e))
1071+
- `(RTBC13)` `BatchContext#remove` function. This method is synchronous:
1072+
- `(RTBC13a)` Expects a `key` `String` argument
1073+
- `(RTBC13b)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1074+
- `(RTBC13c)` If the wrapped value is not a `LiveMap`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007
1075+
- `(RTBC13d)` Queues a message constructor on the `RootBatchContext` that, when executed, creates an `ObjectMessage` for a `MAP_REMOVE` operation in the same manner as `LiveMap#remove` ([RTLM21e](#RTLM21e))
1076+
- `(RTBC14)` `BatchContext#increment` function. This method is synchronous:
1077+
- `(RTBC14a)` Expects the following arguments:
1078+
- `(RTBC14a1)` `amount` `Number` (optional) - the amount by which to increment the counter value. Defaults to 1
1079+
- `(RTBC14b)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1080+
- `(RTBC14c)` If the wrapped value is not a `LiveCounter`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007
1081+
- `(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))
1082+
- `(RTBC15)` `BatchContext#decrement` function. This method is synchronous:
1083+
- `(RTBC15a)` Expects the following arguments:
1084+
- `(RTBC15a1)` `amount` `Number` (optional) - the amount by which to decrement the counter value. Defaults to 1
1085+
- `(RTBC15b)` If the batch is closed, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000
1086+
- `(RTBC15c)` If the wrapped value is not a `LiveCounter`, the library must throw an `ErrorInfo` error with `statusCode` 400 and `code` 92007
1087+
- `(RTBC15d)` Delegates to `BatchContext#increment` ([RTBC14](#RTBC14)) with the negated `amount`
1088+
- `(RTBC16)` Internal `RootBatchContext` - manages the lifecycle and message queue for a batch operation:
1089+
- `(RTBC16a)` Maintains an internal `wrappedInstances` map that memoizes `BatchContext` wrappers by `objectId`
1090+
- `(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
1091+
- `(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`
1092+
- `(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
1093+
- `(RTBC16e)` After the batch is closed, any method call on the `BatchContext` or its children must throw an `ErrorInfo` error with `statusCode` 400 and `code` 40000, indicating that the batch is closed
10081094

10091095
## Interface Definition {#idl}
10101096

@@ -1107,6 +1193,7 @@ Types and their properties/methods are public and exposed to users by default. A
11071193
subscribe((PathObjectSubscriptionEvent) -> listener, PathObjectSubscriptionOptions? options) -> Subscription // RTPO19
11081194
unsubscribe((PathObjectSubscriptionEvent) -> listener) // RTPO20
11091195
subscribeIterator(PathObjectSubscriptionOptions? options) -> Stream<PathObjectSubscriptionEvent> // RTPO21
1196+
batch(((BatchContext) ->) fn) => io // RTPO22
11101197

11111198
class Instance: // RTINS*
11121199
id: String? // RTINS3
@@ -1125,3 +1212,19 @@ Types and their properties/methods are public and exposed to users by default. A
11251212
subscribe((InstanceSubscriptionEvent) -> listener) -> Subscription // RTINS16
11261213
unsubscribe((InstanceSubscriptionEvent) -> listener) // RTINS17
11271214
subscribeIterator() -> Stream<InstanceSubscriptionEvent> // RTINS18
1215+
batch(((BatchContext) ->) fn) => io // RTINS19
1216+
1217+
class BatchContext: // RTBC*
1218+
id: String? // RTBC3
1219+
get(String key) -> BatchContext? // RTBC4
1220+
value() -> (Boolean | Binary | Number | String | JsonArray | JsonObject)? // RTBC5
1221+
entries() -> Iterator<[String, BatchContext]> // RTBC6
1222+
keys() -> Iterator<String> // RTBC7
1223+
values() -> Iterator<BatchContext> // RTBC8
1224+
size() -> Number? // RTBC9
1225+
compact() -> Object? // RTBC10
1226+
compactJson() -> Object? // RTBC11
1227+
set(String key, (Boolean | Binary | Number | String | JsonArray | JsonObject | LiveCounterValueType | LiveMapValueType) value) // RTBC12
1228+
remove(String key) // RTBC13
1229+
increment(Number amount?) // RTBC14
1230+
decrement(Number amount?) // RTBC15

0 commit comments

Comments
 (0)