Skip to content

Conversation

@lawrence-forooghian
Copy link
Collaborator

@lawrence-forooghian lawrence-forooghian commented Jul 23, 2025

Note: This PR is based on top of #45; please review that one first.

Implements ably/specification#350. Have not yet done unit tests; have deferred those to #52, and will instead prioritise porting across the JS integration tests.

Summary by CodeRabbit

  • New Features

    • Added automatic cleanup (garbage collection) of deleted (tombstoned) objects and map entries after a configurable grace period.
    • Objects and map entries now track when they were deleted, improving state management and consistency.
    • Introduced explicit tombstone state and timestamp properties for live counters, maps, and entries.
    • Added support for tombstone-aware operations including object deletion and entry removal.
    • Enabled periodic background garbage collection of tombstoned objects in real-time objects.
  • Bug Fixes

    • Improved handling of deleted objects to prevent unintended operations or data access.
  • Tests

    • Updated tests to reflect new deletion tracking and cleanup logic.
  • Chores

    • Enhanced internal logging for deletion and cleanup events.

@lawrence-forooghian lawrence-forooghian changed the base branch from main to ECO-5337-subscriptions July 23, 2025 14:33
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 23, 2025 14:35 Inactive
@coderabbitai
Copy link

coderabbitai bot commented Jul 23, 2025

Walkthrough

This set of changes introduces tombstone handling and garbage collection for LiveObjects in the codebase. It adds timestamp-based tombstone tracking, updates APIs to propagate tombstone state, and implements periodic garbage collection for tombstoned objects and map entries. Related test helpers and tests are updated to use the new tombstone timestamp logic.

Changes

Cohort / File(s) Change Summary
LiveCounter Tombstone & Logging Enhancements
Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift
Adds logger and clock parameters to key methods, implements tombstone logic with timestamping, exposes isTombstone/tombstonedAt, and adds a data reset method.
LiveMap Tombstone Logic & Entry Release
Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
Implements tombstone state and timestamp tracking for maps and entries, updates methods to account for tombstoned state, adds logic to release tombstoned entries after a grace period, and modifies signatures to propagate delegate and timestamp parameters.
RealtimeObjects Garbage Collection
Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift
Adds periodic garbage collection of tombstoned objects and entries via a background task, with configurable interval and grace period.
LiveObject Protocol Extensions
Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
Extends protocol with resetDataToZeroValued, and adds tombstone/deletion helpers that set timestamps and clear data.
ObjectsMapEntry Tombstone Refactor
Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift
Replaces boolean tombstone with tombstonedAt timestamp and computed property, updates initializer accordingly.
LiveObjectMutableState Tombstone Tracking
Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift
Adds tombstonedAt timestamp and isTombstone computed property, updates initializer to accept tombstone timestamp.
ObjectsPool Tombstone & GC
Sources/AblyLiveObjects/Internal/ObjectsPool.swift
Adds tombstone status/timestamp properties to entries, and implements garbage collection for tombstoned objects and map entries.
Public Proxy Map Delegate Update
Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift
Updates internal call in size property to pass delegate, aligning with new signature.
Test Factories Tombstone Refactor
Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
Changes test entry factories to use tombstonedAt: Date? instead of boolean tombstone.
LiveMap Tests Tombstone & API Update
Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
Refactors tests to use tombstonedAt, updates method calls for new signatures and logic, adjusts assertions for tombstone handling.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant RealtimeObjects
    participant ObjectsPool
    participant LiveMap/Counter
    participant Logger
    participant Clock

    Note over User,RealtimeObjects: Periodic garbage collection
    loop Every interval
        RealtimeObjects->>ObjectsPool: performGarbageCollection(gracePeriod, clock, logger)
        ObjectsPool->>LiveMap/Counter: releaseTombstonedEntries(gracePeriod, logger, clock)
        LiveMap/Counter->>Logger: Log released tombstoned entries
        ObjectsPool->>Logger: Log released tombstoned objects
    end

    Note over User,LiveMap/Counter: Tombstone propagation
    User->>LiveMap/Counter: apply(OBJECT_DELETE, serialTimestamp, logger, clock)
    LiveMap/Counter->>Clock: Get current time (if needed)
    LiveMap/Counter->>Logger: Log tombstone timestamp
    LiveMap/Counter->>LiveMap/Counter: Set tombstonedAt, reset data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
