Skip to content

[AIT-458] Support MAP_CLEAR object operation#2176

Merged
VeskeR merged 3 commits intointegration/protocol-v6from
AIT-458/map-clear
Mar 12, 2026
Merged

[AIT-458] Support MAP_CLEAR object operation#2176
VeskeR merged 3 commits intointegration/protocol-v6from
AIT-458/map-clear

Conversation

@VeskeR
Copy link
Contributor

@VeskeR VeskeR commented Mar 5, 2026

Add support for MAP_CLEAR object operation
See DR [1], realtime implementation [2] and spec [3].

The DR specifies that MAP_CLEAR is currently only emitted by the server
for the root object. An additional decision was made that the client
should be future-proofed to support MAP_CLEAR on any map object ID,
not just root. This implementation follows that decision.

Semantics of MAP_CLEAR support:

  1. OBJECT_SYNC: The clearTimeserial from the sync state is stored on
    the LiveMap for use by future operations. Materialised entries
    from the ObjectMessage.object.map arrive pre-tombstoned by the
    server for entries that predated the clear. Initial entries from the
    ObjectMessage.object.createOp are merged via the existing
    MAP_SET/MAP_REMOVE semantics, which check against clearTimeserial.

  2. MAP_SET: After the usual siteTimeserials check, the operation is
    additionally discarded if clearTimeserial is set and is
    lexicographically greater than or equal to the operation's serial,
    since the set predates (or is concurrent with) the clear.

  3. MAP_REMOVE: No changes needed - a remove after a clear is valid
    regardless of clearTimeserial.

  4. MAP_CLEAR: The operation is discarded if the current clearTimeserial
    is already greater than or equal to the incoming serial (stale
    clear). Otherwise, clearTimeserial is updated to the operation's
    serial, and all existing entries whose timeserial is null or less
    than or equal to the new clearTimeserial are tombstoned. Entries
    with a strictly later timeserial are preserved.

This PR also consolidates tombstonedAt calculation into LiveObject per RTLO6, See the spec change that adds RTLO6

Resolves AIT-458

[1] https://ably.atlassian.net/wiki/x/DABECAE
[2] https://github.com/ably/realtime/pull/8074
[3] ably/specification#432

Summary by CodeRabbit

  • New Features

    • Added a map-clear action to clear live maps in one operation; clears are propagated and exposed to subscriptions.
  • Bug Fixes / Behavior

    • Map operations now respect clear timing (clear serials/timestamps), skipping stale updates and consistently surfacing clear events and tombstones.
  • Refactor

    • Standardized tombstone timestamp handling with a unified fallback to local time when needed; improved logging around map clears.
  • Tests

    • Extensive tests and helpers added to validate map-clear semantics, tombstoning, nesting, timing and sync/apply flows.

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a MAP_CLEAR operation and MapClear payload; tracks clearTimeserial on maps; implements MAP_CLEAR handling in LiveMap (tombstoning entries, cleaning parent references, gating SET/REMOVE by clear serial); wires MAP_CLEAR through object messages/realtime routing; and adds tests and helpers covering MAP_CLEAR semantics.

Changes

