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

[IND-402] Cache and send order updates for stateful orders. #683

Merged

Conversation

vincentwschau
Copy link
Contributor

Changelist

Fixes bug where stateful orders that are not filled are not updated with the total filled amount after placing.
The bug occurs as the OrderUpdate for stateful orders are sent before the OrderPlace message is sent. As such the OrderUpdate is discarded.
The fix proposed in this PR is to cached the OrderUpdate and re-send it when the OrderPlace is received for the stateful order.

Use the added StatefulOrderUpdatesCache to store and send cached order updates for stateful orders.
Updates the OrderUpdateHandler in vulcan to cache order updates for stateful orders if the order update does not match any order in the order cache.
Updates the handler for stateful order placements in ender to re-send any cached order updates for the stateful order after sending the OrderPlace message.
Updates roundtable to add a task to remove any old cached OrderUpdates.

Will not be merged until the base PR is merged.

Test Plan

Added unit tests.

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

@vincentwschau vincentwschau added the bug Something isn't working label Oct 23, 2023
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 23, 2023

Walkthrough

The changes primarily focus on handling stateful order updates in the system. They introduce caching for stateful order updates, add a mechanism to remove old cached order updates, and modify existing handlers and tasks to accommodate these changes. The changes also include additional test cases to verify the new functionality and configuration parameters to control the behavior.

Changes

File(s) Summary
.../stateful-order-placement-handler.test.ts
.../stateful-order-placement-handler.ts
Added handling for cached stateful order updates in the statefulOrderPlacementHandler function and corresponding test cases.
.../remove-old-order-updates.test.ts
.../remove-old-order-updates.ts
Introduced a new task to remove old cached stateful order updates and added test cases to verify the functionality.
.../order-update-handler.test.ts
.../order-update-handler.ts
Modified the handleOrderUpdate function to handle stateful order updates and added corresponding test cases.
.../config.ts
.../index.ts
Added new configuration parameters to control the removal of old cached order updates and started a loop for the task based on the configuration.

Tips

Chat with CodeRabbit Bot (@coderabbitai)

  • If you reply to a review comment from CodeRabbit, the bot will automatically respond.
  • To engage with CodeRabbit bot directly around the specific lines of code in the PR, mention @coderabbitai in your review comment
  • Note: Review comments are made on code diffs or files, not on the PR overview.
  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai help to get help.

Note: For conversation with the bot, please use the review comments on code diffs or files.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • 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/coderabbit-overrides.json

Base automatically changed from vincentc/add-stateful-order-update-cache to main October 23, 2023 13:59
@linear
Copy link

linear bot commented Oct 23, 2023

IND-402 Fix Stateful order placement with no fill

context

Stateful orders that are not filled do not get added to the order book.

Issue:
1. Orders aren't set to be "resting on the book" until an OrderUpdate message is sent for the message. After receiving an OrderPlace message the order does not have it's size added to the order book.
2. OrderUpdate messages aren't processed if an OrderPlace message was not previously processed for the order.
3. For stateful orders
- the OrderPlace message is sent after ender processes the order
- the OrderUpdate message is sent by the node after the stateful order is placed
4. The OrderUpdate message is received before the OrderPlace, due to it being sent from a different service.

Potential Solution:

  • keep track of all OrderUpdate messages for stateful orders that don't update an order in a separate cache similar to the CanceledOrdersCache

  • when a stateful order placement is ingested by ender, check if there's a corresponding OrderUpdate message, and if so, send it to vulcan

  • the difference in time between the OrderPlace and OrderUpdate should be a most a few blocks, so keep only the last 1 minute of OrderUpdate messages in the cache

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.

Review Status

Actionable comments generated: 1
Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between aee5223 and 2352bd2.
Files selected for processing (7)
  • indexer/services/ender/tests/handlers/stateful-order/stateful-order-placement-handler.test.ts (5 hunks)
  • indexer/services/ender/src/handlers/stateful-order/stateful-order-placement-handler.ts (2 hunks)
  • indexer/services/roundtable/tests/tasks/remove-old-order-updates.test.ts (1 hunks)
  • indexer/services/roundtable/src/config.ts (1 hunks)
  • indexer/services/roundtable/src/tasks/remove-old-order-updates.ts (1 hunks)
  • indexer/services/vulcan/tests/handlers/order-update-handler.test.ts (4 hunks)
  • indexer/services/vulcan/src/handlers/order-update-handler.ts (2 hunks)