Implement LiveObjects Swift deletions (ECO-5461)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Possibly related PRs

  • Groundwork for deletions and writes #42: The main PR and the retrieved PR Groundwork for deletions and writes #42 both modify InternalDefaultLiveCounter and InternalDefaultLiveMap by adding logger and clock parameters to methods like replaceData and apply, introducing tombstone state handling with timestamps, and updating method signatures and internal state properties consistently; thus, their changes are directly related at the code level.

Suggested reviewers

  • maratal
  • umair-ably

Poem

A tombstone now marks where objects once played,
With timestamps to track when their data decayed.
The garbage collector sweeps, so neat and discreet,
Old maps and counters make room for new feet.
With every tick, a cleaner codebase blooms—
Hooray for the rabbits who tidy the rooms! 🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e334349 and 2167277.

📒 Files selected for processing (8)
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift (7 hunks)
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (14 hunks)
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift (3 hunks)
  • Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift (1 hunks)
  • Sources/AblyLiveObjects/Internal/ObjectsPool.swift (2 hunks)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift (1 hunks)
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift (2 hunks)
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift
  • Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift
  • Sources/AblyLiveObjects/Internal/ObjectsPool.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: Example app, tvOS (Xcode 16.3)
  • GitHub Check: Example app, iOS (Xcode 16.3)
  • GitHub Check: Example app, macOS (Xcode 16.3)
  • GitHub Check: Xcode, iOS (Xcode 16.3)
  • GitHub Check: Xcode, release configuration, macOS (Xcode 16.3)
  • GitHub Check: Xcode, tvOS (Xcode 16.3)
  • GitHub Check: Xcode, macOS (Xcode 16.3)
  • GitHub Check: SPM (Xcode 16.3)
  • GitHub Check: Xcode, release configuration, iOS (Xcode 16.3)
  • GitHub Check: Xcode, release configuration, tvOS (Xcode 16.3)
  • GitHub Check: SPM, release configuration (Xcode 16.3)
  • GitHub Check: Generate code coverage
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ECO-5461-deletions

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 24, 2025 08:04 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 24, 2025 08:15 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 24, 2025 08:41 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 29, 2025 13:36 Inactive
@lawrence-forooghian lawrence-forooghian changed the base branch from ECO-5337-subscriptions to groundwork-for-deletions-and-writes July 30, 2025 07:47
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 30, 2025 07:48 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 30, 2025 10:55 Inactive
@lawrence-forooghian lawrence-forooghian changed the base branch from groundwork-for-deletions-and-writes to 44-JSON-map-entries-support July 30, 2025 10:57
@lawrence-forooghian lawrence-forooghian force-pushed the 44-JSON-map-entries-support branch from 6fecc91 to 59783b1 Compare July 30, 2025 16:12
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 30, 2025 16:14 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 31, 2025 08:23 Inactive
@lawrence-forooghian lawrence-forooghian changed the base branch from 44-JSON-map-entries-support to ECO-5435-write-API July 31, 2025 08:25
@lawrence-forooghian lawrence-forooghian changed the base branch from ECO-5435-write-API to 44-JSON-map-entries-support July 31, 2025 14:27
@lawrence-forooghian lawrence-forooghian marked this pull request as ready for review July 31, 2025 14:28
@lawrence-forooghian lawrence-forooghian changed the title [WIP, ECO-5461] Implement tombstoning and garbage collection spec [ECO-5461] Implement tombstoning and garbage collection spec Jul 31, 2025
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects July 31, 2025 14:31 Inactive
@lawrence-forooghian
Copy link
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jul 31, 2025

✅ Actions performed

Full review triggered.

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: 6

🔭 Outside diff range comments (1)
Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift (1)

544-545: Update test to use tombstonedAt instead of removed tombstone

The tombstone boolean no longer exists in the codebase, so this assertion will fail to compile. Update the test to assert that the new timestamp property is nil after a MAP_SET:

• File: Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
Lines: 544–545

Replace:

- #expect(map.testsOnly_data["key1"]?.tombstone == false)
+ #expect(map.testsOnly_data["key1"]?.tombstonedAt == nil)
🧹 Nitpick comments (1)
Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift (1)

320-325: Fix incorrect comment about tombstone state.

The comment on line 322 says "tombstonedAt is false" but should say "tombstonedAt is nil" to accurately reflect that this entry is not tombstoned because tombstonedAt defaults to nil when not specified.