Cohort / File(s) Summary
Type Definitions & Messages
liveobjects.d.ts, src/plugins/liveobjects/objectmessage.ts
Add ObjectOperationActions.MAP_CLEAR / ObjectOperationAction.MAP_CLEAR, export MapClear interface, add mapClear?: MapClear to ObjectOperation, and clearTimeserial?: string to ObjectsMap. Wire mapClear through toUserFacingObjectOperation.
LiveMap Implementation
src/plugins/liveobjects/livemap.ts
Add private _clearTimeserial?: string; implement _applyMapClear(objectMessage) to set _clearTimeserial, tombstone entries, and cleanup parent refs; rename _canApplyMapOperation_canApplyMapEntryOperation; gate _applyMapSet/_applyMapRemove by _clearTimeserial; set _clearTimeserial during overrideWithObjectState.
Realtime Routing
src/plugins/liveobjects/realtimeobject.ts
Handle ObjectOperationAction.MAP_CLEAR in object operation application similar to OBJECT_DELETE: ensure target object exists for apply and record ACK serial when applicable.
LiveObject tombstone helper
src/plugins/liveobjects/liveobject.ts
Introduce protected _calculateTombstonedAt(serialTimestamp, action, details?) to centralize tombstone timestamp logic and use it when tombstoning.
Test Helpers
test/common/modules/liveobjects_helper.js
Add MAP_CLEAR constants and mapClearOp(opts) helper; allow clearTimeserial in mapObject payloads and include it when emitting object state.
Tests
test/realtime/liveobjects.test.js
Add waitForMapClear helper and extensive MAP_CLEAR tests: OBJECT_SYNC handling of clearTimeserial, MAP_CLEAR application on root/non-root maps, tombstoning timestamps and serialTimestamp fallback, interactions with SET/REMOVE, nested maps/object refs, and subscription propagation across sync modes.
Private API Recorder (tests)
test/common/modules/private_api_recorder.js
Allow new private API identifiers call.channel.sendState and read.LiveObject._parentReferences in recorder whitelist.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant Server as Server
    participant LM as LiveMap
    participant State as InternalState

    rect rgba(100,150,200,0.5)
    note over Client,Server: Submit MAP_CLEAR
    Client->>Server: send MAP_CLEAR (op with clearTimeserial)
    Server->>LM: applyOperation(MAP_CLEAR, objectMessage)
    LM->>State: _applyMapClear(objectMessage)
    State->>State: set _clearTimeserial = clearTimeserial
    State->>State: tombstone entries with entrySerial <= clearTimeserial
    State->>State: cleanup parent references for object entries
    LM->>Client: emit LiveMapUpdate (tombstones + clear marker)
    end

    rect rgba(150,100,200,0.5)
    note over Server,LM: Subsequent MAP_SET gating
    Server->>LM: applyOperation(MAP_SET, op with opSerial)
    LM->>LM: _canApplyMapEntryOperation(entrySerial, opSerial)
    alt opSerial <= _clearTimeserial
        LM->>LM: skip apply (cleared)
    else opSerial > _clearTimeserial
        LM->>State: apply map entry update
        LM->>Client: emit LiveMapUpdate (entry set)
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped and tapped the map to clear,

serials marked what once drew near,
keys laid down like winter stones,
parents freed from tethered bones,
a tidy hop — the map breathes clear.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding support for the MAP_CLEAR object operation, which is the core focus of all file modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch AIT-458/map-clear
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot temporarily deployed to staging/pull/2176/bundle-report March 5, 2026 12:08 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/features March 5, 2026 12:08 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/typedoc March 5, 2026 12:08 Inactive
@VeskeR VeskeR force-pushed the AIT-458/map-clear branch from 1c08be7 to 321ca58 Compare March 5, 2026 12:12
@VeskeR VeskeR requested a review from ttypic March 5, 2026 12:13
@github-actions github-actions bot temporarily deployed to staging/pull/2176/features March 5, 2026 12:13 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/bundle-report March 5, 2026 12:13 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/typedoc March 5, 2026 12:13 Inactive
@VeskeR VeskeR force-pushed the AIT-458/map-clear branch from 321ca58 to c136f2b Compare March 5, 2026 12:14
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/realtime/liveobjects.test.js`:
- Around line 1509-1573: The file test/realtime/liveobjects.test.js
(specifically the test block "OBJECT_SYNC sequence initializes map with
clearTimeserial") is misformatted and failing the repo Prettier check; run the
repository formatter (e.g. prettier --write test/realtime/liveobjects.test.js or
the project's format script such as npm run format) to reformat the file, verify
npm run format:check passes, stage the updated file(s) and amend or create a new
commit; repeat for the other affected ranges noted in the review if they reside
in the same file or other files.
- Around line 3381-3384: Replace invalid chained relational assertions like
expect(tsBeforeMsg <= mapEntry.tombstonedAt <= tsAfterMsg).to.be.true with an
explicit conjunction: expect(tsBeforeMsg <= mapEntry.tombstonedAt &&
mapEntry.tombstonedAt <= tsAfterMsg).to.be.true; do the same for the other four
failing assertions that use chained comparisons (any lines using the pattern a
<= b <= c) so each side is tested with && rather than relying on chained
operators.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1ca5bb4a-ef19-4922-9c57-bf86a75dce76

📥 Commits

Reviewing files that changed from the base of the PR and between 1be8271 and 1c08be7.

📒 Files selected for processing (6)
  • liveobjects.d.ts
  • src/plugins/liveobjects/livemap.ts
  • src/plugins/liveobjects/objectmessage.ts
  • src/plugins/liveobjects/realtimeobject.ts
  • test/common/modules/liveobjects_helper.js
  • test/realtime/liveobjects.test.js

@github-actions github-actions bot temporarily deployed to staging/pull/2176/features March 5, 2026 12:15 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/typedoc March 5, 2026 12:15 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/bundle-report March 5, 2026 12:15 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
test/realtime/liveobjects.test.js (1)

3390-3392: ⚠️ Potential issue | 🟠 Major

Fix invalid JavaScript range assertion (a <= b <= c).

At Line 3390, this assertion is not a valid range check in JavaScript and can yield false positives.

Proposed fix
-              tsBeforeMsg <= mapEntry.tombstonedAt <= tsAfterMsg,
+              mapEntry.tombstonedAt >= tsBeforeMsg && mapEntry.tombstonedAt <= tsAfterMsg,
#!/bin/bash
# Verify all chained <= comparisons that should be converted to explicit conjunctions.
rg -nP '\S+\s*<=\s*\S+\s*<=\s*\S+' test/realtime/liveobjects.test.js
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/realtime/liveobjects.test.js` around lines 3390 - 3392, The assertion
uses an invalid chained comparison (tsBeforeMsg <= mapEntry.tombstonedAt <=
tsAfterMsg); replace it with an explicit conjunction so the range is checked
correctly, e.g. change the boolean expression passed to expect in the test
around mapEntry.tombstonedAt to (tsBeforeMsg <= mapEntry.tombstonedAt &&
mapEntry.tombstonedAt <= tsAfterMsg) while keeping the existing message and
.to.be.true expectation; locate the check referencing tsBeforeMsg, tsAfterMsg
and mapEntry.tombstonedAt in the test and update accordingly.
🧹 Nitpick comments (1)
src/plugins/liveobjects/objectmessage.ts (1)

234-240: Consider adding a spec reference for consistency.

The static analysis flags this as an empty interface, but this pattern is intentionally consistent with ObjectDelete (lines 190-192). However, other payload interfaces include @spec references (e.g., MCR1, MST1, MRM1). Consider adding a spec reference if one exists for MAP_CLEAR payload.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/plugins/liveobjects/objectmessage.ts` around lines 234 - 240, The
MapClear interface is intentionally empty but lacks a `@spec` reference like other
payloads; update the MapClear declaration to include the appropriate spec tag
for the MAP_CLEAR payload (e.g., add an `@spec` annotation similar to
MCR1/MST1/MRM1) above the export interface MapClear, and if no spec ID exists,
add a short comment referencing ObjectDelete to document the intentional
emptiness (reference the MapClear identifier and ObjectDelete for consistency).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@liveobjects.d.ts`:
- Around line 1747-1750: The empty interface MapClear (and the similar
ObjectDelete) should be replaced with a type alias to satisfy the
noEmptyInterface lint rule; locate the declarations for MapClear and
ObjectDelete and change "export interface MapClear {}" and "export interface
ObjectDelete {}" to "export type MapClear = {}" and "export type ObjectDelete =
{}", preserving exported names and any surrounding JSDoc comments.

---

Duplicate comments:
In `@test/realtime/liveobjects.test.js`:
- Around line 3390-3392: The assertion uses an invalid chained comparison
(tsBeforeMsg <= mapEntry.tombstonedAt <= tsAfterMsg); replace it with an
explicit conjunction so the range is checked correctly, e.g. change the boolean
expression passed to expect in the test around mapEntry.tombstonedAt to
(tsBeforeMsg <= mapEntry.tombstonedAt && mapEntry.tombstonedAt <= tsAfterMsg)
while keeping the existing message and .to.be.true expectation; locate the check
referencing tsBeforeMsg, tsAfterMsg and mapEntry.tombstonedAt in the test and
update accordingly.

---

Nitpick comments:
In `@src/plugins/liveobjects/objectmessage.ts`:
- Around line 234-240: The MapClear interface is intentionally empty but lacks a
`@spec` reference like other payloads; update the MapClear declaration to include
the appropriate spec tag for the MAP_CLEAR payload (e.g., add an `@spec`
annotation similar to MCR1/MST1/MRM1) above the export interface MapClear, and
if no spec ID exists, add a short comment referencing ObjectDelete to document
the intentional emptiness (reference the MapClear identifier and ObjectDelete
for consistency).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 30445ec2-c678-4278-9ff4-a870915a4971

📥 Commits

Reviewing files that changed from the base of the PR and between 1c08be7 and c136f2b.

📒 Files selected for processing (6)
  • liveobjects.d.ts
  • src/plugins/liveobjects/livemap.ts
  • src/plugins/liveobjects/objectmessage.ts
  • src/plugins/liveobjects/realtimeobject.ts
  • test/common/modules/liveobjects_helper.js
  • test/realtime/liveobjects.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/plugins/liveobjects/realtimeobject.ts
  • test/common/modules/liveobjects_helper.js

