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

Fix filterOrdersBySubaccountId param token and helper name #2694

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

UnbornAztecKing
Copy link

Changelist

Full Node Streaming provides an initial state and streaming updates for positions, orders, prices and fills.

The subscription API admits an optional sub account ID filter, which is only applied to the initial positions and position changes.

The user would like to apply the sub account ID filter to the order messages, in addition to position messages.

The change will add boolean flags to the Websocket and GRPC streaming API's:

  • filterOrdersBySubaccountId boolean field for WS request (if not provided, default to False)
  • filter_orders_by_subaccount_id boolean field for StreamOrderbookUpdatesRequest protobuf (if not provided, default to False)

For all endpoints, the previous behavior of not filtering orders for subaccounts is preserved by default.

If filtering orders is not specified, the code path remains the same for looping over stream updates.
If filtering orders is specified, each slice of updates received from the subscription updatesChannel will be filtered like:

  • if the message is not a StreamUpdate_OrderbookUpdate, forward it
  • if the message is a StreamUpdate_OrderbookUpdate, forward it only if one of the OffChainUpdateV1 messages inside are for a target subaccount

Test Plan

Unit test

Author/Reviewer Checklist

  • If this PR has changes that result in a different app state given the same prior state and transaction list, manually add the state-breaking label.
  • If the PR has breaking postgres changes to the indexer add the indexer-postgres-breaking label.
  • If this PR isn't state-breaking but has changes that modify behavior in PrepareProposal or ProcessProposal, manually add the label proposal-breaking.
  • If this PR is one of many that implement a specific feature, manually label them all feature:[feature-name].
  • If you wish to for mergify-bot to automatically create a PR to backport your change to a release branch, manually add the label backport/[branch-name].
  • Manually add any of the following labels: refactor, chore, bug.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added an option to filter order updates during streaming.
    • Enhanced orderbook subscription with optional order filtering capability.
    • Introduced a new parameter for filtering orders in WebSocket server requests.
  • Improvements

    • Updated streaming interfaces to support the new filtering mechanism.
    • Enhanced error handling for parsing new parameters.
  • Technical Updates

    • Modified protocol buffers and SDK types to include filterOrders parameter.
    • Updated WebSocket server to parse new query parameter.
    • Added test coverage for new streaming functionality.
    • Enhanced functionality to extract subaccount IDs from off-chain updates.

@UnbornAztecKing UnbornAztecKing requested a review from a team as a code owner January 17, 2025 21:50
Copy link
Contributor

coderabbitai bot commented Jan 17, 2025

Walkthrough

This pull request introduces a new feature to filter orderbook updates by subaccount IDs across multiple components of the dYdX protocol. The changes span from protobuf definitions to the WebSocket server, adding a boolean flag filterOrdersBySubaccountId that allows clients to receive only order updates related to specific subaccounts. The modification enhances the granularity of orderbook update streaming by providing more precise control over the updates received by clients.

Changes

File Change Summary
indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.ts Added filterOrdersBySubaccountId boolean property to StreamOrderbookUpdatesRequest interface
proto/dydxprotocol/clob/query.proto Added filter_orders_by_subaccount_id field to StreamOrderbookUpdatesRequest message
protocol/streaming/full_node_streaming_manager.go Added new methods for creating and managing orderbook subscriptions with subaccount filtering
protocol/streaming/types/interface.go Updated Subscribe method signature to include filterOrders parameter
protocol/streaming/ws/websocket_server.go Added parsing for filterOrdersBySubaccountId query parameter

Sequence Diagram

sequenceDiagram
    participant Client
    participant WebSocketServer
    participant StreamingManager
    participant OrderbookFilter

    Client->>WebSocketServer: Connect with filterOrdersBySubaccountId=true
    WebSocketServer->>StreamingManager: Subscribe(clobPairIds, subaccountIds, filterOrders=true)
    StreamingManager->>OrderbookFilter: Filter updates by subaccount
    OrderbookFilter-->>StreamingManager: Filtered updates
    StreamingManager-->>WebSocketServer: Send filtered updates
    WebSocketServer-->>Client: Stream only relevant updates
Loading

Possibly related PRs

Suggested labels

refactor, state-breaking

Suggested reviewers

  • jonfung-dydx
  • teddyding

Poem

🐰 Hop, hop, filtering orders with glee,
Subaccounts dancing, updates now free!
Streaming precise, no noise in sight,
Our rabbit's code makes filtering tight!
Orderbook magic, clean and bright! 🎉

Finishing Touches

  • 📝 Generate Docstrings (Beta)

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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (10)
protocol/streaming/full_node_streaming_manager.go (5)

5-5: Unnecessary Import of 'slices' Package

The slices package imported at line 5 is not used in the code.

Consider removing this import to clean up the code.