-                    // tombstonedAt is false, so not considered tombstoned
+                    // tombstonedAt is nil, so not considered tombstoned

The tombstone entries with Date() timestamps are correctly implemented.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59783b1 and 1badcec.

📒 Files selected for processing (10)
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift (7 hunks)
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (14 hunks)
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift (3 hunks)
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift (1 hunks)
  • Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift (1 hunks)
  • Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift (1 hunks)
  • Sources/AblyLiveObjects/Internal/ObjectsPool.swift (2 hunks)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift (1 hunks)
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift (2 hunks)
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift (11 hunks)
🧰 Additional context used
🧠 Learnings (11)
📚 Learning: applies to ablyliveobjects/**/!(*test|*tests).swift : when importing the ably module inside the ably...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/swift.mdc:0-0
Timestamp: 2025-07-29T08:07:47.875Z
Learning: Applies to AblyLiveObjects/**/!(*Test|*Tests).swift : When importing the Ably module inside the AblyLiveObjects library code (non-test code), use `import Ably`.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
📚 Learning: applies to ablyliveobjects/**/!(*test|*tests).swift : when importing the ablyplugin module inside th...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/swift.mdc:0-0
Timestamp: 2025-07-29T08:07:47.875Z
Learning: Applies to AblyLiveObjects/**/!(*Test|*Tests).swift : When importing the AblyPlugin module inside the AblyLiveObjects library code (non-test code), use `internal import AblyPlugin`.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift
  • Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
📚 Learning: applies to **/tests/**/*.swift : when you need to import the following modules in the tests, do so i...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : When you need to import the following modules in the tests, do so in the following way: Ably: use `import Ably`; AblyLiveObjects: use `@testable import AblyLiveObjects`; AblyPlugin: use `import AblyPlugin`; do not do `internal import`.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
📚 Learning: applies to **/tests/**/*.swift : when creating `testsonly_` property declarations, do not write gene...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:07:58.385Z
Learning: Applies to **/Tests/**/*.swift : When creating `testsOnly_` property declarations, do not write generic comments of the form 'Test-only access to the private createOperationIsMerged property'; the meaning of these properties is already well understood.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
📚 Learning: applies to **/tests/**/*.swift : add comments that explain when some piece of test data is not impor...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : Add comments that explain when some piece of test data is not important for the scenario being tested.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
📚 Learning: applies to **/tests/**/*.swift : when writing tests, make sure to add comments that explain when som...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:07:58.385Z
Learning: Applies to **/Tests/**/*.swift : When writing tests, make sure to add comments that explain when some piece of test data is not important for the scenario being tested.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift
📚 Learning: applies to **/tests/**/*.swift : follow the guidelines given under 'attributing tests to a spec poin...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : Follow the guidelines given under 'Attributing tests to a spec point' in the file `CONTRIBUTING.md` in order to tag the unit tests with the relevant specification points. Make sure to follow the exact format of the comments as described in that file. Pay particular attention to the difference between the meaning of `@spec` and `@specPartial` and be sure not to write `@spec` multiple times for the same specification point.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: applies to **/tests/**/*.swift : do not use `fatalerror` in response to a test expectation failure. ...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : Do not use `fatalError` in response to a test expectation failure. Favour the usage of Swift Testing's `#require` macro.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: applies to **/tests/**/*.swift : when you need to unwrap an optional value in the tests, favour usin...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : When you need to unwrap an optional value in the tests, favour using `#require` instead of `guard let`.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: applies to **/tests/**/*.swift : when you need to pass a logger to internal components in the tests,...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : When you need to pass a logger to internal components in the tests, pass `TestLogger()`.

Applied to files:

  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
📚 Learning: the liveobjects functionality is referred to in the specification simply as 'objects'....
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/specification.mdc:0-0
Timestamp: 2025-07-29T08:07:37.861Z
Learning: The LiveObjects functionality is referred to in the Specification simply as 'Objects'.

Applied to files:

  • Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift
  • Sources/AblyLiveObjects/Internal/InternalLiveObject.swift
🧬 Code Graph Analysis (4)
Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift (1)
Sources/AblyLiveObjects/Internal/InternalLiveObject.swift (1)
  • tombstone (17-35)
Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift (1)
Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (1)
  • size (134-144)
