Skip to content

Conversation

@sacOO7
Copy link
Collaborator

@sacOO7 sacOO7 commented Jul 1, 2025

Summary by CodeRabbit

  • New Features

    • Added LiveObjects plugin support for channel state change notifications.
    • Introduced asynchronous, sequential processing of live object messages with lifecycle state management.
    • Implemented LiveMap and LiveCounter types with real-time synchronization, tombstoning, and garbage collection.
    • Added zero-value object creation and internal object pool management with periodic garbage collection.
    • Enhanced error handling with new error codes and domain-specific exceptions.
    • Improved deserialization with fallback for unknown enum codes.
    • Added comprehensive synchronization tracking and state transition handling.
    • Introduced detailed LiveMapEntry handling including tombstone and value resolution logic.
  • Bug Fixes

    • Improved robustness in message deserialization and error reporting.
    • Fixed handling of missing fields in object messages by populating from protocol messages.
  • Tests

    • Added extensive unit tests covering live objects, synchronization, object pools, counters, maps, error handling, and serialization.
    • Introduced test helpers for mocking and spying on internal live object components.
  • Refactor

    • Changed LiveCounter interface method to return Number instead of Long for counter values.

@coderabbitai
Copy link

coderabbitai bot commented Jul 1, 2025

## Walkthrough

This change implements the core synchronization and operation handling logic for Ably LiveObjects, including object sync, state management, and operation application for LiveMap and LiveCounter types. It introduces multiple new internal classes, updates interfaces, adds error handling, and provides comprehensive unit tests for all major components and flows.

## Changes

| Files / Groups                                                                 | Change Summary |
|-------------------------------------------------------------------------------|---------------|
| `lib/src/main/java/io/ably/lib/objects/LiveObjectsPlugin.java`, `ChannelBase.java`, `LiveCounter.java` | Added `handleStateChange` to `LiveObjectsPlugin` and integrated calls in channel state changes; changed `LiveCounter.value()` return type to `Number`. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/DefaultLiveObjects.kt`, `DefaultLiveObjectsPlugin.kt` | Implemented async, sequential message processing, state management, and channel state change handling in `DefaultLiveObjects` and plugin. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/ErrorCodes.kt`, `Utils.kt`  | Added new error codes and helper for object-related errors. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/Helpers.kt`                 | Added extension to set channel serial from protocol messages. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/ObjectId.kt`                | Introduced `ObjectId` class with parsing and stringification. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/ObjectMessage.kt`           | Added `Unknown` enum values, new extension for object data validation, and doc clarifications. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt`          | Added `ObjectsManager` for sync, buffering, and applying object messages. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsPool.kt`             | Added `ObjectsPool` for managing live objects and GC logic. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsSyncTracker.kt`      | Added `ObjectsSyncTracker` for sync sequence tracking. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/serialization/JsonSerialization.kt`, `MsgpackSerialization.kt` | Improved error handling and fallback for unknown enum codes in deserialization; simplified JSON encoding/decoding. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/type/BaseLiveObject.kt`     | Introduced `BaseLiveObject` abstract class and `ObjectType` enum. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/type/livecounter/DefaultLiveCounter.kt`, `LiveCounterManager.kt` | Added internal `DefaultLiveCounter` class and manager for state/operation logic. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/DefaultLiveMap.kt`, `LiveMapManager.kt` | Added internal `DefaultLiveMap` class and manager for map state/operation logic. |
| `live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/LiveMapEntry.kt`| Added `LiveMapEntry` data class and extension functions for tombstone and GC logic. |
| `live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt`, `TestHelpers.kt` | Added test helpers for reflection, mocking, and dependency injection. |
| `live-objects/src/test/kotlin/io/ably/lib/objects/unit/ObjectIdTest.kt`, `ObjectsSyncTrackerTest.kt` | Added unit tests for `ObjectId` and `ObjectsSyncTracker`. |
| `live-objects/src/test/kotlin/io/ably/lib/objects/unit/ObjectMessageSerializationTest.kt`, `ObjectMessageSizeTest.kt` | Adjusted imports and literals in serialization tests. |
| `live-objects/src/test/kotlin/io/ably/lib/objects/unit/RealtimeObjectsTest.kt` | Renamed test class for clarity. |
| `live-objects/src/test/kotlin/io/ably/lib/objects/unit/objects/DefaultLiveObjectsTest.kt`, `ObjectsManagerTest.kt`, `ObjectsPoolTest.kt` | Added comprehensive tests for live objects orchestration, sync, and pool logic. |
| `live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/BaseLiveObjectTest.kt`, `livecounter/DefaultLiveCounterTest.kt`, `livecounter/LiveCounterManagerTest.kt`, `livemap/DefaultLiveMapTest.kt`, `livemap/LiveMapManagerTest.kt` | Added detailed unit tests for all live object types and their managers. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant ChannelBase
    participant LiveObjectsPlugin
    participant DefaultLiveObjects
    participant ObjectsManager
    participant ObjectsPool

    ChannelBase->>LiveObjectsPlugin: handleStateChange(channelName, state, hasObjects)
    LiveObjectsPlugin->>DefaultLiveObjects: handleStateChange(state, hasObjects)
    DefaultLiveObjects->>ObjectsManager: startNewSync() / endSync()
    DefaultLiveObjects->>ObjectsPool: resetToInitialPool() / clearObjectsData()
    DefaultLiveObjects->>ObjectsManager: handleObjectMessages() / handleObjectSyncMessages()
