Objects
LiveObjects enables you to store shared data as "objects" on a channel, allowing your application data to be synchronized across multiple users and devices in realtime. This document explains the key concepts you need to know when working with objects.
Object Types
LiveObjects provides specialized object types to model your application state. These object types are designed to be conflict-free and eventually consistent, meaning that all operations on them are commutative and converge to the same state across all clients.
LiveMap Object
LiveMap is a key/value data structure similar to a dictionary or JavaScript Map:
- Keys must be strings
- Values can be primitive types or references to other objects
- Supports
set
andremove
operations - Concurrent updates to the same key are resolved using last-write-wins (LWW) semantics
Primitive Types
LiveMap supports the following primitive types as values:
string
number
boolean
bytes
LiveCounter Object
LiveCounter is a numeric counter type:
- The value is a double-precision floating-point number
- Supports
increment
anddecrement
operations
Root Object
The root object is a special LiveMap
instance which:
- Implicitly exists on a channel and does not need to be created explicitly
- Has the special objectId of
root
- Cannot be deleted
- Serves as the entry point for accessing all other objects on a channel
Access the root object using the getRoot()
function:
Reachability
All objects must be reachable from the root object (directly or indirectly). Objects that cannot be reached from the root object will eventually be deleted.
When an object has been deleted, it is no longer usable and calling any methods on the object will fail.
In the example below, the only reference to the counterOld
object is replaced on the root
. This makes counterOld
unreachable and it will eventually be deleted.
Composability
LiveObjects enables you to build complex, hierarchical data structures through composability.
Specifically, a LiveMap can store references to other LiveMap
or LiveCounter
object instances as values. This allows you to create nested hierarchies of data.
It is possible for the same object instance to be accessed from multiple places in your object tree:
It is also possible that object references form a cycle:
Metadata
Objects include metadata that helps with synchronization, conflict resolution and managing the object lifecycle.
Object IDs
Every object has a unique identifier that distinguishes it from all other objects.
Object IDs follow a specific format:
For example:
This format has been specifically designed to ensure uniqueness in a globally distributed system and includes:
Part | Description |
---|---|
type | The object type (either map or counter ). |
hash | A base64 string encoded hash derived from the initial value of the object and a random nonce. |
timestamp | A Unix millisecond timestamp denoting the creation time of the object. |
Tombstones
Tombstones are markers indicating an object or map entry has been deleted.
- A tombstone is created for an object when it becomes unreachable from the root object.
- A tombstone is created for a map entry when it is removed
Tombstones protect against lagging clients from re-introducing a deleted value, ensuring all clients eventually converge on the same state. They are eventually garbage collected after a safe period of time.
Timeserials
When an operation message is published it is assigned a unique logical timestamp called a "timeserial".
This timeserial is stored on map entries in order to implement last-write-wins conflict resolution semantics.
Additionally, all objects store the timeserial of the last operation that was applied to the object. Since Ably operates fully independent data centers, these timeserials are stored on a per-site basis.
Timeserial metadata is used for internal purposes and is not directly exposed in client libraries. However, it can be viewed using the REST API.