Sources/AblyLiveObjects/Internal/InternalLiveObject.swift (2)
Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift (1)
  • resetDataToZeroValued (384-387)
Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (1)
  • resetDataToZeroValued (766-769)
Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift (1)
Sources/AblyLiveObjects/Internal/ObjectsPool.swift (1)
  • performGarbageCollection (322-345)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Xcode, release configuration, tvOS (Xcode 16.3)
  • GitHub Check: Xcode, iOS (Xcode 16.3)
  • GitHub Check: Example app, iOS (Xcode 16.3)
  • GitHub Check: Generate code coverage
🔇 Additional comments (25)
Sources/AblyLiveObjects/Internal/LiveObjectMutableState.swift (3)

13-17: Clean computed property implementation for tombstone state.

The computed isTombstone property provides a clean boolean interface while maintaining timestamp precision internally. The TODO comment suggests this approach may be preferred over storing separate boolean state, which aligns with DRY principles.


19-20: Enhanced tombstone tracking with timestamps.

The tombstonedAt property upgrade from boolean to timestamp enables more sophisticated garbage collection logic based on tombstone age. The optional Date type appropriately represents the tombstone state.


28-28: Appropriate initializer update for test support.

The addition of the testsOnly_tombstonedAt parameter follows established naming conventions and enables test scenarios while maintaining appropriate default behavior (nil = not tombstoned).

Also applies to: 32-32

Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift (1)

37-37: Correct delegate parameter addition for tombstone-aware size calculation.

The addition of the delegate parameter aligns with the updated InternalDefaultLiveMap.size method signature and maintains consistency with other proxied method calls in this class.

Sources/AblyLiveObjects/Internal/InternalObjectsMapEntry.swift (4)

1-2: Necessary Foundation import for Date type.

The import is required for the Date type used in the tombstonedAt property.


5-9: Consistent timestamp-based tombstone tracking implementation.

The transition from boolean to timestamp-based tombstone tracking with computed boolean interface follows the same clean pattern as LiveObjectMutableState. This enables enhanced garbage collection logic while maintaining a familiar boolean interface.


16-16: Correct initializer parameter update.

The parameter change from tombstone: Bool? to tombstonedAt: Date? properly aligns with the new property structure.


17-17: Proper property assignment update.

The assignment correctly uses the new tombstonedAt parameter and property.

Tests/AblyLiveObjectsTests/Helpers/TestFactories.swift (2)

409-409: Correct factory method update for timestamp-based tombstone tracking.

The parameter and assignment changes properly align with the new tombstonedAt: Date? property structure. The default value of nil appropriately represents "not tombstoned".

Also applies to: 414-414


443-443: Appropriate parameter update in internalStringMapEntry.

The parameter change from tombstone: Bool? to tombstonedAt: Date? with nil default is consistent with the tombstone tracking updates.

Sources/AblyLiveObjects/Internal/InternalLiveObject.swift (3)

1-2: Clean protocol extension for tombstone functionality.

The import follows established patterns, and the resetDataToZeroValued() method requirement is properly defined with appropriate mutating keyword and spec documentation.

Also applies to: 10-13


15-35: Well-implemented tombstone method with robust timestamp handling.

The method properly handles both timestamp scenarios (provided vs. local clock fallback) with appropriate logging, and correctly sequences the tombstone timestamp assignment before data reset. The implementation follows the RTLO specification accurately.


37-49: Appropriate delegation for OBJECT_DELETE operation.

The method provides clear semantic meaning for delete operations by delegating to the tombstone method. The implementation correctly follows the RTLO5 specification.

Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift (1)

259-266: Thread safety issue: garbageCollectionGracePeriod accessed without synchronization

The method accesses garbageCollectionGracePeriod without mutex protection. Since this value can be modified by CONNECTED ProtocolMessages (as mentioned in the comments), this could lead to race conditions.

Access the grace period within the mutex lock:

     internal func performGarbageCollection() {
         mutex.withLock {
+            let gracePeriod = garbageCollectionGracePeriod
             mutableState.objectsPool.performGarbageCollection(
-                gracePeriod: garbageCollectionGracePeriod,
+                gracePeriod: gracePeriod,
                 clock: clock,
                 logger: logger,
             )
         }
     }

Alternatively, consider moving garbageCollectionGracePeriod into the MutableState struct to ensure all mutable state is protected by the mutex.

