You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Pull parentReferences maintenance rules from PR #480
Imports the parentReferences bookkeeping spec from PR #480 [1] onto
this integration branch, resolving the committed conflict marker at
RTLO3f and the duplicate clause IDs introduced by the import.
Imported from #480 verbatim:
- RTO5c10: post-sync rebuild of every parentReferences map.
- addParentReference and removeParentReference internal methods,
with set-merge / set-remove / empty-set-delete semantics.
- Tombstone-time children walk for LiveMap, stripping parent
references from each referenced child before the data is cleared.
- MAP_SET, MAP_REMOVE and MAP_CLEAR parent-reference maintenance
(RTLM7a3, RTLM7i, RTLM8a3, RTLM24e1c).
- IDL declarations for the two new internal methods.
The Primitive type alias added in #480 was deliberately not
imported, as it is unrelated to the parentReferences work.
Conflicts reconciled:
- The committed <<<<<<< / >>>>>>> block at RTLO3f. Kept the
objectId-keyed Dict<String, Set<String>> description from this
branch (consistent with #480's own IDL line and its set-style
manipulation contracts; the alternative half mandated a specific
in-memory representation that ably-js does not match literally).
The "set to an empty map on initialisation" clause from #480 was
moved to RTLO3f2; the prior RTLO3f2 TODO is deleted, since the
imported maintenance rules now resolve it. RTO5c10a's
back-reference was updated to point at the new RTLO3f2.
- Duplicate clause IDs introduced by #480 were renamed per the
"rename the later addition" convention in CONTRIBUTING.md:
- addParentReference: RTLO4f -> RTLO4g
- removeParentReference: RTLO4g -> RTLO4h
- tombstone children walk: RTLO4e5* -> RTLO4e9*
All cross-references to the renamed clauses were updated
accordingly. The pre-existing RTLO4f (getFullPaths) and
RTLO4e5-e8 (Compute LiveObjectUpdate through Return) are
untouched.
Linter passes. Still needs human review.
[1] #480
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: specifications/objects-features.md
+30-1Lines changed: 30 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -174,6 +174,10 @@ Objects feature enables clients to store shared data as "objects" on a channel.
174
174
-`(RTO5c1b1c)` This clause has been deleted (redundant to [RTO5f3](#RTO5f3)).
175
175
-`(RTO5c2)` Remove any objects from the internal `ObjectsPool` for which `objectId`s were not received during the sync sequence
176
176
-`(RTO5c2a)` The object with ID `root` must not be removed from `ObjectsPool`, as per [RTO3b](#RTO3b)
177
+
-`(RTO5c10)` After re-establishing the `ObjectsPool` per [RTO5c1](#RTO5c1) and [RTO5c2](#RTO5c2), the client MUST rebuild every `parentReferences` map ([RTLO3f](#RTLO3f)). Specifically:
178
+
-`(RTO5c10a)` For each `LiveObject` in the internal `ObjectsPool` ([RTO3](#RTO3)), reset its `parentReferences` to an empty map as defined in [RTLO3f2](#RTLO3f2)
179
+
-`(RTO5c10b)` After [RTO5c10a](#RTO5c10a) has completed, for each `LiveMap` in the internal `ObjectsPool`, iterate its `LiveMap#entries` as per [RTLM11](#RTLM11)
180
+
-`(RTO5c10b1)` For each iterated entry whose value type is `LiveObject`, call `addParentReference(parent, key)` on the `LiveObject` (per [RTLO4g](#RTLO4g)), passing the iterated `LiveMap` as `parent` and the iterated entry key as `key`
177
181
-`(RTO5c7)` For each previously existing object that was updated as a result of [RTO5c1a](#RTO5c1a), emit the corresponding stored `LiveObjectUpdate` object from [RTO5c1a2](#RTO5c1a2)
178
182
-`(RTO5c6)``ObjectMessages` stored in the `bufferedObjectOperations` list are applied as described in [RTO9](#RTO9), passing `source` as `CHANNEL`
179
183
-`(RTO5c3)` Clear any stored sync sequence identifiers and cursor values
@@ -325,7 +329,7 @@ Objects feature enables clients to store shared data as "objects" on a channel.
325
329
-`(RTLO3e1)` Set to undefined/null when the `LiveObject` is initialized
326
330
-`(RTLO3f)` protected `parentReferences``Dict<String, Set<String>>` - tracks which `LiveMap`s in the local `ObjectsPool` currently reference this `LiveObject`, and at which keys they do so. The mapping is keyed by the parent `LiveMap`'s `objectId`, with each value being the set of keys at which that `LiveMap` references this `LiveObject`. Used by `getFullPaths` ([RTLO4f](#RTLO4f)) to determine every path the object currently occupies in the LiveObjects tree
327
331
-`(RTLO3f1)` This mapping is keyed by `objectId` for consistency with the rest of the LiveObjects spec, where references between objects are stored as `objectId`s and resolved via the `ObjectsPool` on demand. Implementations may store a direct reference to the parent `LiveMap` instead — for example to avoid an `ObjectsPool` lookup at each step of `getFullPaths` ([RTLO4f](#RTLO4f)) traversal — provided the observable behaviour is unchanged. Such implementations should be aware that this may introduce reference cycles between `LiveMap`s, and must ensure this does not cause memory leaks
328
-
-`(RTLO3f2)`TODO: The detailed maintenance rules for `parentReferences` (across `MAP_SET`, `MAP_REMOVE`, `MAP_CLEAR`, `LiveMap` tombstoning, and post-sync rebuild) are to be specified by Sachin in a follow-up; see https://github.com/ably/specification/pull/480 for the in-progress draft
332
+
-`(RTLO3f2)`Set to an empty map when the `LiveObject` is initialized
329
333
-`(RTLO4)``LiveObject` methods:
330
334
-`(RTLO4b)``subscribe` - subscribes a user to data updates on this `LiveObject` instance
331
335
-`(RTLO4b1)` Requires the `OBJECT_SUBSCRIBE` channel mode to be granted per [RTO2](#RTO2)
@@ -362,6 +366,13 @@ Objects feature enables clients to store shared data as "objects" on a channel.
362
366
-`(RTLO4a4)` Get the `siteSerial` value stored for this `LiveObject` in the `siteTimeserials` map using the key `ObjectMessage.siteCode`
363
367
-`(RTLO4a5)` If the `siteSerial` for this `LiveObject` is null or an empty string, return true
364
368
-`(RTLO4a6)` If the `siteSerial` for this `LiveObject` is not an empty string, return true if `ObjectMessage.serial` is greater than `siteSerial` when compared lexicographically
369
+
-`(RTLO4g)` internal `addParentReference(parent, key)` method - records that the `LiveMap``parent` references this `LiveObject` at `key`
370
+
-`(RTLO4g1)` If `parent` is already present in `parentReferences`, `key` MUST be added to the existing set associated with `parent`
371
+
-`(RTLO4g2)` Otherwise, a new entry MUST be inserted into `parentReferences` for `parent` with a set containing only `key`
372
+
-`(RTLO4h)` internal `removeParentReference(parent, key)` method - removes the recorded reference from `parent` at `key`
373
+
-`(RTLO4h1)` If `parent` is not present in `parentReferences`, the call MUST be a no-op
374
+
-`(RTLO4h2)` Otherwise, `key` MUST be removed from the set associated with `parent`
375
+
-`(RTLO4h3)` If, as a result of [RTLO4h2](#RTLO4h2), the set associated with `parent` is empty, the `parent` entry MUST be removed from `parentReferences`
365
376
-`(RTLO4e)` protected `tombstone` - a convenience method used to tombstone this `LiveObject`. The realtime system reserves the right to tombstone an object (i.e. mark it for deletion from the objects pool) by publishing an `OBJECT_DELETE` operation at any time if the object is orphaned (not a descendant of the root object) or remains uninitialized (no `*_CREATE` operation has been received) for an extended period. Only the realtime system may publish an `OBJECT_DELETE` operation; clients must never send it. This method describes the steps the client library must take when it needs to tombstone an object locally. Eventually, tombstoned objects will be garbage collected following the procedure described in [RTO10](#RTO10)
366
377
-`(RTLO4e1)` Expects the following arguments:
367
378
-`(RTLO4e1a)``ObjectMessage`
@@ -370,6 +381,10 @@ Objects feature enables clients to store shared data as "objects" on a channel.
370
381
-`(RTLO4e3a)` This clause has been replaced by [RTLO6a](#RTLO6a)
371
382
-`(RTLO4e3b)` This clause has been replaced by [RTLO6b](#RTLO6b)
372
383
-`(RTLO4e3b1)` This clause has been replaced by [RTLO6b1](#RTLO6b1)
384
+
-`(RTLO4e9)` If the current `LiveObject` is of type `LiveMap`, then before [RTLO4e4](#RTLO4e4) is applied, do following:
385
+
-`(RTLO4e9a)` For each iterated `entry` in current `LiveMap`'s private `data`:
386
+
-`(RTLO4e9a1)` If `entry.value.data` have `objectId` as a field, retrieve corresponding child `LiveObject` from `ObjectsPool` using given `objectId`
387
+
-`(RTLO4e9a2)` If child `LiveObject` exists, call its `removeParentReference(parent, key)` method per [RTLO4h](#RTLO4h), passing current `LiveMap` as `parent` and the iterated `entry.value` as `key`
373
388
-`(RTLO4e4)` Set the `data` attribute of the `LiveObject` to the value described in [RTLC4b](#RTLC4b) or [RTLM4c](#RTLM4c), depending on the object type
374
389
-`(RTLO4e5)` Compute a `LiveObjectUpdate` representing the data change resulting from this `LiveObject` being tombstoned, by calculating the diff between the `data` value from before [RTLO4e4](#RTLO4e4) was applied (as `previousData`) and the current `data` value (as `newData`), per [RTLC14](#RTLC14) or [RTLM22](#RTLM22), depending on the object type
375
390
-`(RTLO4e6)` Set `LiveObjectUpdate.tombstone` to `true` on the object computed in [RTLO4e5](#RTLO4e5)
@@ -684,6 +699,9 @@ Objects feature enables clients to store shared data as "objects" on a channel.
684
699
-`(RTLM7h)` If the private `clearTimeserial` is non-null, and the provided `serial` is null or the `clearTimeserial` is lexicographically greater than or equal to `serial`, discard the operation without taking any action. Return a `LiveMapUpdate` object with `LiveMapUpdate.noop` set to `true`, indicating that no update was made to the object
685
700
-`(RTLM7a)` If an `ObjectsMapEntry` exists in the private `data` for the specified key:
686
701
-`(RTLM7a1)` If the operation cannot be applied to the existing entry as per [RTLM9](#RTLM9), discard the operation without taking any action. Return a `LiveMapUpdate` object with `LiveMapUpdate.noop` set to `true`, indicating that no update was made to the object
702
+
-`(RTLM7a3)` If the current `ObjectsMapEntry` is of type `LiveObject`, before [RTLM7a2](#RTLM7a2e) is applied, the parent reference recorded on existing `ObjectsMapEntry` MUST be removed:
703
+
-`(RTLM7a3a)` To check `ObjectsMapEntry` is of type `LiveObject`, validate `ObjectsMapEntry.data` has a `objectId` field, retrieve corresponding `LiveObject` from `ObjectsPool` using given `objectId`
704
+
-`(RTLM7a3b)` If `LiveObject` exists, call its `removeParentReference(parent, key)` method per [RTLO4h](#RTLO4h), passing this `LiveMap` as `parent` and the operation's key as `key`
687
705
-`(RTLM7a2)` Otherwise, apply the operation to the existing entry:
688
706
-`(RTLM7a2a)` This clause has been replaced by [RTLM7a2e](#RTLM7a2e) as of specification version 6.0.0.
689
707
-`(RTLM7a2e)` Set `ObjectsMapEntry.data` to the `MapSet.value`
@@ -699,6 +717,9 @@ Objects feature enables clients to store shared data as "objects" on a channel.
699
717
-`(RTLM7c1)` This clause has been replaced by [RTLM7g1](#RTLM7g1) as of specification version 6.0.0.
700
718
-`(RTLM7g)` If `MapSet.value.objectId` is non-empty:
701
719
-`(RTLM7g1)` Create a new `LiveObject` for this `objectId` in the internal `ObjectsPool` per [RTO6](#RTO6)
720
+
-`(RTLM7i)` A parent reference MUST be recorded on the `LiveObject` newly referenced by this entry (if any):
721
+
-`(RTLM7i1)` If `MapSet.value.objectId` is not present, no action is required
722
+
-`(RTLM7i2)` Otherwise, call `addParentReference(parent, key)` per [RTLO4g](#RTLO4g) on the `LiveObject` in the local `ObjectsPool` with `objectId` equal to `MapSet.value.objectId` (guaranteed to exist per [RTLM7g](#RTLM7g)), passing this `LiveMap` as `parent` and the operation's key as `key`
702
723
-`(RTLM7f)` Return a `LiveMapUpdate` object with a `LiveMapUpdate.update` map containing the key used in this operation set to `updated`, and `LiveMapUpdate.objectMessage` set to the provided `ObjectMessage`
703
724
-`(RTLM8)` A `MAP_REMOVE` operation for a key can be applied to a `LiveMap` in the following way:
704
725
-`(RTLM8c)` Expects the following arguments:
@@ -711,6 +732,9 @@ Objects feature enables clients to store shared data as "objects" on a channel.
711
732
-`(RTLM8g)` If the private `clearTimeserial` is non-null, and the provided `serial` is null or the `clearTimeserial` is lexicographically greater than or equal to `serial`, discard the operation without taking any action. Return a `LiveMapUpdate` object with `LiveMapUpdate.noop` set to `true`, indicating that no update was made to the object
712
733
-`(RTLM8a)` If an `ObjectsMapEntry` exists in the private `data` for the specified key:
713
734
-`(RTLM8a1)` If the operation cannot be applied to the existing entry as per [RTLM9](#RTLM9), discard the operation without taking any action. Return a `LiveMapUpdate` object with `LiveMapUpdate.noop` set to `true`, indicating that no update was made to the object
735
+
-`(RTLM8a3)` If the current `ObjectsMapEntry` is of type `LiveObject`, before [RTLM8a2](#RTLM8a2) is applied, the parent reference recorded on existing `ObjectsMapEntry` MUST be removed:
736
+
-`(RTLM8a3a)` To check `ObjectsMapEntry` is of type `LiveObject`, validate `ObjectsMapEntry.data` has a `objectId` field, retrieve corresponding `LiveObject` from `ObjectsPool` using given `objectId`
737
+
-`(RTLM8a3b)` If `LiveObject` exists, call its `removeParentReference(parent, key)` method per [RTLO4h](#RTLO4h), passing this `LiveMap` as `parent` and the operation's key as `key`
714
738
-`(RTLM8a2)` Otherwise, apply the operation to the existing entry:
715
739
-`(RTLM8a2a)` Set `ObjectsMapEntry.data` to undefined/null
716
740
-`(RTLM8a2b)` Set `ObjectsMapEntry.timeserial` to the provided `serial`
@@ -734,6 +758,9 @@ Objects feature enables clients to store shared data as "objects" on a channel.
734
758
-`(RTLM24d)` Set the private `clearTimeserial` to the provided `serial`
735
759
-`(RTLM24e)` For each `ObjectsMapEntry` in the internal `data`:
736
760
-`(RTLM24e1)` If `ObjectsMapEntry.timeserial` is null or omitted, or the `serial` is lexicographically greater than `ObjectsMapEntry.timeserial`:
761
+
-`(RTLM24e1c)` If the current `ObjectsMapEntry` is of type `LiveObject`, the parent reference recorded on existing `ObjectsMapEntry` MUST be removed:
762
+
-`(RTLM24e1c1)` To check `ObjectsMapEntry` is of type `LiveObject`, validate `ObjectsMapEntry.data` has a `objectId` field, retrieve corresponding `LiveObject` from `ObjectsPool` using given `objectId`
763
+
-`(RTLM24e1c2)` If `LiveObject` exists, call its `removeParentReference(parent, key)` method per [RTLO4h](#RTLO4h), passing this `LiveMap` as `parent` and the iterated entry key as `key`
737
764
-`(RTLM24e1a)` Remove the entry from the internal `data` map. The entry is not retained as a tombstone.
738
765
-`(RTLM24e1b)` Record the key for the `LiveMapUpdate` as `removed`
739
766
-`(RTLM24f)` Return a `LiveMapUpdate` object with `LiveMapUpdate.update` containing each key recorded in [RTLM24e1b](#RTLM24e1b) set to `removed`, and `LiveMapUpdate.objectMessage` set to the provided `ObjectMessage`
@@ -1106,6 +1133,8 @@ Types and their properties/methods are public and exposed to users by default. A
0 commit comments