15-16: Organize Imports for Better Readability

At lines 15-16, the imports could be grouped logically to enhance readability.

Consider grouping related imports together, such as all github.com/dydxprotocol/v4-chain/protocol imports.


97-130: Redundant Function Definitions for 'NewOrderbookSubscription'

There are two functions for creating a new OrderbookSubscription:

  • Lines 97-114: NewOrderbookSubscription
  • Lines 116-130: (sm *FullNodeStreamingManagerImpl) NewOrderbookSubscription

Consider refactoring to eliminate redundancy. You might combine them or clearly differentiate their purposes.


246-267: Inefficient Filtering Logic in 'FilterStreamUpdateBySubaccount'

The FilterStreamUpdateBySubaccount function may process updates unnecessarily, impacting performance.

Optimize the filtering logic to skip updates that do not relate to subscribed subaccounts, improving efficiency especially with large datasets.


348-353: Potential Side Effects Due to Reassigning 'updates'

In lines 348-353, reassigning updates after filtering could lead to unintended side effects.

Assign the filtered updates to a new variable to preserve the original data and enhance code clarity.

protocol/streaming/util/util.go (1)

25-44: Enhance Error Messages in 'GetOffChainUpdateV1SubaccountIdNumber'

The error message returned when an UpdateMessage type is unrecognized is not very informative.

Provide more detailed error messages to aid debugging.

return 0, fmt.Errorf(
-	"UpdateMessage type not in {OrderPlace, OrderRemove, OrderUpdate, OrderReplace}: %+v",
+	"Unrecognized UpdateMessage type '%T' in GetOffChainUpdateV1SubaccountIdNumber: %+v",
	updateMessage,
)
protocol/streaming/types/interface.go (1)

19-19: Consider using a more specific parameter name.

The parameter name filterOrders is too generic. Consider renaming it to filterOrdersBySubaccountId to better reflect its specific purpose of filtering orders by subaccount ID.

protocol/streaming/util/util_test.go (1)

113-113: Remove debug print statement.

Remove the debug print statement that was likely used during development.

-			fmt.Println("expected", id)
protocol/streaming/ws/websocket_server.go (2)

183-183: Fix line length.

The comment line exceeds the maximum length of 120 characters. Consider wrapping or shortening it.

-// parseFilterOrdersBySubaccountId is a helper function to parse the filterOrdersBySubaccountId flag from the query parameters.
+// parseFilterOrdersBySubaccountId parses the filterOrdersBySubaccountId flag from query parameters.
🧰 Tools
🪛 GitHub Actions: Protocol Lint

[warning] 183-183: Line length exceeds maximum limit of 120 characters (current length: 127 characters)


191-191: Enhance error message.

The error message could be more helpful by indicating the expected format.

-		return false, fmt.Errorf("invalid filterOrdersBySubaccountId: %s", token)
+		return false, fmt.Errorf("invalid filterOrdersBySubaccountId: %s, expected 'true' or 'false'", token)
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 56cc48e and 36313b1.