Likely an incorrect or invalid review comment.

Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift (1)

317-321: Address the TODO: Clarify spec behavior for updating siteTimeserials on tombstoned objects

The implementation currently updates siteTimeserials before checking if the object is tombstoned. The TODO comment raises a valid question about whether this is the intended behavior.

Consider tracking this as a spec clarification issue to ensure the implementation aligns with the intended behavior. The current approach seems reasonable (update metadata regardless of tombstone state), but explicit spec guidance would be helpful.

Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift (10)

42-42: LGTM! Correctly updated tombstone handling.

The change from boolean tombstone: true to tombstonedAt: Date() properly implements the new timestamp-based tombstone tracking while maintaining the test's intent to verify that tombstoned entries return nil.


287-287: LGTM! Method signature correctly updated.

The addition of the delegate parameter to the size method call is consistent with the updated method signature and aligns with other property access methods.


333-351: LGTM! Consistent method signature updates and correct test expectations.

All property access methods are correctly updated to include the delegate parameter, and the test expectations properly verify that tombstoned entries are filtered out (expecting 1 active entry instead of 3 total entries).


375-375: LGTM! Method signature correctly updated.

The size method call is properly updated to include the delegate parameter, maintaining consistency with other property access methods in the test.


425-425: LGTM! Method signature correctly updated.

Consistent with other updates, the size method call properly includes the delegate parameter.


508-508: LGTM! Correctly updated tombstone handling.

The test entry is properly updated to use timestamp-based tombstoning with tombstonedAt: Date(), maintaining the test's intent to verify operation application on tombstoned entries.


688-688: LGTM! Method calls correctly updated with new parameter.

The testsOnly_applyMapRemoveOperation calls are properly updated to include the new operationSerialTimestamp: nil parameter, which is appropriate for test scenarios that don't require specific timestamp values.

Also applies to: 714-714, 748-748, 770-770


706-707: LGTM! Correctly specified non-tombstoned entry.

The explicit tombstonedAt: nil parameter correctly indicates that the entry is not tombstoned initially, making the test setup clear and intentional.


606-606: Verify consistent tombstone property usage across tests.

Similar to the previous comment, these tests are checking the boolean tombstone property rather than the new tombstonedAt timestamp approach. Please verify that:

  1. The tombstone boolean property is still available and correctly computed from tombstonedAt
  2. These assertions are testing the correct behavior
  3. The relationship between tombstone and tombstonedAt is properly maintained

This appears to be a systematic pattern across MAP_SET and MAP_REMOVE operation tests.

Also applies to: 731-731, 773-773


1-1239: Overall assessment: Well-structured update to tombstone timestamp implementation.

The test file has been systematically updated to support the new tombstone timestamp approach:

Strengths:

  • Consistent updates to method signatures (adding delegate parameters)
  • Proper use of tombstonedAt: Date() for creating tombstoned test entries
  • Correct addition of operationSerialTimestamp parameter to MAP_REMOVE operations
  • Test logic and expectations remain sound and comprehensive

Areas verified above:

  • Mixed use of boolean tombstone property in assertions needs verification to ensure compatibility with the new tombstonedAt implementation
  • One minor comment correction needed (line 322)

The changes maintain test coverage while properly adapting to the new tombstone timestamp functionality.

@lawrence-forooghian lawrence-forooghian force-pushed the 44-JSON-map-entries-support branch from 59783b1 to 4034382 Compare August 1, 2025 11:04
Base automatically changed from 44-JSON-map-entries-support to main August 1, 2025 11:13
Based on [1] at 488e932. Haven't updated tests due to being in a bit of
a rush; deferred to #52.

[1] ably/specification#350
Based on spec referenced in 133c85b; same comment re testing applies
too.
Based on spec referenced in 133c85b; same comment re testing applies
too.
Based on spec referenced in 133c85b; same comment re testing applies
too.
Based on spec referenced in 133c85b; same comment re testing applies
too.
Based on spec referenced in 133c85b; same comment re testing applies
too.
@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects August 1, 2025 11:16 Inactive
@umair-ably
Copy link
Collaborator

I think the comments raised by coderabbit are valid enough to review and fix in this pr, wdyt @lawrence-forooghian?

@lawrence-forooghian
Copy link
Collaborator Author

I think the comments raised by coderabbit are valid enough to review and fix in this pr, wdyt @lawrence-forooghian?

