Skip to content

Delete redundant object type check when applying SyncObjectsPool#429

Merged
lawrence-forooghian merged 1 commit intomainfrom
remove-RTO5c1b1c
Feb 27, 2026
Merged

Delete redundant object type check when applying SyncObjectsPool#429
lawrence-forooghian merged 1 commit intomainfrom
remove-RTO5c1b1c

Conversation

@lawrence-forooghian
Copy link
Collaborator

@lawrence-forooghian lawrence-forooghian commented Feb 26, 2026

RTO5f3 (added in 9f4d7de) rejects unsupported object types before they enter the SyncObjectsPool, so the RTO5c1b1c check during pool application can never be reached. Furthermore, testing RTO5c1b1c would require putting the SDK into an internal state that is inconsistent with the rest of the spec — a SyncObjectsPool containing an unsupported object type — which should not be possible.

@lawrence-forooghian lawrence-forooghian changed the title Delete RTO5c1b1c (redundant to RTO5f3) Delete redundant object type check when applying SyncObjectsPool Feb 26, 2026
lawrence-forooghian added a commit to ably/ably-liveobjects-swift-plugin that referenced this pull request Feb 26, 2026
Spec commits 1f22417 and 9f4d7de add partial object sync for
protocol v6+. The server can now split a large object across
multiple OBJECT_SYNC protocol messages. This commit implements
the merge logic in SyncObjectsPool.

SyncObjectsPool.swift — full rewrite:
- Changed from Collection (array-backed) to Sequence
  (dictionary-backed, keyed by objectId)
- Stores InboundObjectMessage internally; Entry is now a computed
  view yielded during iteration
- Entry.state documents the guarantee that either .map or .counter
  is always populated
- New accumulate(objectMessage:logger:) method implements RTO5f:
  - Skips messages with nil .object
  - RTO5f3: Rejects unsupported types (neither map nor counter)
    before pool lookup — provides the Entry.state guarantee
  - RTO5f1: Stores new entries
  - RTO5f2a1: Replaces map entry when incoming tombstone is true
  - RTO5f2a2: Merges map entries otherwise
  - RTO5f2b: Logs error and skips partial counter
- Removed init(entries:) and append(contentsOf:) (replaced by
  accumulate)

InternalDefaultRealtimeObjects.swift — replaced the compactMap +
append(contentsOf:) / init(entries:) pattern with loops calling
pool.accumulate(objectMessage:logger:) for both the multi-message
and single-message paths.

TestFactories.swift — added serialTimestamp parameter to
inboundObjectMessage factory.

SyncObjectsPoolTests.swift — full rewrite with tests for all
RTO5f spec points plus adapted versions of the original tests.

ObjectsPoolTests.swift — added SyncObjectsPool.testsOnly_fromStates
convenience extension; mechanically replaced all .init(entries:)
call sites.

Removed RTO5c1b1c — the ignoresNonMapOrCounterObject test and the
RTO5c1b1c else branch in ObjectsPool.nosync_applySyncObjectsPool
have been removed, replaced with a preconditionFailure referencing
the Entry.state guarantee. A corresponding spec PR
(ably/specification#429) marks RTO5c1b1c
as deleted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
lawrence-forooghian added a commit to ably/ably-liveobjects-swift-plugin that referenced this pull request Feb 26, 2026
Spec commits 1f22417 and 9f4d7de add partial object sync for
protocol v6+. The server can now split a large object across
multiple OBJECT_SYNC protocol messages. This commit implements
the merge logic in SyncObjectsPool.

SyncObjectsPool.swift — full rewrite:
- Changed from Collection (array-backed) to Sequence
  (dictionary-backed, keyed by objectId)
- Stores InboundObjectMessage internally; Entry is now a computed
  view yielded during iteration
- Entry.state documents the guarantee that either .map or .counter
  is always populated
- New accumulate(objectMessage:logger:) method implements RTO5f:
  - Skips messages with nil .object
  - RTO5f3: Rejects unsupported types (neither map nor counter)
    before pool lookup — provides the Entry.state guarantee
  - RTO5f1: Stores new entries
  - RTO5f2a1: Replaces map entry when incoming tombstone is true
  - RTO5f2a2: Merges map entries otherwise
  - RTO5f2b: Logs error and skips partial counter
- Removed init(entries:) and append(contentsOf:) (replaced by
  accumulate)

InternalDefaultRealtimeObjects.swift — replaced the compactMap +
append(contentsOf:) / init(entries:) pattern with loops calling
pool.accumulate(objectMessage:logger:) for both the multi-message
and single-message paths.

TestFactories.swift — added serialTimestamp parameter to
inboundObjectMessage factory.

SyncObjectsPoolTests.swift — full rewrite with tests for all
RTO5f spec points plus adapted versions of the original tests.

ObjectsPoolTests.swift — added SyncObjectsPool.testsOnly_fromStates
convenience extension; mechanically replaced all .init(entries:)
call sites.

ObjectsIntegrationTests.swift — ported the partial OBJECT_SYNC
integration test from ably-js commit 1052acf. (TODO update commit
reference once JS finalised)

Removed RTO5c1b1c — the ignoresNonMapOrCounterObject test and the
RTO5c1b1c else branch in ObjectsPool.nosync_applySyncObjectsPool
have been removed, replaced with a preconditionFailure referencing
the Entry.state guarantee. A corresponding spec PR
(ably/specification#429) marks RTO5c1b1c
as deleted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@VeskeR
Copy link
Contributor

VeskeR commented Feb 26, 2026

I would argue we should remove RTO5c1b1c instead.
Library's first interaction with incoming object sync message happens during RTO5f and without RTO5f3 it is unclear how we should handle an unexpected type.
As RTO5f curates what we put into the internal SyncObjectsPool, and RTO5c1 uses that internal SyncObjectsPool, the RTO5c1b1c check is redundant - it is guaranteed to have only the correct types due to RTO5f3.

@lawrence-forooghian
Copy link
Collaborator Author

I would argue we should remove RTO5c1b1c instead. Library's first interaction with incoming object sync message happens during RTO5f and without RTO5f3 it is unclear how we should handle an unexpected type. As RTO5f curates what we put into the internal SyncObjectsPool, and RTO5c1 uses that internal SyncObjectsPool, the RTO5c1b1c check is redundant - it is guaranteed to have only the correct types due to RTO5f3.

Sorry @VeskeR I feel I must have missed something — isn't what this PR does precisely what you've suggested?

@VeskeR
Copy link
Contributor

VeskeR commented Feb 27, 2026

Sorry, my bad 🤦‍♂️ . I must've misread the PR yesterday, it was a looong day of PR feedback reviews/addressing.
LGTM

RTO5f3 (added in 9f4d7de) rejects unsupported object types before
they enter the SyncObjectsPool, so the RTO5c1b1c check during pool
application can never be reached. Furthermore, testing RTO5c1b1c
would require putting the SDK into an internal state that is
inconsistent with the rest of the spec — a SyncObjectsPool
containing an unsupported object type — which should not be
possible.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@lawrence-forooghian lawrence-forooghian merged commit 670f130 into main Feb 27, 2026
2 checks passed
@lawrence-forooghian lawrence-forooghian deleted the remove-RTO5c1b1c branch February 27, 2026 13:22
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