⛔ Files ignored due to path filters (1)
  • protocol/x/clob/types/query.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (10)
  • indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.ts (6 hunks)
  • proto/dydxprotocol/clob/query.proto (1 hunks)
  • protocol/streaming/full_node_streaming_manager.go (6 hunks)
  • protocol/streaming/full_node_streaming_manager_test.go (1 hunks)
  • protocol/streaming/noop_streaming_manager.go (1 hunks)
  • protocol/streaming/types/interface.go (1 hunks)
  • protocol/streaming/util/util.go (1 hunks)
  • protocol/streaming/util/util_test.go (1 hunks)
  • protocol/streaming/ws/websocket_server.go (3 hunks)
  • protocol/x/clob/keeper/grpc_stream_orderbook.go (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Protocol Lint
protocol/streaming/ws/websocket_server.go

[warning] 183-183: Line length exceeds maximum limit of 120 characters (current length: 127 characters)

⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: test-sim-multi-seed-short
  • GitHub Check: test-sim-after-import
  • GitHub Check: test-sim-import-export
  • GitHub Check: unit-end-to-end-and-integration
  • GitHub Check: liveness-test
  • GitHub Check: test-race
  • GitHub Check: test-coverage-upload
  • GitHub Check: container-tests
  • GitHub Check: test / run_command
  • GitHub Check: benchmark
  • GitHub Check: Summary
🔇 Additional comments (10)
protocol/x/clob/keeper/grpc_stream_orderbook.go (1)

15-15: Missing Validation for New Parameter

At line 15, req.GetFilterOrdersBySubaccountId() is added to the Subscribe call, but there's no validation for this new parameter.

Ensure that filterOrdersBySubaccountId is properly validated before use to prevent misuse or unexpected behavior.

protocol/streaming/noop_streaming_manager.go (1)

27-27: LGTM!

The noop implementation correctly implements the updated interface.

protocol/streaming/ws/websocket_server.go (1)

100-108: LGTM!

The parameter parsing follows the same pattern as other parameters and includes proper error handling.

protocol/streaming/full_node_streaming_manager_test.go (3)

257-261: LGTM: Well-structured test case definition.

The TestCase struct is well-designed with clear fields for inputs (updates, subaccountIds) and expected outputs (filteredUpdates).


318-463: Comprehensive test coverage for filtering scenarios.

The test cases cover a wide range of scenarios:

  • Base cases (in/out of scope)
  • Multiple subaccounts
  • Empty updates/IDs
  • Different update types (fills, taker orders, etc.)

466-476: LGTM: Clean test execution.

The test execution is well-structured:

  • Uses table-driven tests for better organization
  • Proper use of subtests for isolation
  • Clear assertions using require package
indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/query.ts (3)

281-287: LGTM: Clear documentation for the new field.

The TypeScript interface properly documents the new filterOrdersBySubaccountId field with clear comments explaining its purpose and behavior.


1315-1316: LGTM: Proper encoding implementation.

The encoding logic correctly handles the boolean field:

  • Only writes the field if it's true (protobuf optimization)
  • Uses the correct wire format (uint32(32) for field number 4)

Also applies to: 1341-1344


1388-1391: LGTM: Proper decoding implementation.

The decoding logic correctly handles the boolean field using case 4 to match the protobuf field number.

proto/dydxprotocol/clob/query.proto (1)

189-193: LGTM: Clear protobuf field definition.

The new field is well-defined:

  • Uses field number 4 (maintains backward compatibility)
  • Has clear documentation
  • Uses appropriate type (bool)

Comment on lines +223 to +239
func doFilterStreamUpdateBySubaccount(
orderBookUpdate *clobtypes.StreamUpdate_OrderbookUpdate,
subaccountIdNumbers []uint32,
logger log.Logger,
) bool {
for _, orderBookUpdate := range orderBookUpdate.OrderbookUpdate.Updates {
orderBookUpdateSubaccountIdNumber, err := streaming_util.GetOffChainUpdateV1SubaccountIdNumber(orderBookUpdate)
if err == nil {
if slices.Contains(subaccountIdNumbers, orderBookUpdateSubaccountIdNumber) {
return true
}
} else {
logger.Error(err.Error())
}
}
return false
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 17, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Incomplete Error Handling in 'doFilterStreamUpdateBySubaccount'

In the doFilterStreamUpdateBySubaccount function, errors from GetOffChainUpdateV1SubaccountIdNumber are only logged, not handled.

It's important to handle errors appropriately to prevent unexpected behavior. Consider returning the error or taking corrective action when an error occurs.

Copy link
Author

Choose a reason for hiding this comment

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

The only error thrown from streaming_util.GetOffChainUpdateV1SubaccountIdNumber results from handling a new OffChainUpdateV1 message type.

It isn't clear Full Node Streaming should crash under this condition.

Copy link
Contributor

Choose a reason for hiding this comment

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

@UnbornAztecKing You're absolutely right. Looking at the implementation, I can see that GetOffChainUpdateV1SubaccountIdNumber only returns an error when encountering an unknown OffChainUpdateV1.UpdateMessage type, which is explicitly expected behavior when new message types are added. The current approach of logging the error and continuing is the correct pattern as it:

  1. Maintains forward compatibility with new message types that may be added in the future
  2. Provides debug information through logs
  3. Gracefully handles unknown types by filtering them out (returning false)
  4. Preserves service stability instead of crashing
🔗 Analysis chain

@UnbornAztecKing I agree with your assessment. The current error handling approach is appropriate since:

  1. It maintains forward compatibility with new message types
  2. Crashing the streaming service would be too disruptive for what is just a filtering operation
  3. The error is logged for debugging purposes

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find the implementation of GetOffChainUpdateV1SubaccountIdNumber
ast-grep --pattern 'func GetOffChainUpdateV1SubaccountIdNumber($_, $_) ($_, $_) {
  $$$
}'

Length of output: 92


Script:

#!/bin/bash
# Search for error returns in GetOffChainUpdateV1SubaccountIdNumber
rg "return.*error" -A 5 -B 5 "GetOffChainUpdateV1SubaccountIdNumber"

Length of output: 146


Script:

#!/bin/bash
# Search for GetOffChainUpdateV1SubaccountIdNumber function using ripgrep
rg -A 10 -B 10 "func.*GetOffChainUpdateV1SubaccountIdNumber"

Length of output: 3181


Script:

#!/bin/bash
# Try finding the file first
fd "util.go"

Length of output: 580


Script:

#!/bin/bash
# Search for any usage of GetOffChainUpdateV1SubaccountIdNumber
rg "GetOffChainUpdateV1SubaccountIdNumber"

Length of output: 544


✏️ Learnings added
Learnt from: UnbornAztecKing
PR: dydxprotocol/v4-chain#2694
File: protocol/streaming/full_node_streaming_manager.go:223-239
Timestamp: 2025-01-17T22:33:30.979Z
Learning: In Full Node Streaming's filtering operations, when handling OffChainUpdateV1 messages (e.g., in doFilterStreamUpdateBySubaccount), errors from unknown message types should be logged but not cause crashes. This pattern maintains forward compatibility when new message types are added to OffChainUpdateV1.UpdateMessage, as indicated by the comment "Error expected if OffChainUpdateV1.UpdateMessage message type is extended to more order events".

Learnt from: UnbornAztecKing
PR: dydxprotocol/v4-chain#2694
File: protocol/streaming/full_node_streaming_manager.go:223-239
Timestamp: 2025-01-17T22:33:30.979Z
Learning: In Full Node Streaming's filtering operations (e.g., doFilterStreamUpdateBySubaccount), errors from unknown message types should be logged but not cause crashes, to maintain forward compatibility and service stability.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +1166 to +1171
syncLocalUpdates, syncLocalClobPairIds := getStreamUpdatesFromOffchainUpdates(
streaming_util.GetOffchainUpdatesV1(orderBookUpdatesToSyncLocalOpsQueue),
lib.MustConvertIntegerToUint32(ctx.BlockHeight()),
ctx.ExecMode(),
)
sm.cacheStreamUpdatesByClobPairWithLock(sycnLocalUpdates, syncLocalClobPairIds)
sm.cacheStreamUpdatesByClobPairWithLock(syncLocalUpdates, syncLocalClobPairIds)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing Error Handling in 'getStreamUpdatesFromOffchainUpdates'

At lines 1166-1171, the function getStreamUpdatesFromOffchainUpdates is called without checking for errors.

Include error handling to catch and manage potential errors from this function.

Comment on lines +283 to +286
if filterOrdersBySubAccountId && (len(subaccountIds) == 0) {
sm.logger.Error("filterOrdersBySubaccountId with no subaccountIds")
return types.ErrInvalidStreamingRequest
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Inconsistent Parameter Naming

At lines 283-286, filterOrdersBySubAccountId is used instead of filterOrdersBySubaccountId.

Ensure consistent naming conventions for parameters to prevent confusion and potential bugs.

-	if filterOrdersBySubAccountId && (len(subaccountIds) == 0) {
+	if filterOrdersBySubaccountId && (len(subaccountIds) == 0) {
📝 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 filterOrdersBySubAccountId && (len(subaccountIds) == 0) {
sm.logger.Error("filterOrdersBySubaccountId with no subaccountIds")
return types.ErrInvalidStreamingRequest
}
if filterOrdersBySubaccountId && (len(subaccountIds) == 0) {
sm.logger.Error("filterOrdersBySubAccountId with no subaccountIds")
return types.ErrInvalidStreamingRequest
}

Comment on lines +51 to +55
tests := map[string]struct {
update ocutypes.OffChainUpdateV1
id uint32
err error
}{
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add negative test cases.

The test suite should include negative test cases to verify error handling for invalid updates.

Add test cases like:

"InvalidUpdate": {
    update: ocutypes.OffChainUpdateV1{
        UpdateMessage: nil,
    },
    id:  0,
    err: errors.New("invalid update: missing update message"),
},

Comment on lines +114 to +115
require.Equal(t, err, testCase.err)
require.Equal(t, id, testCase.id)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix assertion argument order.

The require.Equal arguments are in the wrong order. The first argument should be the expected value, and the second should be the actual value.

-			require.Equal(t, err, testCase.err)
-			require.Equal(t, id, testCase.id)
+			require.Equal(t, testCase.err, err)
+			require.Equal(t, testCase.id, id)
📝 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
require.Equal(t, err, testCase.err)
require.Equal(t, id, testCase.id)
require.Equal(t, testCase.err, err)
require.Equal(t, testCase.id, id)

Comment on lines +34 to +35
Quantums: uint64(10 ^ 6),
Subticks: 1,
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect power operator usage.

In Go, ^ is the bitwise XOR operator, not the power operator. Use math.Pow() or direct multiplication for powers.

-		Quantums: uint64(10 ^ 6),
-		Subticks: 1,
+		Quantums: uint64(1_000_000), // 10^6
+		Subticks: 1,
📝 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
Quantums: uint64(10 ^ 6),
Subticks: 1,
Quantums: uint64(1_000_000), // 10^6
Subticks: 1,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

1 participant