Yep, am taking a look

@github-actions github-actions bot temporarily deployed to staging/pull/33/AblyLiveObjects August 1, 2025 14:04 Inactive
Based on spec referenced in 133c85b; same comment re testing applies
too.
Based on spec referenced in 133c85b; same comment re testing applies
too.
Based on spec referenced in 133c85b; same comment re testing applies
too.

Have not yet implemented RTO10b2's server-specified grace period; have
split this work into #32.
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: 0

♻️ Duplicate comments (1)
Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (1)

530-534: Update TODO: siteTimeserials should not be updated on tombstoned maps

Per the Ably Live Objects specification, operations on tombstoned objects are explicitly rejected (error 92002) and no timeserial metadata is updated after tombstoning. Please remove or revise the TODO to reflect that behavior.

-            // RTLM15e
-            // TODO: are we still meant to update siteTimeserials? https://github.com/ably/specification/pull/350/files#r2218718854
-            if liveObjectMutableState.isTombstone {
-                return
-            }
+            // RTLM15e: per spec, operations on tombstoned objects are rejected and siteTimeserials remain unchanged.
+            if liveObjectMutableState.isTombstone {
+                return
+            }
🧹 Nitpick comments (3)
Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (3)

342-347: Add explicit access control level

The method is missing an explicit access control level, violating the SwiftLint explicit_acl rule specified in the coding guidelines.

