Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CHA-M5] Improved message subscription history implementation #78

Merged
merged 1 commit into from
Dec 4, 2024

Conversation

sacOO7
Copy link
Contributor

@sacOO7 sacOO7 commented Dec 3, 2024

  1. Removed use of custom DeferredValue, which can become bottleneck in heavy concurrency scenarios.
  2. Replaced with thread-safe native CompletableDeferred.
  3. Removed use of unnecessary lock and listeners properties from Messages, since desired behaviour is already taken care by the thread-safe CompletableDeferred
  4. Updated tests using DeferredValue with CompletableDeferred

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced message handling with improved subscription and channel serial management.
    • Introduced specific error handling functions for client and server errors.
  • Bug Fixes

    • Streamlined error handling and improved clarity in message subscription tests.
  • Refactor

    • Transitioned from DeferredValue to CompletableDeferred for better concurrency handling.
    • Removed unnecessary internal classes and functions to simplify the codebase.
  • Tests

    • Updated tests to reflect changes in asynchronous handling, ensuring robustness in message and presence subscriptions.

Copy link

coderabbitai bot commented Dec 3, 2024

Walkthrough

The changes primarily focus on enhancing the message handling and subscription logic within the Messages.kt and related files of the chat-android project. Key modifications include the transition from DeferredValue to CompletableDeferred for asynchronous operations, the introduction of a ConcurrentHashMap for managing channel serials, and the removal of the DeferredValue class. Additionally, new error handling functions were added, and several test cases were updated to reflect these changes, ensuring robust handling of message subscriptions and channel states.

Changes

File Path Change Summary
chat-android/src/main/java/com/ably/chat/Messages.kt - Changed fromSerialProvider from DeferredValue<String> to CompletableDeferred<String>.
- Added channelSerialMap as ConcurrentHashMap<PubSubMessageListener, CompletableDeferred<String>>.
- Updated deferredChannelSerial to CompletableDeferred<String>.
- Modified subscribe method to manage channel serials.
- Streamlined error handling with a custom clientError function.
- Enhanced updateChannelSerialsAfterDiscontinuity method for simultaneous updates.
chat-android/src/main/java/com/ably/chat/Utils.kt - Added clientError and serverError functions for error handling.
- Removed DeferredValue<T> class.
chat-android/src/test/java/com/ably/chat/MessagesTest.kt - Updated tests to use CompletableDeferred<MessageEvent>() instead of DeferredValue<MessageEvent>().
- Simplified access to eventListeners field.
chat-android/src/test/java/com/ably/chat/PresenceTest.kt - Changed DeferredValue<PresenceEvent>() to CompletableDeferred<PresenceEvent>() in tests.
chat-android/src/test/java/com/ably/chat/RoomReactionsTest.kt - Updated deferredValue from DeferredValue<Reaction>() to CompletableDeferred<Reaction>().
chat-android/src/test/java/com/ably/chat/TestUtils.kt - Removed clientError and serverError functions.

Possibly related PRs

Suggested reviewers

  • ttypic

🐰 In the land of code where messages flow,
Changes abound, as we all know!
From DeferredValue we take our leave,
With CompletableDeferred, we now believe.
Errors are clearer, subscriptions refined,
A brighter path for all, we find! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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

Copy link

github-actions bot commented Dec 3, 2024

Code Coverage

File Coverage [83.43%]
chat-android/src/main/java/com/ably/chat/Messages.kt 91.11%
chat-android/src/main/java/com/ably/chat/Utils.kt 75.82%
Total Project Coverage 81.94%

@sacOO7 sacOO7 force-pushed the improve/messages-feature-channel-serial branch 2 times, most recently from ffb652d to f351de0 Compare December 3, 2024 11:59
@sacOO7 sacOO7 marked this pull request as ready for review December 3, 2024 13:07
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
chat-android/src/test/java/com/ably/chat/MessagesTest.kt (1)

269-269: Avoid using reflection to access private fields in tests

Accessing eventListeners via reflection may lead to maintenance issues. Consider exposing necessary internal states through public or internal APIs designed for testing.

Apply this diff to replace reflection with a testing accessor:

- val eventListeners = getPrivateField<HashMap<*, *>>("eventListeners")
+ val eventListeners = channel.getEventListenersForTesting()

And in the Channel class, add:

// In the Channel class
internal fun getEventListenersForTesting(): HashMap<*, *> {
    return eventListeners
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 274d226 and f351de0.

📒 Files selected for processing (6)
  • chat-android/src/main/java/com/ably/chat/Messages.kt (6 hunks)
  • chat-android/src/main/java/com/ably/chat/Utils.kt (1 hunks)
  • chat-android/src/test/java/com/ably/chat/MessagesTest.kt (4 hunks)
  • chat-android/src/test/java/com/ably/chat/PresenceTest.kt (4 hunks)
  • chat-android/src/test/java/com/ably/chat/RoomReactionsTest.kt (2 hunks)
  • chat-android/src/test/java/com/ably/chat/TestUtils.kt (0 hunks)
💤 Files with no reviewable changes (1)
  • chat-android/src/test/java/com/ably/chat/TestUtils.kt
🧰 Additional context used
📓 Learnings (1)
chat-android/src/test/java/com/ably/chat/RoomReactionsTest.kt (1)
Learnt from: sacOO7
PR: ably/ably-chat-kotlin#66
File: chat-android/src/test/java/com/ably/chat/RoomReactionsTest.kt:44-50
Timestamp: 2024-11-28T11:08:42.524Z
Learning: The test cases for verifying behavior when the room is not in the ATTACHED state are covered in `chat-android/src/test/java/com/ably/chat/room/RoomEnsureAttachedTest.kt`.
🔇 Additional comments (16)
chat-android/src/test/java/com/ably/chat/RoomReactionsTest.kt (2)

13-13: LGTM! Import aligns with the concurrency improvements.

The addition of CompletableDeferred import supports the transition from custom DeferredValue to Kotlin's built-in concurrent primitives.


63-66: LGTM! Proper implementation of CompletableDeferred.

The changes correctly implement the transition from DeferredValue to CompletableDeferred:

  • Using CompletableDeferred<Reaction>() for type-safe deferred value
  • Using complete(it) method which is the standard way to complete a CompletableDeferred

This change aligns with the PR's objective of improving concurrency handling by using native thread-safe primitives.

chat-android/src/test/java/com/ably/chat/PresenceTest.kt (4)

14-14: LGTM: Appropriate import for CompletableDeferred

The addition of CompletableDeferred import aligns with the PR's objective to improve concurrency handling.


46-49: LGTM: Clean transition to CompletableDeferred

The change from DeferredValue to CompletableDeferred maintains the same test behavior while providing better thread-safety. The use of complete(it) is the correct method for CompletableDeferred.


82-85: LGTM: Consistent implementation pattern

The changes maintain consistency with the previous test method, following the same CompletableDeferred pattern.


119-122: LGTM: Complete transition to CompletableDeferred

The consistent application of CompletableDeferred across all test methods completes the transition from DeferredValue. The test coverage and behavior remain intact while gaining improved thread-safety.

Let's verify that all DeferredValue usage has been replaced:

✅ Verification successful

Transition to CompletableDeferred is complete and consistent

All instances of deferred values in the test files are using CompletableDeferred. The search results show that the pattern is consistently applied across all test files (PresenceTest.kt, MessagesTest.kt, RoomReactionsTest.kt, SandboxTest.kt), and there are no remaining instances of the old DeferredValue pattern. The transition has been successfully completed while maintaining the same testing behavior.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify no remaining DeferredValue usage
# Search for any remaining references to DeferredValue
rg "DeferredValue" 

Length of output: 18


Script:

#!/bin/bash
# Let's search for any deferred value usage patterns more broadly
rg -i "deferred.*value"

# Also check for any remaining test files that might use similar patterns
fd "Test.kt" --exec rg -l "Deferred"

Length of output: 3004

chat-android/src/main/java/com/ably/chat/Messages.kt (6)

11-12: Imports are appropriate for concurrency improvements

The addition of ConcurrentHashMap and CompletableDeferred imports is necessary to support the enhanced concurrency mechanisms implemented in the code.


241-248: Use of ConcurrentHashMap and CompletableDeferred enhances thread safety

Declaring channelSerialMap as a ConcurrentHashMap and deferredChannelSerial as a CompletableDeferred ensures thread-safe operations when managing channel serials across multiple subscribers.


291-297: Initialize channel serial for new subscribers when channel is attached

The code correctly assigns the current channelSerial to new subscribers if the channel state is attached, ensuring they have the appropriate starting point for message history.


307-308: Appropriate error handling when subscription is already unsubscribed

Throwing a clientError if the messageListener is not found in channelSerialMap provides clear feedback that the subscription has been unsubscribed.


319-319: Verify that channelSerial is available when channel is attached

Ensure that channel.properties.channelSerial is not null when the channel is in the attached state to avoid unexpected errors.

Run the following script to check if channelSerial is always set upon attachment:

✅ Verification successful

Based on the code analysis, I can now generate the final response:

The channelSerial validation is correctly implemented

The code shows that:

  1. The channelSerial is properly checked during channel attachment, as evidenced by the test cases that verify the serial's availability.
  2. The implementation updates channelSerial appropriately during channel state changes, particularly after reattachment.
  3. The test cases demonstrate that channelSerial is consistently available in the attached state and is properly propagated to subscriptions.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Ensure that channelSerial is non-null when channel state is 'attached'

# Search for places where the channel becomes attached without a channelSerial
rg 'channel\.state\s*=\s*ChannelState\.attached' -A 5 | rg -C 5 'channelSerial\s*=\s*null'

Length of output: 92


Script:

#!/bin/bash
# Let's analyze the channel attachment and channelSerial handling more thoroughly

# First, let's find where channelSerial is set during attachment
rg -A 5 "channelSerial\s*=.*" 

# Also check for attachment state handling
rg -A 10 "ChannelState\.attached" 

# Look for any error handling related to channelSerial
rg -A 5 "channelSerial.*null|channelSerial.*undefined"

Length of output: 22613


Script:

#!/bin/bash
# Let's look at the implementation of requireChannelSerial and where channelSerial is set during attachment

# Find the implementation of requireChannelSerial
ast-grep --pattern 'fun requireChannelSerial() {
  $$$
}'

# Look for where channelSerial is set during attachment process
rg -B 5 'properties\.channelSerial' 

# Check for attachment protocol implementation
ast-grep --pattern 'onChannelStateChanged($$$) {
  $$$
}'

Length of output: 3775


324-324: Confirm attachSerial is set when channel is attached

Similarly, verify that channel.properties.attachSerial is not null in the attached state to prevent errors when calling requireAttachSerial().

Run the following script to ensure attachSerial is always set upon attachment:

chat-android/src/main/java/com/ably/chat/Utils.kt (1)

200-202: Addition of helper functions for error handling enhances code clarity

The clientError and serverError functions provide convenient wrappers for creating exceptions with appropriate error codes and statuses, improving code readability and maintainability.

chat-android/src/test/java/com/ably/chat/MessagesTest.kt (3)

20-20: Updated import aligns with concurrency changes

Importing CompletableDeferred reflects the shift from DeferredValue to CompletableDeferred in the main codebase.


97-100: Refactor tests to use CompletableDeferred

Replacing DeferredValue with CompletableDeferred in tests ensures consistency with the updated implementation and improves test reliability.


202-208: Additional assertions strengthen test coverage

Verifying that channelSerial is correctly used at the point of subscription when the channel state is attached enhances the robustness of the test by ensuring the subscription logic functions as intended.

chat-android/src/main/java/com/ably/chat/Messages.kt Outdated Show resolved Hide resolved
@sacOO7 sacOO7 force-pushed the improve/messages-feature-channel-serial branch from f351de0 to 1e053fb Compare December 3, 2024 13:15
@sacOO7 sacOO7 requested a review from ttypic December 3, 2024 13:29
1. Removed use of custom 'DeferredValue', which can become bottleneck in
heavy concurrency scenarios.
2. Replaced with thread safe native 'CompletableDeferred', supports
thread safe concurrent operations.
3. Removed unnecessary 'lock' and 'listeners' properties from Messages, since
concurrency and channel serial access already taken care by CompletableDeferred
4. Updated tests using `DeferredValue` with `CompletableDeferred`
@sacOO7 sacOO7 force-pushed the improve/messages-feature-channel-serial branch from 1e053fb to b21dd36 Compare December 3, 2024 13:54
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

🧹 Outside diff range and nitpick comments (1)
chat-android/src/main/java/com/ably/chat/Messages.kt (1)

241-248: LGTM: Thread-safe channel serial management

Good implementation of concurrent data structures. Consider adding a brief comment explaining the purpose of channelSerialMap for better code documentation.

Add documentation for channelSerialMap:

+    /**
+     * Thread-safe mapping of message listeners to their corresponding channel serials.
+     * Used to track subscription points for each listener independently.
+     */
     private val channelSerialMap = ConcurrentHashMap<PubSubMessageListener, CompletableDeferred<String>>()
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f351de0 and b21dd36.

📒 Files selected for processing (6)
  • chat-android/src/main/java/com/ably/chat/Messages.kt (6 hunks)
  • chat-android/src/main/java/com/ably/chat/Utils.kt (1 hunks)
  • chat-android/src/test/java/com/ably/chat/MessagesTest.kt (4 hunks)
  • chat-android/src/test/java/com/ably/chat/PresenceTest.kt (4 hunks)
  • chat-android/src/test/java/com/ably/chat/RoomReactionsTest.kt (2 hunks)
  • chat-android/src/test/java/com/ably/chat/TestUtils.kt (0 hunks)
💤 Files with no reviewable changes (1)
  • chat-android/src/test/java/com/ably/chat/TestUtils.kt
🚧 Files skipped from review as they are similar to previous changes (4)
  • chat-android/src/test/java/com/ably/chat/PresenceTest.kt
  • chat-android/src/test/java/com/ably/chat/RoomReactionsTest.kt
  • chat-android/src/main/java/com/ably/chat/Utils.kt
  • chat-android/src/test/java/com/ably/chat/MessagesTest.kt
🔇 Additional comments (5)
chat-android/src/main/java/com/ably/chat/Messages.kt (5)

11-12: LGTM: Appropriate imports for concurrent operations

The addition of ConcurrentHashMap and CompletableDeferred imports aligns with the goal of improving thread-safety in message subscription handling.


258-267: Implementation looks good

The atomic update of channel serials has been implemented as previously suggested.


317-317: LGTM: Improved error handling consistency

Good transition to using specific error types with clear error messages.

Also applies to: 322-322, 339-339

✅ Verification successful

Let me analyze the error handling patterns from the results. I can see that:

  1. clientError is used consistently in Messages.kt for client-side validation errors
  2. serverError is used for server-related issues
  3. AblyException.fromErrorInfo is used primarily in utility functions and API-related code

Let me verify if these patterns align with any established conventions in the codebase.


Error handling follows established patterns correctly

The changes align with the codebase's error handling conventions where:

  • clientError() is used for client-side validation failures (HTTP 400)
  • serverError() is used for server-related issues (HTTP 500)
  • Both are utility functions that create properly formatted AblyException instances
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify consistent error handling across the codebase
rg "throw (AblyException|clientError|serverError)" --type kotlin

Length of output: 2824


Script:

#!/bin/bash
# Check if there are any error handling utility functions or documentation
rg -A 5 "fun (client|server)Error" --type kotlin

Length of output: 447


196-196: LGTM: Improved thread-safety with CompletableDeferred

The change from DeferredValue to CompletableDeferred enhances thread-safety for serial handling.

✅ Verification successful

LGTM: CompletableDeferred change is properly implemented

The verification confirms that the change from DeferredValue to CompletableDeferred is correctly implemented. All usages of fromSerialProvider are compatible with the new type:

  • Used correctly in test assertions with .await()
  • Properly initialized in the implementation
  • No remnants of the old DeferredValue type found in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify that all usages of fromSerialProvider are compatible with CompletableDeferred
rg "fromSerialProvider.*DeferredValue" --type kotlin

Length of output: 54


Script:

#!/bin/bash
# Let's first find all usages of fromSerialProvider to understand its implementation and usage
rg "fromSerialProvider" -A 3 --type kotlin

# Also search for DeferredValue to understand the previous type usage
rg "DeferredValue" -A 3 --type kotlin

# Let's also check the class/interface definition where fromSerialProvider is declared
ast-grep --pattern 'interface $_ {
  $$$
  val fromSerialProvider
  $$$
}'

Length of output: 3606


289-306: Consider potential race condition in channel state checking

While the implementation is generally good, there's a potential race condition between checking the channel state and retrieving the channel serial. Consider using a synchronized block or atomic operation to ensure consistency.

Copy link
Collaborator

@ttypic ttypic left a comment

Choose a reason for hiding this comment

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

LGTM

Base automatically changed from refactor/room-feature-and-tests to main December 4, 2024 08:25
@sacOO7 sacOO7 merged commit 41e71f3 into main Dec 4, 2024
3 checks passed
@sacOO7 sacOO7 deleted the improve/messages-feature-channel-serial branch December 4, 2024 08: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.

2 participants