Files skipped from review due to trivial changes (2)
  • indexer/services/roundtable/tests/tasks/remove-old-order-updates.test.ts
  • indexer/services/roundtable/src/tasks/remove-old-order-updates.ts
Additional comments (Suppressed): 16
indexer/services/roundtable/src/config.ts (1)
  • 123-124: The addition of OLD_CACHED_ORDER_UPDATES_WINDOW_MS configuration parameter is appropriate for managing the time window for removing old cached order updates. Ensure that the default value of 30 seconds is suitable for your application's needs and that it's properly documented for future reference.
indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-placement-handler.test.ts (6)
  • 22-22: The import of OrderUpdateV1 is new and seems to be used for handling cached order updates. Ensure that this import is used correctly throughout the code.

  • 49-51: New imports from @dydxprotocol-indexer/redis are introduced. These are used for handling the caching of stateful order updates. Make sure these are used correctly and the necessary dependencies are installed.

  • 72-72: The addition of await redis.deleteAllAsync(redisClient); is to clear the Redis cache after each test. This is a good practice to ensure that each test starts with a clean state.

  • 147-172: The test cases have been expanded to include scenarios where there is a cached order update. This is a good addition as it tests the new functionality of handling cached order updates.

  • 178-188: The new code checks if there is a cached order update and if so, it adds it to the StatefulOrderUpdatesCache. This is a good addition as it tests the new functionality of handling cached order updates.

  • 228-245: The new code checks if there was a cached order update and if so, it verifies that the cache is now empty and that the cached order update was sent to the Kafka producer. This is a good addition as it tests the new functionality of handling cached order updates.

indexer/services/ender/src/handlers/stateful-order/stateful-order-placement-handler.ts (2)
  • 9-9: The StatefulOrderUpdatesCache from @dydxprotocol-indexer/redis is imported. This is a new addition to the code and is used to store and retrieve cached order updates for stateful orders.

  • 66-94: The logic for handling stateful order placement events has been updated. After generating the off-chain update for the order placement status, the code now retrieves any pending order update from the StatefulOrderUpdatesCache and generates an off-chain update for it. This ensures that any order updates that were received before the order placement are included in the Kafka events. This is a significant change and should be thoroughly tested to ensure it works as expected.

-    return [
-      this.generateConsolidatedVulcanKafkaEvent(
-         getOrderIdHash(order.orderId!),
-        offChainUpdate,
-      ),
-    ];
+    const kafakEvents: ConsolidatedKafkaEvent[] = [];
+    kafakEvents.push(this.generateConsolidatedVulcanKafkaEvent(
+      getOrderIdHash(order.orderId!),
+      offChainUpdate,
+    ));
+
+    const pendingOrderUpdate: OrderUpdateV1 | undefined = await StatefulOrderUpdatesCache
+      .removeStatefulOrderUpdate(
+        OrderTable.orderIdToUuid(order.orderId!),
+        Date.now(),
+        redisClient,
+      );
+    if (pendingOrderUpdate !== undefined) {
+      kafakEvents.push(this.generateConsolidatedVulcanKafkaEvent(
+        getOrderIdHash(order.orderId!),
+        OffChainUpdateV1.fromPartial({
+          orderUpdate: pendingOrderUpdate,
+        }),
+      ));
+    }
+
+    return kafakEvents;
indexer/services/vulcan/__tests__/handlers/order-update-handler.test.ts (4)
  • 7-7: The StatefulOrderUpdatesCache module is imported. This module is used to cache order updates for stateful orders.

  • 37-37: The OrderUpdateV1 type is imported. This type is used to represent order updates in the new test case.

  • 469-469: The test case description has been updated to specify that it applies to short-term orders. This change reflects the new logic in the OrderUpdateHandler module, which handles short-term orders and stateful orders differently.

  • 489-521: A new test case has been added to verify the behavior when an order update is received for a stateful order that does not exist in the order cache. The test case checks that the order update is added to the StatefulOrderUpdatesCache, and that no other actions are taken (such as updating the OrderbookLevelsCache or sending WebSocket messages). The test case also checks that an informational log message is generated, and that a metric is incremented with the order flags of the order update.

+    it('adds order update to stateful order update cache if stateful order not found', async () => {
+      synchronizeWrapBackgroundTask(wrapBackgroundTask);
+      const producerSendSpy: jest.SpyInstance = jest.spyOn(producer, 'send').mockReturnThis();
+      const statefulOrderUpdate: redisTestConstants.OffChainUpdateOrderUpdateUpdateMessage = {
+        ...redisTestConstants.orderUpdate,
+        orderUpdate: {
+          ...redisTestConstants.orderUpdate.orderUpdate,
+          orderId: redisTestConstants.defaultOrderIdGoodTilBlockTime,
+        },
+      };
+      await handleOrderUpdate(statefulOrderUpdate);
+
+      const cachedOrderUpdate: OrderUpdateV1 | undefined = await StatefulOrderUpdatesCache
+        .removeStatefulOrderUpdate(
+          redisTestConstants.defaultOrderUuidGoodTilBlockTime,
+          Date.now(),
+          client,
+        );
+      expect(cachedOrderUpdate).toBeDefined();
+      expect(cachedOrderUpdate).toEqual(statefulOrderUpdate.orderUpdate);
+
+      expect(OrderbookLevelsCache.updatePriceLevel).not.toHaveBeenCalled();
+      expect(logger.info).toHaveBeenCalledWith(expect.objectContaining({
+        at: 'OrderUpdateHandler#handle',
+        message: expect.stringMatching('Received order update for order that does not exist, order id '),
+      }));
+      expectWebsocketMessagesNotSent(producerSendSpy);
+      expect(stats.increment).toHaveBeenCalledWith(
+        'vulcan.order_update_order_does_not_exist',
+        1,
+        {
+          orderFlags: String(statefulOrderUpdate.orderUpdate.orderId!.orderFlags),
+        },
+      );
+    });
indexer/services/vulcan/src/handlers/order-update-handler.ts (3)
  • 11-18: The new hunk imports OrderTable and StatefulOrderUpdatesCache from @dydxprotocol-indexer/postgres and @dydxprotocol-indexer/redis respectively. It also imports isStatefulOrder from @dydxprotocol-indexer/v4-proto-parser. These new imports are used in the updated logic for handling order updates.

  • 80-108: The new hunk adds a check to see if the order update was for a stateful order when the order does not exist in the cache. If it is a stateful order, the order update is added to a cache of order updates for stateful orders. This allows the order update to be re-sent after the on-chain event for the stateful order placement is processed. The order id is converted to a UUID before being added to the cache. The current time is used as the timestamp for the cache entry. The Redis client is passed to the function that adds the order update to the cache. The order flags are logged and used as a tag for a stats counter increment.

  • 80-108: Ensure that the StatefulOrderUpdatesCache.addStatefulOrderUpdate function properly handles any errors that may occur during the operation. This is important to prevent the application from crashing or entering an inconsistent state due to an unhandled exception.

/**
* This task removes any old cached stateful orer updates from the StatefulOrderUpdates cache
*/
export default async function runTask(): Promise<void> {
Copy link
Contributor

Choose a reason for hiding this comment

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

are you going to create a followup PR for index.ts changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, I missed adding the code to run the task. Will add.

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.

Review Status

Actionable comments generated: 0
Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 2352bd2 and 51cea70.
Files selected for processing (2)
  • indexer/services/roundtable/src/config.ts (3 hunks)
  • indexer/services/roundtable/src/index.ts (2 hunks)
Files skipped from review due to trivial changes (1)
  • indexer/services/roundtable/src/index.ts
Additional comments (Suppressed): 3
indexer/services/roundtable/src/config.ts (3)
  • 44-44: The new configuration parameter LOOPS_ENABLED_REMOVE_OLD_ORDER_UPDATES is introduced to control whether the task for removing old cached order updates is enabled or not. Ensure that this configuration is properly documented and the implications of enabling/disabling this task are clearly communicated to the users.

  • 74-76: The new configuration parameter LOOPS_INTERVAL_MS_REMOVE_OLD_ORDER_UPDATES is introduced to control the interval at which the task for removing old cached order updates is run. Ensure that this interval is set to a reasonable value that balances the need for timely removal of old cached order updates and the potential impact on system performance.

  • 126-128: The new configuration parameter OLD_CACHED_ORDER_UPDATES_WINDOW_MS is introduced to control the time window for removing old cached order updates. Ensure that this time window is set to a reasonable value that balances the need for timely removal of old cached order updates and the potential impact on system performance.

@vincentwschau vincentwschau merged commit ca5bcb4 into main Oct 23, 2023
11 checks passed
@vincentwschau vincentwschau deleted the vincentc/ind-402-send-cached-stateful-order-updates branch October 23, 2023 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working indexer
Development

Successfully merging this pull request may close these issues.

2 participants