-    /// Releases entries that were tombstoned more than `gracePeriod` ago, per RTLM19.
-    internal func releaseTombstonedEntries(gracePeriod: TimeInterval, clock: SimpleClock) {
+    /// Releases entries that were tombstoned more than `gracePeriod` ago, per RTLM19.
+    internal func releaseTombstonedEntries(gracePeriod: TimeInterval, clock: SimpleClock) {

Actually, looking more carefully, the method already has internal access control level. This appears to be correct.


349-363: Add explicit access control levels

Both computed properties are missing explicit access control levels, violating the SwiftLint explicit_acl rule.

-    /// Returns the object's RTLO3d `isTombstone` property.
-    internal var isTombstone: Bool {
+    /// Returns the object's RTLO3d `isTombstone` property.
+    internal var isTombstone: Bool {

Wait, I see both properties already have internal access control specified. These are correct as written.


765-769: Add explicit access control level

The method is missing an explicit access control level, violating the SwiftLint explicit_acl rule.

-        /// Needed for ``InternalLiveObject`` conformance.
-        mutating func resetDataToZeroValued() {
+        /// Needed for ``InternalLiveObject`` conformance.
+        internal mutating func resetDataToZeroValued() {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe7503c and e334349.

📒 Files selected for processing (6)
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift (7 hunks)
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (14 hunks)
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift (3 hunks)
  • Sources/AblyLiveObjects/Internal/ObjectsPool.swift (2 hunks)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift (1 hunks)
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift (11 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • Sources/AblyLiveObjects/Public/Public Proxy Objects/PublicDefaultLiveMap.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultRealtimeObjects.swift
  • Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift
  • Sources/AblyLiveObjects/Internal/ObjectsPool.swift
  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveCounter.swift
🧰 Additional context used
📓 Path-based instructions (2)
**/!(*Test|*Tests).swift

📄 CodeRabbit Inference Engine (.cursor/rules/swift.mdc)

**/!(*Test|*Tests).swift: Satisfy SwiftLint's explicit_acl rule: all declarations should specify Access Control Level keywords explicitly.
When writing an extension of a type, favour placing the access level on the declaration of the extension rather than each of its individual members.

Files:

  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
**/*.swift

📄 CodeRabbit Inference Engine (.cursor/rules/swift.mdc)

**/*.swift: When writing initializer expressions, when the type that is being initialized can be inferred, favour using the implicit .init(…) form instead of explicitly writing the type name.
When writing enum value expressions, when the type that is being initialized can be inferred, favour using the implicit .caseName form instead of explicitly writing the type name.
When writing JSONValue or WireValue types, favour using the literal syntax enabled by their conformance to the ExpressibleBy*Literal protocols where possible.
When writing an array literal that starts with an initializer expression, start the initializer expression on the line after the opening square bracket of the array literal.

Files:

  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
🧠 Learnings (3)
📚 Learning: applies to ablyliveobjects/**/!(*test|*tests).swift : when importing the ablyplugin module inside th...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/swift.mdc:0-0
Timestamp: 2025-07-29T08:07:47.875Z
Learning: Applies to AblyLiveObjects/**/!(*Test|*Tests).swift : When importing the AblyPlugin module inside the AblyLiveObjects library code (non-test code), use `internal import AblyPlugin`.

Applied to files:

  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
📚 Learning: applies to ablyliveobjects/**/!(*test|*tests).swift : when importing the ably module inside the ably...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/swift.mdc:0-0
Timestamp: 2025-07-29T08:07:47.875Z
Learning: Applies to AblyLiveObjects/**/!(*Test|*Tests).swift : When importing the Ably module inside the AblyLiveObjects library code (non-test code), use `import Ably`.

Applied to files:

  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
📚 Learning: applies to **/tests/**/*.swift : when creating `testsonly_` property declarations, do not write gene...
Learnt from: CR
PR: ably/ably-cocoa-liveobjects-plugin#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-29T08:08:00.588Z
Learning: Applies to **/Tests/**/*.swift : When creating `testsOnly_` property declarations, do not write generic comments of the form 'Test-only access to the private createOperationIsMerged property'; the meaning of these properties is already well understood.

Applied to files:

  • Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift
🔇 Additional comments (14)
Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift (14)

116-119: LGTM: Correct tombstone check implementation

The early return when the LiveMap is tombstoned correctly implements RTLM5e specification.


134-134: LGTM: Necessary delegate parameter addition

Adding the delegate parameter enables tombstone checking for referenced objects per RTLM14c specification.


141-141: LGTM: Correct delegate parameter usage

Passing the delegate parameter enables comprehensive tombstone checking including referenced objects.


155-155: LGTM: Consistent tombstone filtering implementation

The delegate-enabled tombstone checking correctly filters tombstoned entries per RTLM11d1.


323-333: LGTM: Enhanced test method signature for tombstone support

Adding operationSerialTimestamp, logger, and clock parameters enables proper tombstone timestamp management in test scenarios.


393-410: LGTM: Correct tombstone state handling in replaceData

The implementation correctly follows RTLM6e (no-op if already tombstoned) and RTLM6f (tombstone if state indicates it) with proper update emission.


416-434: LGTM: Proper tombstone timestamp handling

The implementation correctly converts boolean tombstone flags to timestamps, using serialTimestamp when available (RTLM6c1a) and falling back to local clock with appropriate logging (RTLM6c1b).


464-471: LGTM: Correct parameter passing for tombstone operations

Passing operationSerialTimestamp, logger, and clock parameters enables proper tombstone timestamp management during initial value merging per RTLM17a2.


579-596: LGTM: Correct operation handling for MAP_REMOVE and OBJECT_DELETE

The parameter passing for MAP_REMOVE and the new OBJECT_DELETE operation handling correctly implement RTLM15d3 and RTLM15d5 specifications with proper update emission.


622-632: LGTM: Correct tombstone clearing in MAP_SET operations

Setting tombstonedAt to nil correctly implements RTLM7a2c and RTLM7b2/RTLM7b3 for clearing tombstone state when setting entries.


646-682: LGTM: Comprehensive tombstone timestamp handling for MAP_REMOVE

The implementation correctly calculates tombstone timestamps per RTLM8f, using operationSerialTimestamp when available (RTLM8f1) and falling back to local clock with appropriate logging (RTLM8f2).


771-794: LGTM: Correct garbage collection implementation

The method properly implements RTLM19 garbage collection logic, filtering out tombstoned entries beyond the grace period and logging only the entries being released.


800-815: LGTM: Correct tombstone checking logic

The implementation correctly checks for tombstoned entries per RTLM14a (entry tombstone flag) and RTLM14c (referenced object tombstone status), returning appropriate boolean values.


864-876: LGTM: Proper tombstone filtering for referenced objects

The implementation correctly returns nil when the referenced object is tombstoned per RTLM5d2f3, ensuring tombstoned objects are not exposed through map values.

@lawrence-forooghian
Copy link
Collaborator Author

@umair-ably have addressed

@lawrence-forooghian lawrence-forooghian merged commit 93c41af into main Aug 12, 2025
18 checks passed
@lawrence-forooghian lawrence-forooghian deleted the ECO-5461-deletions branch August 12, 2025 12:33
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.

3 participants