sequenceDiagram
    participant DefaultLiveObjects
    participant ObjectsManager
    participant ObjectsPool
    participant BaseLiveObject

    DefaultLiveObjects->>ObjectsManager: handleObjectMessages(list)
    ObjectsManager->>ObjectsPool: get(objectId) or createZeroValueObjectIfNotExists()
    ObjectsPool->>BaseLiveObject: applyObjectSync() / applyObject()
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~150 minutes

Assessment against linked issues

Objective (Issue #) Addressed Explanation
Implement object sync (#1107, ECO-5426) The implementation includes sync sequence tracking, applying sync messages, and state updates.
Implement incoming object operations (#1114) Object operation messages are handled with validation, buffering, and application logic.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Change of LiveCounter.value() return type (lib/src/main/java/io/ably/lib/objects/LiveCounter.java) This is a minor API broadening unrelated to the core sync and operation implementation objectives.

Possibly related PRs

  • Setup : LiveObject plugin #1085: Initial introduction of the LiveObjectsPlugin interface and integration into channel and connection classes, related as the current PR extends the plugin interface with handleStateChange.

Suggested reviewers

  • ttypic

Poem

In fields of code where objects sync and flow,
The rabbits hop as counters rise and grow.
Maps are alive, their keys in bloom,
With every state change, new features loom.
Tests abound, the garden’s neat—
LiveObjects now are quite the treat!
🐇✨


<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

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


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 32b1a365bf327c9dde9a3fe72c032bcdc191f327 and 17164c45bec51976cd50b591547fec9740b0939e.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livemap/LiveMapManagerTest.kt` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livemap/LiveMapManagerTest.kt

</details>

<details>
<summary>⏰ 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). (11)</summary>

* GitHub Check: check (21)
* GitHub Check: check (24)
* GitHub Check: check (19)
* GitHub Check: check (29)
* GitHub Check: check
* GitHub Check: check-rest-okhttp
* GitHub Check: check-realtime-okhttp
* GitHub Check: check-liveobjects
* GitHub Check: check-rest
* GitHub Check: check-realtime
* GitHub Check: build

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings
<details>
<summary>🧪 Generate unit tests</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with unit tests
- [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Post copyable unit tests in a comment
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Commit unit tests in branch `feature/object-sync`

</details>

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

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.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=ably/ably-java&utm_content=1113):

- 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](https://www.coderabbit.ai/contact-us/support) 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](https://docs.coderabbit.ai/finishing-touches/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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@sacOO7 sacOO7 changed the title [ECO-5426] Implemented objects sync [ECO-5426] Implement objects sync Jul 1, 2025
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 1, 2025 10:46 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 1, 2025 10:47 Inactive
@sacOO7 sacOO7 force-pushed the feature/object-sync branch from d57e57b to a67ea0c Compare July 2, 2025 11:48
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 2, 2025 11:49 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 2, 2025 11:51 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 3, 2025 08:57 Inactive
@sacOO7 sacOO7 force-pushed the feature/object-sync branch from 1449ae8 to 38a7c34 Compare July 3, 2025 08:57
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 3, 2025 08:59 Inactive
context engineered prompts
1. Fixed build issues for some of the code.
2. Added spec annoations for code / code blocks
@sacOO7 sacOO7 force-pushed the feature/object-sync branch from 38a7c34 to 632a518 Compare July 3, 2025 09:31
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 3, 2025 09:32 Inactive
1. Added missing error codes and updated error handling for the same
2. Created separate class for ObjectId with static constructor
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 3, 2025 12:05 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 3, 2025 12:07 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 4, 2025 12:12 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 4, 2025 12:15 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 4, 2025 13:51 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 4, 2025 13:53 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 4, 2025 14:19 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 4, 2025 14:21 Inactive
@sacOO7 sacOO7 changed the title [ECO-5426] Implement objects sync [ECO-5426][LiveObjects] Handle incoming object messages Jul 7, 2025
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 7, 2025 13:03 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 23, 2025 05:00 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: 0

🧹 Nitpick comments (1)
live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (1)

29-42: Logic is correct with minor optimization opportunity.

The method properly implements the buffering vs immediate application logic based on sync state.

Consider caching the state to avoid accessing it twice:

  internal fun handleObjectMessages(objectMessages: List<ObjectMessage>) {
+   val currentState = liveObjects.state
-   if (liveObjects.state != ObjectsState.SYNCED) {
+   if (currentState != ObjectsState.SYNCED) {
      // RTO7 - The client receives object messages in realtime over the channel concurrently with the sync sequence.
      // Some of the incoming object messages may have already been applied to the objects described in
      // the sync sequence, but others may not; therefore we must buffer these messages so that we can apply
      // them to the objects once the sync is complete.
-     Log.v(tag, "Buffering ${objectMessages.size} object messages, state: $liveObjects.state")
+     Log.v(tag, "Buffering ${objectMessages.size} object messages, state: $currentState")
      bufferedObjectOperations.addAll(objectMessages) // RTO8a
      return
    }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3d45982 and f7a5ce9.

📒 Files selected for processing (6)
  • live-objects/src/main/kotlin/io/ably/lib/objects/Helpers.kt (1 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/ObjectMessage.kt (4 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (1 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/serialization/JsonSerialization.kt (1 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/serialization/MsgpackSerialization.kt (5 hunks)
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/ObjectMessageSizeTest.kt (1 hunks)
🧠 Learnings (2)
📓 Common learnings
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.
Learnt from: sacOO7
PR: ably/ably-java#1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.
Learnt from: sacOO7
PR: ably/ably-java#1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.
Learnt from: sacOO7
PR: ably/ably-java#1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/MsgpackSerialization.kt:207-211
Timestamp: 2025-06-23T14:28:23.301Z
Learning: In the Ably Java LiveObjects MessagePack deserialization code, the `action` field in ObjectOperation is guaranteed to always be present in the protocol, so using a default value during deserialization is acceptable and won't mask real protocol errors.
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (`objects as Array<ObjectMessage>`) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.
Learnt from: sacOO7
PR: ably/ably-java#1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:48-61
Timestamp: 2025-06-03T09:15:18.827Z
Learning: User sacOO7 prefers simple test utilities without extensive error handling, believing tests should fail fast if incorrect field/method names are used rather than having defensive programming.
live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (4)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (objects as Array<ObjectMessage>) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.

Learnt from: sacOO7
PR: #1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.

🧬 Code Graph Analysis (1)
live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (1)
live-objects/src/main/kotlin/io/ably/lib/objects/Utils.kt (1)
  • clientError (33-33)
🚧 Files skipped from review as they are similar to previous changes (5)
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/ObjectMessageSizeTest.kt
  • live-objects/src/main/kotlin/io/ably/lib/objects/Helpers.kt
  • live-objects/src/main/kotlin/io/ably/lib/objects/serialization/JsonSerialization.kt
  • live-objects/src/main/kotlin/io/ably/lib/objects/serialization/MsgpackSerialization.kt
  • live-objects/src/main/kotlin/io/ably/lib/objects/ObjectMessage.kt
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.
Learnt from: sacOO7
PR: ably/ably-java#1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.
Learnt from: sacOO7
PR: ably/ably-java#1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.
Learnt from: sacOO7
PR: ably/ably-java#1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/MsgpackSerialization.kt:207-211
Timestamp: 2025-06-23T14:28:23.301Z
Learning: In the Ably Java LiveObjects MessagePack deserialization code, the `action` field in ObjectOperation is guaranteed to always be present in the protocol, so using a default value during deserialization is acceptable and won't mask real protocol errors.
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (`objects as Array<ObjectMessage>`) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.
Learnt from: sacOO7
PR: ably/ably-java#1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:48-61
Timestamp: 2025-06-03T09:15:18.827Z
Learning: User sacOO7 prefers simple test utilities without extensive error handling, believing tests should fail fast if incorrect field/method names are used rather than having defensive programming.
live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (4)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (objects as Array<ObjectMessage>) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.

Learnt from: sacOO7
PR: #1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.

🧬 Code Graph Analysis (1)
live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (1)
live-objects/src/main/kotlin/io/ably/lib/objects/Utils.kt (1)
  • clientError (33-33)
⏰ 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). (11)
  • GitHub Check: check-realtime-okhttp
  • GitHub Check: check-liveobjects
  • GitHub Check: check-rest
  • GitHub Check: check-rest-okhttp
  • GitHub Check: check (29)
  • GitHub Check: check-realtime
  • GitHub Check: check (24)
  • GitHub Check: check
  • GitHub Check: check (19)
  • GitHub Check: build
  • GitHub Check: check (21)
🔇 Additional comments (10)
live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (10)

12-23: Well-structured class with clear separation of concerns.

The class design follows good practices with proper encapsulation, clear field purposes, and comprehensive spec documentation. The dependency injection pattern with DefaultLiveObjects provides good testability.


49-66: Correctly implements sync sequence protocol.

The method properly handles the sync sequence lifecycle with appropriate checks for new sync sequences and completion. The deferred state event mechanism ensures proper event ordering for listeners.


73-81: Properly implements sync sequence initialization.

The method correctly clears stale data and sets up the new sync sequence according to the specification. The order of operations ensures clean state before starting the new sync.


88-99: Correctly implements sync sequence completion.

The method follows the proper order of operations: apply sync data first, then buffered operations, followed by cleanup. This ensures consistent object state and proper resource management.


105-115: Simple and correct utility methods.

These methods provide controlled access for clearing internal state, which is appropriate for the integration with DefaultLiveObjects.handleStateChange.


122-155: Correctly implements sync data application with proper object lifecycle management.

The method handles both existing and new objects appropriately, maintains object pool consistency by removing stale objects, and efficiently batches notifications after all updates are complete.


162-186: Well-designed object message application with robust error handling.

The zero-value object creation strategy elegantly handles operations for unknown object IDs, while the error handling appropriately skips invalid messages with proper logging without stopping the entire batch.


193-208: Proper sync message validation and application.

The method correctly validates that object states contain the required data (counter or map) and handles invalid messages gracefully with appropriate logging.


215-221: Clean object creation with proper type dispatch and validation.

The method uses appropriate pattern matching to create the correct object type and properly validates that the object state contains required data, throwing a meaningful error for invalid states.


223-227: Appropriate cleanup implementation.

The dispose method properly clears all internal state, providing clean resource management for the ObjectsManager lifecycle.

@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 23, 2025 08:00 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 23, 2025 08:03 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 23, 2025 08:46 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 23, 2025 08:47 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: 0

♻️ Duplicate comments (4)
live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/BaseLiveObjectTest.kt (3)

51-51: Fix the reflection method lookup missing parameter types.

The findMethod call is missing parameter types, which will cause NoSuchMethodException at runtime since canApplyOperation requires two String parameters.

Apply this fix:

-    val method = BaseLiveObject::class.java.findMethod("canApplyOperation")
+    val method = BaseLiveObject::class.java.getDeclaredMethod("canApplyOperation", String::class.java, String::class.java)

143-145: Fix incorrect test comment.

The comment states "Should return false" but the test correctly uses assertTrue. The comment should match the test expectation.

-    assertTrue(liveMap.canApplyOperation("site1", "serialA"),
-      "Should return false when message serial 'serialA' < siteSerial 'serial2'")
+    assertTrue(liveMap.canApplyOperation("site1", "serialA"),
+      "Should return true when message serial 'serialA' > siteSerial 'serial2'")

105-105: Avoid direct mutation of internal state in tests.

The test directly mutates siteTimeserials, which is an internal mutable map. This breaks encapsulation and ties tests to implementation details.

Consider providing an internal test API in BaseLiveObject for controlled state manipulation, such as setSiteSerial(siteId: String, serial: String), and update the test to use this API instead of direct map access.

live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livecounter/LiveCounterManagerTest.kt (1)

124-124: Fix incorrect comment.

The comment states "Should not change (still 0)" but the test expects the value to remain 4.0, not 0.

-    assertEquals(4.0, liveCounter.data.get()) // Should not change (still 0)
+    assertEquals(4.0, liveCounter.data.get()) // Should not change (still 4)
🧹 Nitpick comments (1)
live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/DefaultLiveMap.kt (1)

40-74: Track TODO implementations for future completion.

All public API methods are currently stubbed with TODO. This is expected for the current PR scope but should be tracked for future implementation.

Would you like me to create an issue to track the implementation of these LiveMap public API methods (get, set, remove, entries, keys, values, size, and async variants)?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 45fde3f and 529efd8.

📒 Files selected for processing (15)
  • live-objects/src/main/kotlin/io/ably/lib/objects/DefaultLiveObjects.kt (2 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/DefaultLiveObjectsPlugin.kt (2 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (1 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsPool.kt (1 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/type/livecounter/DefaultLiveCounter.kt (1 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/type/livecounter/LiveCounterManager.kt (1 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/DefaultLiveMap.kt (1 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/LiveMapEntry.kt (1 hunks)
  • live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/LiveMapManager.kt (1 hunks)
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/TestHelpers.kt (2 hunks)
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/objects/DefaultLiveObjectsTest.kt (1 hunks)
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/objects/ObjectsManagerTest.kt (1 hunks)
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/objects/ObjectsPoolTest.kt (1 hunks)
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/BaseLiveObjectTest.kt (1 hunks)
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livecounter/LiveCounterManagerTest.kt (1 hunks)
🧠 Learnings (8)
📓 Common learnings
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.
Learnt from: sacOO7
PR: ably/ably-java#1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.
Learnt from: sacOO7
PR: ably/ably-java#1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.
Learnt from: sacOO7
PR: ably/ably-java#1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/MsgpackSerialization.kt:207-211
Timestamp: 2025-06-23T14:28:23.301Z
Learning: In the Ably Java LiveObjects MessagePack deserialization code, the `action` field in ObjectOperation is guaranteed to always be present in the protocol, so using a default value during deserialization is acceptable and won't mask real protocol errors.
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (`objects as Array<ObjectMessage>`) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.
Learnt from: sacOO7
PR: ably/ably-java#1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:48-61
Timestamp: 2025-06-03T09:15:18.827Z
Learning: User sacOO7 prefers simple test utilities without extensive error handling, believing tests should fail fast if incorrect field/method names are used rather than having defensive programming.
live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/BaseLiveObjectTest.kt (3)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (objects as Array<ObjectMessage>) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.

live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (4)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (objects as Array<ObjectMessage>) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.

Learnt from: sacOO7
PR: #1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.

live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/DefaultLiveMap.kt (3)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

Learnt from: sacOO7
PR: #1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.

live-objects/src/test/kotlin/io/ably/lib/objects/unit/TestHelpers.kt (8)

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1095
File: live-objects/src/test/kotlin/io/ably/lib/objects/unit/LiveObjectTest.kt:1-6
Timestamp: 2025-06-05T10:24:28.789Z
Learning: In Kotlin, functions, classes, and other declarations within the same package are automatically accessible without explicit import statements. Do not suggest adding imports for functions/classes that are already in the same package.

Learnt from: sacOO7
PR: #1059
File: lib/src/test/java/io/ably/lib/chat/ChatMessagesTest.java:124-143
Timestamp: 2025-01-22T13:13:02.809Z
Learning: In test classes, prefer keeping setup code within individual test methods rather than extracting to shared helpers, as it makes tests more independent and readable by keeping the context visible within each test.

Learnt from: sacOO7
PR: #1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.

Learnt from: sacOO7
PR: #1095
File: live-objects/src/test/kotlin/io/ably/lib/objects/integration/setup/IntegrationTest.kt:87-89
Timestamp: 2025-06-06T09:28:12.298Z
Learning: The Sandbox.kt file in ably-java live-objects module already has comprehensive HTTP retry mechanism using HttpRequestRetry with 5 retries, exponential backoff, and automatic retry on non-success responses and timeout exceptions.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (objects as Array<ObjectMessage>) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.

Learnt from: sacOO7
PR: #1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.

live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsPool.kt (1)

Learnt from: sacOO7
PR: #1095
File: live-objects/src/test/kotlin/io/ably/lib/objects/integration/setup/IntegrationTest.kt:87-89
Timestamp: 2025-06-06T09:28:12.298Z
Learning: The Sandbox.kt file in ably-java live-objects module already has comprehensive HTTP retry mechanism using HttpRequestRetry with 5 retries, exponential backoff, and automatic retry on non-success responses and timeout exceptions.

live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/LiveMapEntry.kt (2)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livecounter/LiveCounterManagerTest.kt (1)

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

🧬 Code Graph Analysis (3)
live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/BaseLiveObjectTest.kt (1)
live-objects/src/test/kotlin/io/ably/lib/objects/unit/TestHelpers.kt (1)
  • getDefaultLiveObjectsWithMockedDeps (79-97)
live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (1)
live-objects/src/main/kotlin/io/ably/lib/objects/Utils.kt (1)
  • clientError (33-33)
live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livecounter/LiveCounterManagerTest.kt (1)
live-objects/src/test/kotlin/io/ably/lib/objects/unit/TestHelpers.kt (1)
  • getDefaultLiveCounterWithMockedDeps (113-124)
🚧 Files skipped from review as they are similar to previous changes (8)
  • live-objects/src/main/kotlin/io/ably/lib/objects/DefaultLiveObjectsPlugin.kt
  • live-objects/src/main/kotlin/io/ably/lib/objects/type/livecounter/DefaultLiveCounter.kt
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/objects/ObjectsPoolTest.kt
  • live-objects/src/main/kotlin/io/ably/lib/objects/type/livecounter/LiveCounterManager.kt
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/objects/ObjectsManagerTest.kt
  • live-objects/src/main/kotlin/io/ably/lib/objects/DefaultLiveObjects.kt
  • live-objects/src/test/kotlin/io/ably/lib/objects/unit/objects/DefaultLiveObjectsTest.kt
  • live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/LiveMapManager.kt
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.
Learnt from: sacOO7
PR: ably/ably-java#1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.
Learnt from: sacOO7
PR: ably/ably-java#1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.
Learnt from: sacOO7
PR: ably/ably-java#1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/MsgpackSerialization.kt:207-211
Timestamp: 2025-06-23T14:28:23.301Z
Learning: In the Ably Java LiveObjects MessagePack deserialization code, the `action` field in ObjectOperation is guaranteed to always be present in the protocol, so using a default value during deserialization is acceptable and won't mask real protocol errors.
Learnt from: sacOO7
PR: ably/ably-java#1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (`objects as Array<ObjectMessage>`) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.
Learnt from: sacOO7
PR: ably/ably-java#1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:48-61
Timestamp: 2025-06-03T09:15:18.827Z
Learning: User sacOO7 prefers simple test utilities without extensive error handling, believing tests should fail fast if incorrect field/method names are used rather than having defensive programming.
live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/BaseLiveObjectTest.kt (3)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (objects as Array<ObjectMessage>) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.

live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (4)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (objects as Array<ObjectMessage>) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.

Learnt from: sacOO7
PR: #1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.

live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/DefaultLiveMap.kt (3)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

Learnt from: sacOO7
PR: #1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.

live-objects/src/test/kotlin/io/ably/lib/objects/unit/TestHelpers.kt (8)

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1095
File: live-objects/src/test/kotlin/io/ably/lib/objects/unit/LiveObjectTest.kt:1-6
Timestamp: 2025-06-05T10:24:28.789Z
Learning: In Kotlin, functions, classes, and other declarations within the same package are automatically accessible without explicit import statements. Do not suggest adding imports for functions/classes that are already in the same package.

Learnt from: sacOO7
PR: #1059
File: lib/src/test/java/io/ably/lib/chat/ChatMessagesTest.java:124-143
Timestamp: 2025-01-22T13:13:02.809Z
Learning: In test classes, prefer keeping setup code within individual test methods rather than extracting to shared helpers, as it makes tests more independent and readable by keeping the context visible within each test.

Learnt from: sacOO7
PR: #1087
File: lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java:32-34
Timestamp: 2025-05-27T12:11:25.084Z
Learning: In LiveObjects implementation (lib/src/main/java/io/ably/lib/objects/LiveObjectsAdapter.java), the send method intentionally hardcodes queueEvents to true rather than respecting ably.options.queueMessages. This is because LiveObjects requires reliable message delivery to ensure proper state synchronization and acknowledgment, unlike other realtime components that may allow configurable queuing behavior.

Learnt from: sacOO7
PR: #1095
File: live-objects/src/test/kotlin/io/ably/lib/objects/integration/setup/IntegrationTest.kt:87-89
Timestamp: 2025-06-06T09:28:12.298Z
Learning: The Sandbox.kt file in ably-java live-objects module already has comprehensive HTTP retry mechanism using HttpRequestRetry with 5 retries, exponential backoff, and automatic retry on non-success responses and timeout exceptions.

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:38-46
Timestamp: 2025-06-23T14:14:17.847Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class uses intentional unsafe casting (objects as Array<ObjectMessage>) without type validation in both writeMsgpackArray and asJsonArray methods. This is a deliberate design decision to keep the dynamically-loaded class simple and let ClassCastException occur naturally for type mismatches.

Learnt from: sacOO7
PR: #1085
File: lib/src/main/java/io/ably/lib/objects/LiveObjects.java:0-0
Timestamp: 2025-05-20T13:12:19.013Z
Learning: The LiveObjects interface does not currently include public API methods for resource management (dispose) or change listeners, as these features are not yet implemented.

live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsPool.kt (1)

Learnt from: sacOO7
PR: #1095
File: live-objects/src/test/kotlin/io/ably/lib/objects/integration/setup/IntegrationTest.kt:87-89
Timestamp: 2025-06-06T09:28:12.298Z
Learning: The Sandbox.kt file in ably-java live-objects module already has comprehensive HTTP retry mechanism using HttpRequestRetry with 5 retries, exponential backoff, and automatic retry on non-success responses and timeout exceptions.

live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/LiveMapEntry.kt (2)

Learnt from: sacOO7
PR: #1106
File: live-objects/src/main/kotlin/io/ably/lib/objects/serialization/Serialization.kt:25-29
Timestamp: 2025-06-23T14:18:25.315Z
Learning: In the ably-java codebase, the DefaultLiveObjectSerializer class methods like writeMsgpackArray will have their type signatures updated to be non-nullable since the calling sites ensure objects are never null when passed to these methods.

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livecounter/LiveCounterManagerTest.kt (1)

Learnt from: sacOO7
PR: #1092
File: live-objects/src/test/kotlin/io/ably/lib/objects/TestUtils.kt:21-32
Timestamp: 2025-06-03T09:15:15.338Z
Learning: In test utility code for the Ably Java Live Objects module, the team prefers to keep reflection-based field access utilities simple without additional error handling, allowing tests to fail fast if incorrect field names are used.

🧬 Code Graph Analysis (3)
live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/BaseLiveObjectTest.kt (1)
live-objects/src/test/kotlin/io/ably/lib/objects/unit/TestHelpers.kt (1)
  • getDefaultLiveObjectsWithMockedDeps (79-97)
live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (1)
live-objects/src/main/kotlin/io/ably/lib/objects/Utils.kt (1)
  • clientError (33-33)
live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livecounter/LiveCounterManagerTest.kt (1)
live-objects/src/test/kotlin/io/ably/lib/objects/unit/TestHelpers.kt (1)
  • getDefaultLiveCounterWithMockedDeps (113-124)
⏰ 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). (11)
  • GitHub Check: check (21)
  • GitHub Check: check (29)
  • GitHub Check: check (24)
  • GitHub Check: check (19)
  • GitHub Check: check-liveobjects
  • GitHub Check: check-rest
  • GitHub Check: check-realtime
  • GitHub Check: check-rest-okhttp
  • GitHub Check: check-realtime-okhttp
  • GitHub Check: check
  • GitHub Check: build
🔇 Additional comments (39)
live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/LiveMapEntry.kt (4)

10-15: LGTM: Well-structured data class for live map entries.

The data class appropriately models live map entry state with optional tombstone metadata and data payload. Default values and nullable types are correctly applied.


21-33: LGTM: Robust tombstone checking logic.

The extension function correctly implements the tombstone checking logic per RTLM14 specification, handling both direct tombstoning and reference tombstoning with proper null safety.


39-53: LGTM: Comprehensive value resolution with tombstone awareness.

The value resolution logic correctly handles all scenarios including tombstones, primitive values, and object references with proper null safety and tombstone filtering.


58-61: LGTM: Correct GC eligibility implementation.

The GC eligibility check correctly validates tombstone status and grace period using current system time. The logic properly handles null tombstone timestamps.

live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsManager.kt (6)

12-23: LGTM: Well-structured manager with clear responsibilities.

The class properly encapsulates sync data pooling and message buffering with clear documentation referencing specifications. The mutable collections are appropriately scoped as private.


29-42: LGTM: Correct buffering logic for non-synced states.

The message handling properly buffers operations during non-synced states per RTO7/RTO8 specifications, with appropriate logging and immediate application when synced.


164-186: LGTM: Robust operation application with comprehensive validation.

The method correctly handles missing operations, unknown actions, and creates zero-value objects for unknown IDs per RTO9 specification. The logging for skipped messages provides good debugging information.


193-208: LGTM: Proper sync message validation and collection.

The sync message handling correctly validates object state presence and content, with appropriate logging for invalid messages. The validation ensures either counter or map data is present.


215-221: LGTM: Type-safe object creation from state.

The factory method correctly creates objects based on state type with appropriate error handling using the established clientError utility for protocol violations.


223-227: LGTM: Proper resource cleanup in dispose method.

The dispose method correctly clears both internal collections, ensuring no memory leaks.

live-objects/src/main/kotlin/io/ably/lib/objects/type/livemap/DefaultLiveMap.kt (5)

18-22: LGTM: Well-structured class with proper inheritance.

The class correctly implements LiveMap and extends BaseLiveObject with appropriate constructor parameters and default semantics. The private constructor with companion factory method is a good pattern.


29-29: LGTM: Thread-safe data storage choice.

Using ConcurrentHashMap is appropriate for thread-safe access from both public APIs and internal manager operations.


76-84: LGTM: Proper delegation to manager for core operations.

The delegation pattern to LiveMapManager for validation, state application, and operation handling is well-implemented and maintains separation of concerns.


86-89: LGTM: Efficient data clearing with diff calculation.

The clearData method efficiently calculates updates from current state to empty state before clearing, enabling proper change notifications.


100-103: LGTM: Standard zero-value factory implementation.

The factory method follows the established pattern for creating zero-value live objects as seen in the codebase.

live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsPool.kt (7)

14-23: LGTM: Well-defined configuration constants.

The GC configuration constants are appropriately defined with clear documentation explaining the 24-hour grace period rationale for avoiding serial ordering issues.


35-57: LGTM: Proper initialization with root object and GC setup.

The pool correctly initializes with a root object and starts the GC coroutine. The use of SupervisorJob ensures GC failures don't affect the parent scope.


77-80: LGTM: Safe pool reset preserving root object.

The reset logic correctly preserves the root object while clearing others, maintaining object reference stability as documented.


106-119: LGTM: Robust zero-value object creation.

The method correctly handles existing objects, parses object IDs for type determination, and creates appropriate zero-value objects with proper pool registration.


124-132: LGTM: Safe garbage collection with eligibility checks.

The GC interval handler correctly removes eligible objects and calls per-object GC methods on retained objects.


137-148: LGTM: Robust GC coroutine with error handling.

The GC coroutine properly handles exceptions without terminating the loop and uses isActive for clean cancellation. The exception logging provides good debugging information.


154-159: LGTM: Complete resource cleanup in dispose method.

The disposal method correctly cancels the GC job, cancels the coroutine scope, and clears the pool, ensuring no resource leaks.

live-objects/src/test/kotlin/io/ably/lib/objects/unit/TestHelpers.kt (6)

3-10: LGTM! Import structure is clear and appropriate.

The combination of wildcard and specific imports provides good clarity for the test helper dependencies.


21-45: LGTM! Well-structured channel mock setup.

The function creates a realistic channel mock with proper state management and flexible configuration options.


47-58: LGTM! Simple and effective mock factory functions.

The functions provide clean abstractions for creating test dependencies, and the size extension follows the established pattern for reflection-based field access.


65-97: LGTM! Comprehensive DefaultLiveObjects mocking setup.

The helper provides flexible mocking options with clear access to internal dependencies, enabling thorough testing of DefaultLiveObjects behavior.


109-124: LGTM! Consistent LiveCounter mocking pattern.

The helper maintains the same flexible mocking approach as other components, enabling controlled testing of DefaultLiveCounter behavior.


136-151: LGTM! Consistent LiveMap mocking pattern.

The helper follows the established pattern, providing uniform mocking capabilities across all live object types.

live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livecounter/LiveCounterManagerTest.kt (11)

1-9: LGTM! Appropriate imports for LiveCounterManager testing.

All necessary dependencies are imported for comprehensive testing of the LiveCounterManager component.


12-32: LGTM! Comprehensive test for state override functionality.

The test correctly validates RTLC6 specification requirements, including proper data override and update calculation.


35-62: LGTM! Correct test for create operation merging.

The test properly validates the merging of create operations during state synchronization according to RTLC6d requirements.


65-85: LGTM! Proper error handling test for unsupported actions.

The test correctly validates that unsupported operations throw appropriate exceptions with the right error codes.


87-103: LGTM! Correct test for counter create operation.

The test properly validates the application of counter create operations and the setting of the merged flag.


128-150: LGTM! Correct test for applying unmerged create operations.

The test properly validates the application of create operations when the merged flag is false, including proper data accumulation.


152-172: LGTM! Proper test for null count handling in create operations.

The test correctly validates that null counts default to 0 and the merged flag is properly set.


174-192: LGTM! Correct test for counter increment operations.

The test properly validates the application of counter increment operations according to RTLC specification requirements.


194-214: LGTM! Proper error handling test for missing payloads.

The test correctly validates that increment operations with missing payloads throw appropriate exceptions.


217-235: LGTM! Focused test for RTLC9b increment behavior.

The test properly validates the specific requirements of RTLC9b for counter increment operations.


237-255: LGTM! Proper test for null amount handling in increments.

The test correctly validates that null amounts in increment operations are treated as 0, resulting in no change to the counter value.

@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 26, 2025 09:40 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 26, 2025 09:43 Inactive
@sacOO7 sacOO7 force-pushed the feature/object-sync branch from e72b521 to 529efd8 Compare July 26, 2025 10:08
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 26, 2025 10:09 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 26, 2025 10:11 Inactive
- Removed unnecessary `encoding` field used to detect json values
@sacOO7 sacOO7 force-pushed the feature/object-sync branch from ad0ef62 to 4b084f8 Compare July 29, 2025 10:01
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 29, 2025 10:02 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 29, 2025 10:03 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 31, 2025 14:58 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 31, 2025 15:01 Inactive
@sacOO7 sacOO7 force-pushed the feature/object-sync branch from b206815 to 32b1a36 Compare July 31, 2025 15:14
@github-actions github-actions bot temporarily deployed to staging/pull/1113/features July 31, 2025 15:15 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/1113/javadoc July 31, 2025 15:16 Inactive
@sacOO7
Copy link
Collaborator Author

sacOO7 commented Aug 6, 2025

closing in favor of #1137

@sacOO7 sacOO7 closed this Aug 6, 2025
@ttypic ttypic deleted the feature/object-sync branch September 28, 2025 21:55
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.

LiveObject: Implement incoming object operations LiveObject: Implement object sync

3 participants