Skip to content

Latest commit

 

History

History
282 lines (265 loc) · 30.4 KB

File metadata and controls

282 lines (265 loc) · 30.4 KB
title Objects Features
section client-lib-development-guide
index 65
jump_to
Help with
Objects Features Overview#overview

Overview

This document outlines the feature specification for the Objects feature of the Realtime system. It is currently under development and stored separately from the main specification to simplify the initial implementation of the feature in other SDKs. Once completed, it will be moved to the main features spec.

Objects feature enables clients to store shared data as “objects” on a channel. When an object is updated, changes are automatically propagated to all subscribed clients in realtime, ensuring each client always sees the latest state.

RealtimeObjects

  • (RTO1) RealtimeObjects#getRoot function:
    • (RTO1a) Requires the OBJECT_SUBSCRIBE channel mode to be granted per RTO2
    • (RTO1b) If the channel is in the DETACHED or FAILED state, the library should throw an ErrorInfo error with statusCode 400 and code 90001
    • (RTO1c) Waits for the objects sync sequence to complete and for RTO5c to finish
    • (RTO1d) Returns the object with id root from the internal ObjectsPool as a LiveMap
  • (RTO2) Certain object operations may require a specific channel mode to be set on a channel in order to be performed. If a specific channel mode is required by an operation, then:
    • (RTO2a) If the channel is in the ATTACHED state, the presence of the required channel mode is checked against the set of channel modes granted by the server per RTL4m :
      • (RTO2a1) If the channel mode is in the set, the operation is allowed
      • (RTO2a2) If the channel mode is missing, unless otherwise specified by the operation, the library should throw an ErrorInfo error with statusCode 400 and code 40024, indicating that the operation cannot be performed without the required channel mode
    • (RTO2b) Otherwise, a best-effort attempt is made, and the channel mode is checked against the set of channel modes requested by the user per TB2d :
      • (RTO2b1) If the channel mode is in the set, the operation is allowed
      • (RTO2b2) If the channel mode is missing, unless otherwise specified by the operation, the library should throw an ErrorInfo error with statusCode 400 and code 40024, indicating that the operation cannot be performed without the required channel mode
  • (RTO3) An internal ObjectsPool should be used to maintain the list of objects present on a channel
    • (RTO3a) ObjectsPool is a Dict<String, LiveObject> – a map of @LiveObject@s keyed by objectId string
    • (RTO3b) It must always contain a LiveMap object with id root
      • (RTO3b1) Upon initialization of the ObjectsPool, create a new LiveMap (per RTLM4) with objectId set to root and add it to the ObjectsPool
  • (RTO4) When a channel ATTACHED ProtocolMessage is received, the ProtocolMessage may contain a HAS_OBJECTS bit flag indicating that it will perform an objects sync, see TR3 . Note that this does not imply that objects are definitely present on the channel, only that there may be; the OBJECT_SYNC message may be empty
    • (RTO4a) If the HAS_OBJECTS flag is 1, the server will shortly perform an OBJECT_SYNC sequence as described in RTO5
    • (RTO4b) If the HAS_OBJECTS flag is 0 or there is no flags field, the sync sequence must be considered complete immediately, and the client library must perform the following actions in order:
      • (RTO4b1) All objects except the one with id root must be removed from the internal ObjectsPool
      • (RTO4b2) The data for the LiveMap with id root must be cleared by setting it to a zero-value per RTLM4. Note that the client SDK must not create a new LiveMap instance with id root; it must only clear the internal data of the existing LiveMap with id root
      • (RTO4b3) The SyncObjectsPool list must be cleared
      • (RTO4b5) The BufferedObjectOperations list must be cleared
      • (RTO4b4) Perform the actions for objects sync completion as described in RTO5c
  • (RTO5) The realtime system reserves the right to initiate an objects sync of the objects on a channel at any point once a channel is attached. A server initiated objects sync provides Ably with a means to send a complete list of objects present on the channel at any point
    • (RTO5d) If an OBJECT_SYNC ProtocolMessage is received and ObjectMessage.object is null or omitted, the client library should skip processing that ProtocolMessage
    • (RTO5a) When an OBJECT_SYNC ProtocolMessage is received with a channel attribute matching the channel name, the client library must parse the channelSerial attribute:
      • (RTO5a1) The channelSerial is used as the sync cursor and is a two-part identifier: <sequence id>:<cursor value>
      • (RTO5a2) If a new sequence id is sent from Ably, the client library must treat it as the start of a new objects sync sequence, and any previous in-flight sync must be discarded:
        • (RTO5a2a) The SyncObjectsPool list must be cleared
        • (RTO5a2b) The BufferedObjectOperations list must be cleared
      • (RTO5a3) If the sequence id matches the previously received sequence id, the client library should continue the sync process
      • (RTO5a4) The objects sync sequence for that sequence identifier is considered complete once the cursor is empty; that is when the channelSerial looks like <sequence id>:
      • (RTO5a5) An OBJECT_SYNC may also be sent with no channelSerial attribute. In this case, the sync data is entirely contained within the ProtocolMessage
    • (RTO5b) During the sync sequence, the ObjectMessage.object values from incoming OBJECT_SYNC ProtocolMessages must be temporarily stored in the internal SyncObjectsPool list
    • (RTO5c) When the objects sync has completed, the client library must perform the following actions in order:
      • (RTO5c1) For each ObjectState in the SyncObjectsPool list:
        • (RTO5c1a) If an object with ObjectState.objectId exists in the internal ObjectsPool:
          • (RTO5c1a1) Replace the internal data for the object as described in RTLC6 or RTLM6 depending on the object type, passing in current ObjectState
        • (RTO5c1b) If an object with ObjectState.objectId does not exist in the internal ObjectsPool:
          • (RTO5c1b1) Create a new LiveObject using the data from ObjectState and add it to the internal ObjectsPool:
            • (RTO5c1b1a) If ObjectState.counter is present, create a zero-value LiveCounter (per RTLC4), set its private objectId equal to ObjectState.objectId and replace its internal data using the current ObjectState per RTLC6
            • (RTO5c1b1b) If ObjectState.map is present, create a zero-value LiveMap (per RTLM4), set its private objectId equal to ObjectState.objectId, set its private semantics equal to ObjectState.map.semantics and replace its internal data using the current ObjectState per RTLM6
            • (RTO5c1b1c) Otherwise, log a warning that an unsupported object state message has been received, and discard the current ObjectState without taking any action
      • (RTO5c2) Remove any objects from the internal ObjectsPool for which @objectId@s were not received during the sync sequence
        • (RTO5c2a) The object with ID root must not be removed from ObjectsPool, as per RTO3b
      • (RTO5c6) ObjectMessages stored in the BufferedObjectOperations list are applied as described in RTO9
      • (RTO5c3) Clear any stored sync sequence identifiers and cursor values
      • (RTO5c4) The SyncObjectsPool must be cleared
      • (RTO5c5) The BufferedObjectOperations list must be cleared
  • (RTO6) Certain object operations may require creating a zero-value object if one does not already exist in the internal ObjectsPool for the given objectId. This can be done as follows:
    • (RTO6a) If an object with objectId exists in ObjectsPool, do not create a new object
    • (RTO6b) The expected type of the object can be inferred from the provided objectId:
      • (RTO6b1) Split the objectId (formatted as type:hash&#64;timestamp) on the separator : and parse the first part as the type string
      • (RTO6b2) If the parsed type is map, create a zero-value LiveMap per RTLM4 in the ObjectsPool
      • (RTO6b3) If the parsed type is counter, create a zero-value LiveCounter per RTLC4 in the ObjectsPool
  • (RTO7) The client library may receive OBJECT ProtocolMessages in realtime over the channel concurrently with OBJECT_SYNC ProtocolMessages during the object sync sequence (RTO5). Some of the incoming OBJECT messages may have already been applied to the objects described in the sync sequence, while others may not. Therefore, the client must buffer OBJECT messages during the sync sequence so that it can determine which of them should be applied to the objects once the sync is complete. See RTO8
    • (RTO7a) An internal BufferedObjectOperations should be used to store the buffered ObjectMessages, as described in RTO8a. BufferedObjectOperations is an array of ObjectMessage instances
      • (RTO7a1) This array is empty upon RealtimeObjects initialization
  • (RTO8) When the library receives a ProtocolMessage with an action of OBJECT, each member of the ProtocolMessage.state array (decoded into ObjectMessage objects) is passed to the RealtimeObjects instance per RTL1. Each ObjectMessage from OBJECT ProtocolMessage (also referred to as an OBJECT message) describes an operation to be applied to an object on a channel and must be handled as follows:
    • (RTO8a) If an object sync sequence is currently in progress, add the ObjectMessages to the internal BufferedObjectOperations array
    • (RTO8b) Otherwise, apply the ObjectMessages as described in RTO9
  • (RTO9) OBJECT messages can be applied to RealtimeObjects in the following way:
    • (RTO9a) For each ObjectMessage in the provided list:
      • (RTO9a1) If ObjectMessage.operation is null or omitted, log a warning indicating that an unsupported object operation message has been received, and discard the current ObjectMessage without taking any action
      • (RTO9a2) The ObjectMessage.operation.action field (see ObjectOperationAction) determines the type of operation to apply:
        • (RTO9a2a) If ObjectMessage.operation.action is one of the following: MAP_CREATE, MAP_SET, MAP_REMOVE, COUNTER_CREATE, COUNTER_INC, or OBJECT_DELETE, then:
          • (RTO9a2a1) If it does not already exist, create a zero-value LiveObject in the internal ObjectsPool per RTO6 using the objectId from ObjectMessage.operation.objectId
          • (RTO9a2a2) Get the LiveObject instance from the internal ObjectsPool using the objectId from ObjectMessage.operation.objectId
          • (RTO9a2a3) Apply the ObjectMessage.operation to the LiveObject; see RTLC7, RTLM15
        • (RTO9a2b) Otherwise, log a warning that an object operation message with an unsupported action has been received, and discard the current ObjectMessage without taking any action

LiveObject

  • (RTLO1) The LiveObject represents the common interface and includes shared functionality for concrete object types
  • (RTLO2) The client library may choose to implement LiveObject as an abstract class
  • (RTLO3) LiveObject properties:
    • (RTLO3a) protected objectId string – an Object ID for this object
      • (RTLO3a1) Must be provided and set in the constructor
    • (RTLO3b) protected siteTimeserials Dict<String, String> – a map of serials keyed by siteCode, representing the last operations applied to this object
      • (RTLO3b1) Set to an empty map when the LiveObject is initialized, so that any future operation can be applied to this object
    • (RTLO3c) protected createOperationIsMerged boolean – a flag indicating whether the corresponding MAP_CREATE or COUNTER_CREATE operation has been applied to this LiveObject instance
      • (RTLO3c1) Set to false when the LiveObject is initialized
  • (RTLO4) LiveObject methods:
    • (RTLO4a) protected canApplyOperation – a convenience method used to determine whether the ObjectMessage.operation should be applied to this object based on a serial value:
      • (RTLO4a1) Expects the following arguments:
        • (RTLO4a1a) ObjectMessage
      • (RTLO4a2) Returns a boolean indicating whether the operation should be applied to this object
      • (RTLO4a3) Both ObjectMessage.serial and ObjectMessage.siteCode must be non-empty strings. Otherwise, log a warning that the object operation message has invalid serial values. The client library must not apply this operation to the object
      • (RTLO4a4) Get the siteSerial value stored for this LiveObject in the siteTimeserials map using the key ObjectMessage.siteCode
      • (RTLO4a5) If the siteSerial for this LiveObject is null or an empty string, return true
      • (RTLO4a6) If the siteSerial for this LiveObject is not an empty string, return true if ObjectMessage.serial is greater than siteSerial when compared lexicographically

LiveCounter

  • (RTLC1) The LiveCounter extends LiveObject
  • (RTLC2) Represents the counter object type for Object IDs of type counter
  • (RTLC3) Holds a 64-bit floating-point number as a private data
  • (RTLC4) The zero-value LiveCounter is a LiveCounter with data set to 0
  • (RTLC5) LiveCounter#value function:
    • (RTLC5a) Requires the OBJECT_SUBSCRIBE channel mode to be granted per RTO2
    • (RTLC5b) If the channel is in the DETACHED or FAILED state, the library should throw an ErrorInfo error with statusCode 400 and code 90001
    • (RTLC5c) Returns the current data value
  • (RTLC6) @LiveCounter@’s internal data can be replaced with the provided ObjectState in the following way:
    • (RTLC6a) Replace the private siteTimeserials of the LiveCounter with the value from ObjectState.siteTimeserials
    • (RTLC6b) Set the private flag createOperationIsMerged to false
    • (RTLC6c) Set data to the value of ObjectState.counter.count, or to 0 if it does not exist
    • (RTLC6d) If ObjectState.createOp is present, merge the initial value into the LiveCounter as described in RTLC10, passing in the ObjectState.createOp instance
      • (RTLC6d1) This clause has been replaced by RTLC10a
      • (RTLC6d2) This clause has been replaced by RTLC10b
  • (RTLC7) An ObjectOperation from ObjectMessage.operation can be applied to a LiveCounter in the following way:
    • (RTLC7a) A client library may choose to implement this logic as a convenience method named applyOperation, which accepts an ObjectMessage instance with an existing ObjectMessage.operation object, with ObjectMessage.operation.objectId matching the Object ID of this LiveCounter. This ObjectMessage represents the operation to be applied to this LiveCounter
    • (RTLC7b) If ObjectMessage.operation cannot be applied based on the result of LiveObject.canApplyOperation, log a debug or trace message indicating that the operation cannot be applied because its serial value is not newer than the object’s, and discard the ObjectMessage without taking any action
    • (RTLC7c) Set the entry in the private siteTimeserials map at the key ObjectMessage.siteCode to equal ObjectMessage.serial
    • (RTLC7d) The ObjectMessage.operation.action field (see ObjectOperationAction) determines the type of operation to apply:
      • (RTLC7d1) If ObjectMessage.operation.action is set to COUNTER_CREATE, apply the operation as described in RTLC8, passing in ObjectMessage.operation
      • (RTLC7d2) If ObjectMessage.operation.action is set to COUNTER_INC, apply the operation as described in RTLC9, passing in ObjectMessage.operation.counterOp
      • (RTLC7d3) Otherwise, log a warning that an object operation message with an unsupported action has been received, and discard the current ObjectMessage without taking any action
  • (RTLC8) A COUNTER_CREATE operation can be applied to a LiveCounter in the following way:
    • (RTLC8a) Expects the following arguments:
      • (RTLC8a1) ObjectOperation
    • (RTLC8b) If the private flag createOperationIsMerged is true, log a debug or trace message indicating that the operation will not be applied because a COUNTER_CREATE operation has already been applied to this LiveCounter, and discard the operation without taking any further action
    • (RTLC8c) Otherwise merge the initial value into the LiveCounter as described in RTLC10, passing in the ObjectOperation instance
  • (RTLC9) A COUNTER_INC operation can be applied to a LiveCounter in the following way:
    • (RTLC9a) Expects the following arguments:
      • (RTLC9a1) ObjectsCounterOp
    • (RTLC9b) Add ObjectsCounterOp.amount to data, if it exists
  • (RTLC10) The initial value from ObjectOperation.counter can be merged into this LiveCounter in the following way:
    • (RTLC10a) Add ObjectOperation.counter.count to data, if it exists
    • (RTLC10b) Set the private flag createOperationIsMerged to true

LiveMap

  • (RTLM1) The LiveMap extends LiveObject
  • (RTLM2) Represents the map object type for Object IDs of type map
  • (RTLM3) Holds a Dict<String, ObjectsMapEntry> as a private data map
  • (RTLM4) The zero-value LiveMap is a LiveMap with data set to an empty map
  • (RTLM5) LiveMap#get function:
    • (RTLM5a) Accepts a key of type String
    • (RTLM5b) Requires the OBJECT_SUBSCRIBE channel mode to be granted per RTO2
    • (RTLM5c) If the channel is in the DETACHED or FAILED state, the library should throw an ErrorInfo error with statusCode 400 and code 90001
    • (RTLM5d) Returns the value from the current data at the specified key, as follows:
      • (RTLM5d1) If no ObjectsMapEntry exists at the key, return undefined/null
      • (RTLM5d2) If an ObjectsMapEntry exists at the key:
        • (RTLM5d2a) If ObjectsMapEntry.tombstone is true, return undefined/null
        • (RTLM5d2b) If ObjectsMapEntry.data.boolean exists, return it
        • (RTLM5d2c) If ObjectsMapEntry.data.bytes exists, return it
        • (RTLM5d2d) If ObjectsMapEntry.data.number exists, return it
        • (RTLM5d2e) If ObjectsMapEntry.data.string exists, return it
        • (RTLM5d2f) If ObjectsMapEntry.data.objectId exists, get the object stored at that objectId from the internal ObjectsPool:
          • (RTLM5d2f1) If an object with id objectId does not exist, return undefined/null
          • (RTLM5d2f2) If an object with id objectId exists, return it
        • (RTLM5d2g) Otherwise, return undefined/null
  • (RTLM10) LiveMap#size:
    • (RTLM10a) A method or property, depending on what is more idiomatic for the platform to use for a Map/Dictionary interface. For example, in JavaScript, this is a property similar to Map.size for the native Map class
    • (RTLM10b) Requires the OBJECT_SUBSCRIBE channel mode to be granted per RTO2
    • (RTLM10c) If the channel is in the DETACHED or FAILED state, the library should throw an ErrorInfo error with statusCode 400 and code 90001
    • (RTLM10d) Returns the number of non-tombstoned entries (per RTLM14) in the internal data map
  • (RTLM11) LiveMap#entries:
    • (RTLM11a) A method or property, depending on what is more idiomatic for the platform to use for a Map/Dictionary interface. For example, in JavaScript, this is a method similar to Map.entries() for the native Map class
    • (RTLM11b) Requires the OBJECT_SUBSCRIBE channel mode to be granted per RTO2
    • (RTLM11c) If the channel is in the DETACHED or FAILED state, the library should throw an ErrorInfo error with statusCode 400 and code 90001
    • (RTLM11d) Returns key-value pairs from the internal data map:
      • (RTLM11d1) Pairs with tombstoned entries (per RTLM14) are not returned
      • (RTLM11d3) ObjectsMapEntry values are mapped to user-facing values following the same procedure as in RTLM5d2
        • (RTLM11d3a) Note that if RTLM5d2 results in an ObjectsMapEntry being mapped to an undefined/null value, the corresponding key-value pair is still returned by this LiveMap#entries call
      • (RTLM11d2) The return type is idiomatic for the platform’s analogous Map/Dictionary interface operation. For example, in JavaScript, it returns a map iterator object like the one returned by Map.entries() method for the native Map class
  • (RTLM12) LiveMap#keys:
    • (RTLM12a) A method or property, depending on what is more idiomatic for the platform to use for a Map/Dictionary interface. For example, in JavaScript, this is a method similar to Map.keys() for the native Map class
    • (RTLM12b) The implementation is identical to LiveMap#entries, except that it returns only the keys from the internal data map
  • (RTLM13) LiveMap#values:
    • (RTLM13a) A method or property, depending on what is more idiomatic for the platform to use for a Map/Dictionary interface. For example, in JavaScript, this is a method similar to Map.values() for the native Map class
    • (RTLM13b) The implementation is identical to LiveMap#entries, except that it returns only the values from the internal data map
  • (RTLM14) An ObjectsMapEntry in the internal data map can be checked for being tombstoned using the convenience method:
    • (RTLM14a) The method returns true if ObjectsMapEntry.tombstone is true
    • (RTLM14b) Otherwise, it returns false
  • (RTLM6) LiveMap internal data can be replaced with the provided ObjectState in the following way:
    • (RTLM6a) Replace the private siteTimeserials of the LiveMap with the value from ObjectState.siteTimeserials
    • (RTLM6b) Set the private flag createOperationIsMerged to false
    • (RTLM6c) Set data to ObjectState.map.entries, or to an empty map if it does not exist
    • (RTLM6d) If ObjectState.createOp is present, merge the initial value into the LiveMap as described in RTLM17, passing in the ObjectState.createOp instance
      • (RTLM6d1) This clause has been replaced by RTLM17a
        • (RTLM6d1a) This clause has been replaced by RTLM17a1
        • (RTLM6d1b) This clause has been replaced by RTLM17a2
      • (RTLM6d2) This clause has been replaced by RTLM17b
  • (RTLM15) An ObjectOperation from ObjectMessage.operation can be applied to a LiveMap in the following way:
    • (RTLM15a) A client library may choose to implement this logic as a convenience method named applyOperation, which accepts an ObjectMessage instance with an existing ObjectMessage.operation object, with ObjectMessage.operation.objectId matching the Object ID of this LiveMap. This ObjectMessage represents the operation to be applied to this LiveMap
    • (RTLM15b) If ObjectMessage.operation cannot be applied based on the result of LiveObject.canApplyOperation, log a debug or trace message indicating that the operation cannot be applied because its serial value is not newer than the object’s, and discard the ObjectMessage without taking any action
    • (RTLM15c) Set the entry in the private siteTimeserials map at the key ObjectMessage.siteCode to equal ObjectMessage.serial
    • (RTLM15d) The ObjectMessage.operation.action field (see ObjectOperationAction) determines the type of operation to apply:
      • (RTLM15d1) If ObjectMessage.operation.action is set to MAP_CREATE, apply the operation as described in RTLM16, passing in ObjectMessage.operation
      • (RTLM15d2) If ObjectMessage.operation.action is set to MAP_SET, apply the operation as described in RTLM7, passing in ObjectMessage.operation.mapOp and ObjectMessage.serial
      • (RTLM15d3) If ObjectMessage.operation.action is set to MAP_REMOVE, apply the operation as described in RTLM8, passing in ObjectMessage.operation.mapOp and ObjectMessage.serial
      • (RTLM15d4) Otherwise, log a warning that an object operation message with an unsupported action has been received, and discard the current ObjectMessage without taking any action
  • (RTLM16) A MAP_CREATE operation can be applied to a LiveMap in the following way:
    • (RTLM16a) Expects the following argument:
      • (RTLM16a1) ObjectOperation
    • (RTLM16b) If the private flag createOperationIsMerged is true, log a debug or trace message indicating that the operation will not be applied because a MAP_CREATE operation has already been applied to this LiveMap, and discard the operation without taking any further action
    • (RTLM16c) If the private semantics field does not match ObjectOperation.map.semantics, log a warning that the operation cannot be applied due to mismatched semantics, and discard the operation without taking any further action
    • (RTLM16d) Otherwise merge the initial value into the LiveMap as described in RTLM17, passing in the ObjectOperation instance
  • (RTLM7) A MAP_SET operation for a key can be applied to a LiveMap in the following way:
    • (RTLM7d) Expects the following arguments:
      • (RTLM7d1) ObjectsMapOp
      • (RTLM7d2) serial string – operation’s serial value
    • (RTLM7a) If an ObjectsMapEntry exists in the private data for the specified key:
      • (RTLM7a1) If the operation cannot be applied to the existing entry as per RTLM9, discard the operation without taking any action
      • (RTLM7a2) Otherwise, apply the operation to the existing entry:
        • (RTLM7a2a) Set ObjectsMapEntry.data to the ObjectData from the operation
        • (RTLM7a2b) Set ObjectsMapEntry.timeserial to the provided serial
        • (RTLM7a2c) Set ObjectsMapEntry.tombstone to false
    • (RTLM7b) If an entry does not exist in the private data for the specified key:
      • (RTLM7b1) Create a new ObjectsMapEntry in data for the specified key, with ObjectsMapEntry.data set to the provided ObjectData and ObjectsMapEntry.timeserial set to serial
      • (RTLM7b2) Set ObjectsMapEntry.tombstone for the new entry to false
    • (RTLM7c) If the operation has a non-empty ObjectData.objectId attribute:
      • (RTLM7c1) Create a zero-value LiveObject for this ObjectData.objectId in the internal ObjectsPool per RTO6
  • (RTLM8) MAP_REMOVE operation for a key can be applied to a LiveMap in the following way:
    • (RTLM8c) Expects the following arguments:
      • (RTLM8c1) ObjectsMapOp
      • (RTLM8c2) serial string – operation’s serial value
    • (RTLM8a) If an ObjectsMapEntry exists in the private data for the specified key:
      • (RTLM8a1) If the operation cannot be applied to the existing entry as per RTLM9, discard the operation without taking any action
      • (RTLM8a2) Otherwise, apply the operation to the existing entry:
        • (RTLM8a2a) Set ObjectsMapEntry.data to undefined/null
        • (RTLM8a2b) Set ObjectsMapEntry.timeserial to the provided serial
        • (RTLM8a2c) Set ObjectsMapEntry.tombstone to true
    • (RTLM8b) If an entry does not exist in the private data for the specified key:
      • (RTLM8b1) Create a new ObjectsMapEntry in data for the specified key, with ObjectsMapEntry.data set to undefined/null and ObjectsMapEntry.timeserial set to the provided serial
      • (RTLM8b2) Set ObjectsMapEntry.tombstone for the new entry to true
  • (RTLM9) Whether a map operation can be applied to a map entry is determined as follows:
    • (RTLM9a) For a LiveMap with semantics set to ObjectsMapSemantics.LWW (Last-Write-Wins CRDT semantics), the operation must only be applied if its serial is strictly greater (“after”) than the entry’s serial when compared lexicographically
    • (RTLM9b) If both the entry serial and the operation serial are null or empty strings, they are treated as the “earliest possible” serials and considered “equal”, so the operation must not be applied
    • (RTLM9c) If only the entry serial exists and is not an empty string, the missing operation serial is considered lower than the existing entry serial, so the operation must not be applied
    • (RTLM9d) If only the operation serial exists and is not an empty string, it is considered greater than the missing entry serial, so the operation can be applied
    • (RTLM9e) If both serials exist and are not empty strings, compare them lexicographically and allow operation to be applied only if the operation’s serial is greater than the entry’s serial
  • (RTLM17) The initial value from ObjectOperation.map can be merged into this LiveMap in the following way:
    • (RTLM17a) For each key–@ObjectsMapEntry@ pair in ObjectOperation.map.entries:
      • (RTLM17a1) If ObjectsMapEntry.tombstone is false or omitted, apply the MAP_SET operation to the current key as described in RTLM7, passing in ObjectsMapEntry.data and the current key as ObjectsMapOp, and ObjectsMapEntry.timeserial as serial
      • (RTLM17a2) If ObjectsMapEntry.tombstone is true, apply the MAP_REMOVE operation to the current key as described in RTLM8, passing in the current key as ObjectsMapOp, and ObjectsMapEntry.timeserial as serial
    • (RTLM17b) Set the private flag createOperationIsMerged to true

Interface Definition

Describes types for RealtimeObjects.
Types and their properties/methods are public and exposed to users by default. An internal label may be used to indicate that a type or its property/method must not be exposed to users and is intended for internal SDK use only.

class RealtimeObjects: // RTO*
  getRoot() => io LiveMap // RTO1

class LiveObject: // RTLO*
  objectId: String // RTLO3a, internal
  siteTimeserials: Dict<String, String> // RTLO3b, internal
  createOperationIsMerged: Boolean // RTLO3c, internal
  canApplyOperation(ObjectMessage) -> Boolean // RTLO4a, internal

class LiveCounter extends LiveObject: // RTLC*, RTLC1
  value() -> Number // RTLC5

class LiveMap extends LiveObject: // RTLM*, RTLM1
  get(key: String) -> (Boolean | Binary | Number | String | JsonArray | JsonObject | LiveCounter | LiveMap)? // RTLM5
  size() -> Number // RTLM10
  entries() -> [String, (Boolean | Binary | Number | String | JsonArray | JsonObject | LiveCounter | LiveMap)?][] // RTLM11
  keys() -> String[] // RTLM12
  values() -> (Boolean | Binary | Number | String | JsonArray | JsonObject | LiveCounter | LiveMap)?[] // RTLM13