@VeskeR VeskeR force-pushed the AIT-458/map-clear branch from c136f2b to 2924ce0 Compare March 5, 2026 15:01
@github-actions github-actions bot temporarily deployed to staging/pull/2176/bundle-report March 5, 2026 15:02 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/typedoc March 5, 2026 15:02 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/features March 5, 2026 15:02 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/plugins/liveobjects/objectmessage.ts`:
- Around line 234-239: Replace the empty interface MapClear with a non-empty
type to satisfy Biome's noEmptyInterface rule: update the declaration for
MapClear (the exported MapClear symbol) to be a type alias using a sealed record
type (i.e., Record<string, never>) so the shape is non-empty but still forbids
any properties; keep the export and comment describing MAP_CLEAR semantics
unchanged.

In `@test/realtime/liveobjects.test.js`:
- Around line 3402-3418: The test currently sends a MAP_CLEAR via
helper.recordPrivateApi('call.channel.sendState') and immediately asserts
counterObj._parentReferences.size, which can race with async message processing;
update the test to await the state-application callback before asserting (e.g.,
use the helper to wait for the corresponding apply step such as the private API
call that handles the incoming state, e.g. wait for 'call.channel.applyState' or
another helper-provided "state applied" event) after channel.sendState([...])
and only then check counterObj._parentReferences.size to ensure the MAP_CLEAR
has been applied.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a54e150b-8684-49e9-9ef9-b05fe10251a9

📥 Commits

Reviewing files that changed from the base of the PR and between c136f2b and 2924ce0.

📒 Files selected for processing (7)
  • liveobjects.d.ts
  • src/plugins/liveobjects/livemap.ts
  • src/plugins/liveobjects/objectmessage.ts
  • src/plugins/liveobjects/realtimeobject.ts
  • test/common/modules/liveobjects_helper.js
  • test/common/modules/private_api_recorder.js
  • test/realtime/liveobjects.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/plugins/liveobjects/realtimeobject.ts

@VeskeR VeskeR force-pushed the AIT-458/map-clear branch from 2924ce0 to 217e5bf Compare March 6, 2026 01:02
@github-actions github-actions bot temporarily deployed to staging/pull/2176/features March 6, 2026 01:03 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/bundle-report March 6, 2026 01:03 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/typedoc March 6, 2026 01:03 Inactive
@VeskeR VeskeR force-pushed the AIT-458/map-clear branch from 217e5bf to 3081e9c Compare March 6, 2026 01:04
@github-actions github-actions bot temporarily deployed to staging/pull/2176/features March 6, 2026 01:04 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/bundle-report March 6, 2026 01:04 Inactive
{ serial: lexicoTimeserial('bbb', 4, 0), siteCode: 'bbb', cleared: false }, // existing site, earlier CGO, not applied
{ serial: lexicoTimeserial('bbb', 5, 0), siteCode: 'bbb', cleared: false }, // existing site, same CGO, not applied
{ serial: lexicoTimeserial('bbb', 6, 0), siteCode: 'bbb', cleared: true }, // existing site, later CGO, applied
{ serial: lexicoTimeserial('aaa', 1, 0), siteCode: 'aaa', cleared: true }, // different site, earlier CGO, applied
Copy link
Collaborator

@lawrence-forooghian lawrence-forooghian Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If "existing site" means "site that already exists in siteTimeserials", then isn't aaa also an existing site (because of the MAP_SET)? Also, the mention of CGO here threw me a bit — I was trying to figure out what relevance CGO had to the siteTimeserials check (it doesn't). For the "existing site" cases, I think "regional order" would be clearer, and then for the "different site" ones I don't think we need to mention CGO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If "existing site" means "site that already exists in siteTimeserials", then isn't aaa also an existing site (because of the MAP_SET)?

Oops. It doesn't affect the test though as the set used the earliest possible timestamp. On second thought, we don't actually need a set here. What we want is a map with some initial data to test against, and ideally only a single entry in siteTimeserials to check (via the remove op). We can achieve that by creating a map with initial entries (no siteTimeserials) and then applying a remove op. I also realised I'm not actually creating any map objects in this test - only doing set/remove on them. Will fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the mention of CGO here threw me a bit — I was trying to figure out what relevance CGO had to the siteTimeserials check (it doesn't). For the "existing site" cases, I think "regional order" would be clearer, and then for the "different site" ones I don't think we need to mention CGO?

I don't agree with this, I think mention of CGO is relevant. The point of the test is to confirm that even though the incoming MAP_CLEAR operation has an earlier CGO than some previously applied operations on that object, it should still be applied via the siteTimeserial procedure - because it's coming from a different site (and there is no set clearTimeserial with later CGO). Both concepts are connected here. That's in contrast with map entry operations, which also perform a straight lexicographic timeserial check before applying an op, meaning an earlier CGO from a different site wouldn't pass that check solely by virtue of being from a different site - and just like MAP_CLEAR has its own check against the previously set clearTimeserial.

Copy link
Contributor Author

@VeskeR VeskeR Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can achieve that by creating a map with initial entries (no siteTimeserials) and then applying a remove op.

Scratch that, map will still have a site timeserial from the create op. Still going to change to map create and use earliest timeserials for entries, just going to add some additional comments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's my understanding of what we're doing in this test: we're injecting a MAP_CLEAR which always passes the CGO comparison against the visible { foo: bar } entry, and thus which we expect to clear this entry if and only if the siteTimeserials check permits it. Thus, I'm reading the various entries of the ops array as scenarios that test the siteTimeserials check. So, it remains confusing to me why we're mentioning CGO in a test of the siteTimeserials check since in theory CGO is not a concept that is relevant for this check, which is that an operation should be permitted if and only if it's from a site for which there isn't an entry, or is in later regional order than the current entry for the site it's from.

It seems to me that what your assertions are doing is asserting the fact that CGO is irrelevant for the siteTimeserials check (i.e. that higher CGO doesn't matter if it's from a different site), and that all that matters is regional order within a given site. If so, I think that the comments alongside the ops array could be a bit clearer.

If you think that the test needs no changes though then I don't want to block merging over this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asserting the fact that CGO is irrelevant for the siteTimeserials check (i.e. that higher CGO doesn't matter if it's from a different site), and that all that matters is regional order within a given site. If so, I think that the comments alongside the ops array could be a bit clearer.

Yes, this is a good way to explain it.

If you think that the test needs no changes though then I don't want to block merging over this.

There are a couple of tests that do a similar siteTimeserials check, and I'd prefer to update them separately in #2181 - for some of them, CGO will actually matter for different sites (like MAP_SET and MAP_REMOVE, which have their own entry timeserial check), so I'll need to make sure the comments make sense for each case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure — let's keep this one unresolved for future reference, but happy to leave it with you to do separately

@VeskeR VeskeR force-pushed the AIT-458/map-clear branch from 1a1f8f1 to 35f6d04 Compare March 12, 2026 16:29
@github-actions github-actions bot temporarily deployed to staging/pull/2176/features March 12, 2026 16:30 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/bundle-report March 12, 2026 16:30 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/2176/typedoc March 12, 2026 16:30 Inactive
@VeskeR
Copy link
Contributor Author

VeskeR commented Mar 12, 2026

Squashed feedback commits, should be ready for approval and merge

See DR [1], realtime implementation [2] and spec [3].

The DR specifies that MAP_CLEAR is currently only emitted by the server
for the root object. An additional decision was made that the client
should be future-proofed to support MAP_CLEAR on any map object ID,
not just root. This implementation follows that decision.

Semantics of MAP_CLEAR support:

1. OBJECT_SYNC: The clearTimeserial from the sync state is stored on
   the LiveMap for use by future operations. Materialised entries
   from the `ObjectMessage.object.map` arrive pre-tombstoned by the
   server for entries that predated the clear. Initial entries from the
   `ObjectMessage.object.createOp` are merged via the existing
   MAP_SET/MAP_REMOVE semantics, which check against clearTimeserial.

2. MAP_SET: After the usual siteTimeserials check, the operation is
   additionally discarded if clearTimeserial is set and is
   lexicographically greater than or equal to the operation's serial,
   since the set predates (or is concurrent with) the clear.

3. MAP_REMOVE: No changes needed - a remove after a clear is valid
   regardless of clearTimeserial.

4. MAP_CLEAR: The operation is discarded if the current clearTimeserial
   is already greater than or equal to the incoming serial (stale
   clear). Otherwise, clearTimeserial is updated to the operation's
   serial, and all existing entries whose timeserial is null or less
   than or equal to the new clearTimeserial are tombstoned. Entries
   with a strictly later timeserial are preserved.

Resolves AIT-458

[1] https://ably.atlassian.net/wiki/x/DABECAE
[2] ably/realtime#8074
[3] ably/specification#432
@VeskeR VeskeR merged commit c77a0bc into integration/protocol-v6 Mar 12, 2026
11 of 14 checks passed
@VeskeR VeskeR deleted the AIT-458/map-clear branch March 12, 2026 18:49
VeskeR added a commit that referenced this pull request Mar 12, 2026
[AIT-458] Support MAP_CLEAR object operation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants