Skip to content

Conversation

@kakysha
Copy link

@kakysha kakysha commented Apr 3, 2025

Summary by CodeRabbit

  • New Features
    • Introduced an asynchronous client option that supports non-blocking transaction processing with callback support.
    • Added a new client creation mechanism that allows applications to manage their own concurrency for improved control.
  • Bug Fixes
    • Improved concurrency handling in the mempool by serializing access to transaction recheck callbacks.

@coderabbitai
Copy link

coderabbitai bot commented Apr 3, 2025

Walkthrough

This pull request introduces a new unsynchronized client implementation for ABCI, adding an unsyncLocalClient in the ABCI client package to handle asynchronous transaction checks with a mutex-protected callback. In addition, a new unsynchronized client creator, unsyncLocalClientCreator, is added to the proxy package to instantiate the new client, delegating concurrency control to the application layer. Separately, the CListMempool struct in the mempool package is enhanced with a new mutex to serialize access to its resCbRecheck callback method.

Changes

File(s) Change Summary
abci/client/unsync_local_client.go Added a new file defining unsyncLocalClient struct implementing the Client interface. Introduces constructor NewUnsyncLocalClient, a mutex-protected callback setter SetResponseCallback, asynchronous CheckTxAsync method running CheckTx in a goroutine, and multiple methods forwarding calls directly to the embedded application. Includes no-op Error and Flush methods and an Echo method returning the input message. Concurrency control is delegated to the application except for callback synchronization.
proxy/client.go Added a new type unsyncLocalClientCreator implementing the ClientCreator interface. Introduced constructor NewUnsyncLocalClientCreator and method NewABCIClient that creates an unsyncLocalClient instance via abcicli.NewUnsyncLocalClient. Added comments clarifying that this client does not internally synchronize calls, placing concurrency management responsibility on the application.
mempool/clist_mempool.go Added a new mutex field recheckCbMux to CListMempool struct. Modified the resCbRecheck method to acquire and release this mutex, serializing concurrent access to the callback function. No other logic changes were made.

Sequence Diagram(s)

sequenceDiagram
    participant C as UnsyncLocalClient
    participant A as Application
    participant M as Callback Mutex

    C->>M: Lock callback
    C->>A: Invoke CheckTxAsync (in goroutine)
    A-->>C: Return response
    C->>M: Unlock callback
    C->>Callback: Invoke callback if set
Loading
sequenceDiagram
    participant App as Application
    participant Creator as UnsyncLocalClientCreator
    participant Client as UnsyncLocalClient

    App->>Creator: NewUnsyncLocalClientCreator(app)
    Creator->>Client: NewABCIClient()
    Client-->>Creator: Returns new unsync client instance
Loading

Poem

Hopping through the lines of code so bright,
I found a client dancing in unsync light.
Mutex tucked its secrets safe and tight,
Async calls leaped with all their might.
From bouncy bytes to clever designs,
This rabbit cheers as the new code shines! 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 99b244d and 13ea52b.

📒 Files selected for processing (2)
  • abci/client/unsync_local_client.go (1 hunks)
  • mempool/clist_mempool.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • abci/client/unsync_local_client.go
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: e2e-test
🔇 Additional comments (2)
mempool/clist_mempool.go (2)

58-60: LGTM: Adding synchronization mutex for recheck callback

The added recheckCbMux mutex field is a good addition for thread safety in the new unsynchronized client architecture.


483-484: Good synchronization pattern applied to resCbRecheck

The mutex lock/unlock pattern correctly serializes access to the recheck callback, preventing concurrent invocations that could lead to race conditions. This aligns with the PR's goal of delegating concurrency control to the application layer rather than the client.


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

@kakysha kakysha changed the title feat(abci): unsync local client ported from comet v1 feat(abci): unsync local client ported from comet v1 [CP-197] Apr 3, 2025
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)
proxy/client.go (1)

86-90: Consider validating non-nil application.
A nil app may lead to runtime panics. Safeguarding here could be beneficial.

 func NewUnsyncLocalClientCreator(app types.Application) ClientCreator {
+    if app == nil {
+        panic("application cannot be nil")
+    }
     return &unsyncLocalClientCreator{
         app: app,
     }
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between d7eea0e and 983e4bc.

📒 Files selected for processing (2)
  • abci/client/unsync_local_client.go (1 hunks)
  • proxy/client.go (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
proxy/client.go (1)
abci/client/unsync_local_client.go (1)
  • NewUnsyncLocalClient (32-38)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: e2e-test
🔇 Additional comments (27)
proxy/client.go (4)

72-76: Helpful concurrency model explanation.
These docstrings clearly convey that this client creator assumes concurrency is handled externally.


77-79: Struct definition aligns with "unsync" approach.
This minimal struct without any mutex fields is consistent with deferring concurrency to the application.


81-85: Documentation clarifies advanced usage.
The comment strongly warns about concurrency management. No concerns here.


92-94: Correctly instantiates an unsynchronized client.
This method straightforwardly creates and returns the new unsynced client.

abci/client/unsync_local_client.go (23)

1-9: Package and imports look clean.
Imports are minimal and relevant, no immediate issues.


11-19: Struct definition for unsyncLocalClient.
It uses a mutex only for callback synchronization, aligning with the stated unsynced model for the rest of the application.


20-38: Constructor function.
Clear initialization of the base service and the embedded application. No issues noted.


40-44: SetResponseCallback with mutex lock.
This ensures thread safety for setting the callback reference. Good approach.


46-55: CheckTxAsync defers concurrency to the application.
After checking the transaction, it calls callback. Make sure the application is concurrency-safe.

Could you confirm the underlying application is thread-safe for concurrent CheckTx calls?


57-64: Callback invocation.
Invokes the callback if set, then returns a request-response wrapper. Implementation seems fine.


66-70: Error method returns nil.
No error tracking is done here. If the client needs to propagate errors, consider a future enhancement.

Do we need to track any internal error states that might arise at runtime?


72-74: Flush is a stub.
Since it's a local-only client, returning nil may be sufficient.


76-78: Echo method.
Straightforward echo. No concerns about concurrency here.


80-82: Info method.
Just passes through to app.Application. Looks good.


84-86: CheckTx method (synchronous).
Also directly delegated to the application. Fine for a local approach.


88-90: Query method.
Again, a simple pass-through. No issues.


92-94: Commit method.
Delegates to app’s commit logic. No concurrency guard needed here, by design.


96-98: InitChain method.
No local concurrency management, consistent with unsync design.


100-102: ListSnapshots method.
Pass-through to existing application logic. No further comments.


104-106: OfferSnapshot method.
Delegated snapshot offering. Implementation is straightforward.


108-111: LoadSnapshotChunk method.
Directly calls LoadSnapshotChunk on the embedded app. No concerns.


113-116: ApplySnapshotChunk method.
Another pass-through call. Implementation looks clean.


118-120: PrepareProposal method.
Again delegates concurrency to the application. Matches the unsync design.


122-124: ProcessProposal method.
Consistently delegates proposal handling to the application.


126-128: ExtendVote method.
Continues the unsynchronized pattern with simple pass-through.


130-132: VerifyVoteExtension method.
Delegates vote extension checks. No concurrency or logic issues.


134-136: FinalizeBlock method.
Ends the block by forwarding to the embedded app. Implementation remains consistent.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
abci/client/unsync_local_client.go (1)

11-18: Mutex initialization needs to be clear for maintainers

The mtx mutex field is declared but not explicitly initialized in the struct or constructor. While Go's zero value for sync.Mutex is valid and usable, it would be helpful to add a comment explaining this for future maintainers.

I see that another reviewer previously asked about mutex initialization. Although Go automatically initializes the mutex to its zero value (which is usable), consider adding a clarifying comment in the constructor to address this explicitly.

🧹 Nitpick comments (2)
abci/client/unsync_local_client.go (2)

53-53: Remove or clarify commented code

The commented line with "don't think this is needed" creates ambiguity about proper error handling. Either remove this comment entirely or add a clear explanation of why error responses aren't needed in this context.


46-62: Document the CheckTxAsync goroutine management

While the constructor docs explain the high-level concurrency model, there's no comment explaining the goroutine management in CheckTxAsync. Consider adding documentation that explains how the goroutine is managed and what happens to it if the client is stopped or closed.

Add something like:

// CheckTxAsync asynchronously runs the application's CheckTx in a goroutine.
// Note: If the client is stopped, ongoing goroutines will not be explicitly terminated.
func (app *unsyncLocalClient) CheckTxAsync(ctx context.Context, req *types.RequestCheckTx) (*ReqRes, error) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 983e4bc and 99b244d.

📒 Files selected for processing (1)
  • abci/client/unsync_local_client.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: e2e-test
🔇 Additional comments (4)
abci/client/unsync_local_client.go (4)

57-58: Potential race condition with callback invocation

While SetResponseCallback() properly protects the callback with a mutex, reqres.InvokeCallback() is called without mutex protection, which could lead to a race condition if the callback is being modified concurrently.

Please verify how InvokeCallback() is implemented. If it directly accesses the callback field without proper synchronization, you should add mutex protection here:

reqres.Response = types.ToResponseCheckTx(res)
+ app.mtx.Lock()
reqres.InvokeCallback()
+ app.mtx.Unlock()

Alternatively, if reqres.InvokeCallback() uses the callback from the unsyncLocalClient, modify its implementation to acquire the mutex before invoking the callback.


32-38: Robust constructor implementation with proper documentation

The constructor clearly explains the difference between this implementation and NewLocalClient, which is crucial for users to understand the concurrency implications.


78-134: Clean pass-through implementations

The implementation correctly forwards all application methods directly to the embedded Application, maintaining the non-synchronized nature of this client as described in the constructor comments.


46-62:

Details

❓ Verification inconclusive

Verify if context cancellation is handled correctly

The provided context is passed to the Application's CheckTx call, but it's not clear if the goroutine properly respects context cancellation. If the parent context is canceled, the goroutine should terminate gracefully.

Please verify if the embedded Application's CheckTx implementation properly respects context cancellation. If not, consider adding explicit handling:


🏁 Script executed:

#!/bin/bash
# Find out how Application.CheckTx handles context cancellation
grep -r "func.*CheckTx.*context\.Context" --include="*.go" .

Length of output: 4092


Subject: Verify Context Cancellation Handling in unsyncLocalClient.checkTxAsync

The grep search confirms that all CheckTx implementations (including in the Application used by unsyncLocalClient) accept a context. However, the implementation in unsyncLocalClient simply passes the provided context into Application.CheckTx within a goroutine without additional cancellation checks. This design relies on the underlying Application.CheckTx to honor context cancellation. Please review the exact Application.CheckTx implementation (for example, in abci/example/kvstore/kvstore.go) to ensure it listens to ctx.Done() and terminates work when canceled. If it doesn’t, consider adding explicit cancellation handling in the goroutine to avoid potential goroutine leaks.

Comment on lines 52 to 55
if err != nil {
// reqres.Response = types.ToResponseException(err.Error()) // don't think this is needed
return
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling in CheckTxAsync

When an error occurs from app.Application.CheckTx(), it's currently ignored and the goroutine simply returns without setting any response or notifying the caller. This could lead to requests hanging indefinitely if they're waiting for a response.

Consider setting an error response and still invoking the callback:

if err != nil {
-   // reqres.Response = types.ToResponseException(err.Error()) // don't think this is needed
-   return
+   reqres.Response = types.ToResponseException(err.Error())
+   reqres.InvokeCallback()
+   return
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if err != nil {
// reqres.Response = types.ToResponseException(err.Error()) // don't think this is needed
return
}
if err != nil {
reqres.Response = types.ToResponseException(err.Error())
reqres.InvokeCallback()
return
}

@kakysha kakysha force-pushed the CP-197/parallel_checktx branch from 13ea52b to 983e4bc Compare April 16, 2025 08:49
Eduard-Voiculescu pushed a commit to streamingfast/cometbft that referenced this pull request May 16, 2025
…tbft#4890)

due to sec vuln

Vulnerability InjectiveLabs#1: GO-2025-3420
Sensitive headers incorrectly sent after cross-domain redirect in
net/http
  More info: https://pkg.go.dev/vuln/GO-2025-3420
  Standard library
    Found in: net/[email protected]
    Fixed in: net/[email protected]
    Example traces found:
Error: InjectiveLabs#1: rpc/jsonrpc/client/http_json_client.go:231:34:
client.Client.Call calls http.Client.Do
Error: InjectiveLabs#2: libs/cli/setup.go:89:26: cli.Executor.Execute calls
cobra.Command.Execute, which eventually calls http.Client.Get
Error: InjectiveLabs#3: cmd/cometbft/commands/debug/util.go:70:23: debug.dumpProfile
calls http.Get

Vulnerability InjectiveLabs#2: GO-2025-3373
Usage of IPv6 zone IDs can bypass URI name constraints in crypto/x509
  More info: https://pkg.go.dev/vuln/GO-2025-3373
  Standard library
    Found in: crypto/[email protected]
    Fixed in: crypto/[email protected]
    Example traces found:
Error: InjectiveLabs#1: abci/tutorials/abci-v2-forum-app/model/db.go:143:20:
model.DB.Close calls badger.DB.Close, which eventually calls
x509.CertPool.AppendCertsFromPEM
Error: InjectiveLabs#2: internal/autofile/group.go:468:30: autofile.GroupReader.Read
calls bufio.Reader.Read, which eventually calls x509.Certificate.Verify
Error: InjectiveLabs#3: rpc/jsonrpc/client/ws_client.go:290:29: client.WSClient.dial
calls websocket.Dialer.Dial, which eventually calls
x509.Certificate.VerifyHostname
Error: InjectiveLabs#4: light/errors.go:483:84: light.errBadWitness.Error calls
x509.HostnameError.Error
Error: InjectiveLabs#5: rpc/jsonrpc/server/http_server.go:166:19:
server.ServeTLSWithShutdown calls http.Server.ServeTLS, which eventually
calls x509.ParseCertificate
Error: InjectiveLabs#6: rpc/jsonrpc/server/http_server.go:166:19:
server.ServeTLSWithShutdown calls http.Server.ServeTLS, which eventually
calls x509.ParseECPrivateKey
Error: InjectiveLabs#7: rpc/jsonrpc/server/http_server.go:166:19:
server.ServeTLSWithShutdown calls http.Server.ServeTLS, which eventually
calls x509.ParsePKCS1PrivateKey
Error: InjectiveLabs#8: rpc/jsonrpc/server/http_server.go:166:19:
server.ServeTLSWithShutdown calls http.Server.ServeTLS, which eventually
calls x509.ParsePKCS8PrivateKey
<hr>This is an automatic backport of pull request cometbft#4888 done by
[Mergify](https://mergify.com).

---------

Co-authored-by: Anton Kaliaev <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants