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-477, IND-478, IND-479, IND-480] Update all stateful order handlers to use a SQL function to perform reads & updates. #774

Merged
merged 1 commit into from
Nov 8, 2023

Conversation

lcwik
Copy link
Contributor

@lcwik lcwik commented Nov 8, 2023

Changelist

[IND-477, IND-478, IND-479, IND-480] Update all stateful order handlers to use a SQL function to perform reads & updates.

Test Plan

Updated existing tests and added some new ones.

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.

…rs to use a SQL function to perform reads & updates.
Copy link

linear bot commented Nov 8, 2023

IND-479 Ender stateful order placement handler SQL pushdown

Update ender stateful order placement to execute updates via a SQL function.

IND-480 Ender stateful order removal handler SQL pushdown

Update ender stateful order removal to execute updates via a SQL function.

IND-477 Ender conditional order placement handler SQL pushdown

Update ender conditional order placement to execute updates via a SQL function.

IND-478 Ender conditional order trigger handler SQL pushdown

Update ender conditional order trigger to execute updates via a SQL function.

Copy link
Contributor

coderabbitai bot commented Nov 8, 2023

Walkthrough

The changes introduce a new configuration option USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION that switches between SQL function and Knex-based handling in the stateful order handlers. New methods for SQL function handling and Kafka event creation are added. Test suites are updated to cover both methods. A new PostgreSQL function dydx_stateful_order_handler is introduced to process order-related events. Additionally, the convertToIndexerOrder function is updated to handle subaccounts.

Changes

File(s) Change Summary
.../postgres/src/lib/order-translations.ts Introduced a new function convertToIndexerOrderWithSubaccount and updated convertToIndexerOrder to handle subaccounts.
.../ender/__tests__/handlers/stateful-order/... Updated test suites to cover both SQL function and Knex-based handling, controlled by the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION flag.
.../ender/src/config.ts Added a new configuration option USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION.
.../ender/src/handlers/abstract-stateful-order-handler.ts Added a new method handleEventViaSqlFunction for SQL function handling.
.../ender/src/handlers/stateful-order/... Added methods for SQL function handling and Kafka event creation. Updated existing methods to conditionally use SQL function or Knex-based handling.
.../ender/src/helpers/postgres/postgres-functions.ts Added two new SQL scripts: 'dydx_protocol_condition_type_to_order_type.sql' and 'dydx_stateful_order_handler.sql'.
.../ender/src/scripts/dydx_from_protocol_order_side.sql Reformatted the CASE statement for order_side handling.
.../ender/src/scripts/dydx_protocol_condition_type_to_order_type.sql Introduced a new PostgreSQL function dydx_protocol_condition_type_to_order_type to convert 'ConditionType' enum to 'OrderType' enum.
.../ender/src/scripts/dydx_stateful_order_handler.sql Introduced a new PostgreSQL function dydx_stateful_order_handler to process order-related events and update the database.

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.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.

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

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: 6

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 365c8dc and f0e22cc.
Files selected for processing (16)
  • indexer/packages/postgres/src/lib/order-translations.ts (2 hunks)
  • indexer/services/ender/tests/handlers/stateful-order/conditional-order-placement-handler.test.ts (4 hunks)
  • indexer/services/ender/tests/handlers/stateful-order/conditional-order-triggered-handler.test.ts (3 hunks)
  • indexer/services/ender/tests/handlers/stateful-order/stateful-order-placement-handler.test.ts (5 hunks)
  • indexer/services/ender/tests/handlers/stateful-order/stateful-order-removal-handler.test.ts (4 hunks)
  • indexer/services/ender/tests/scripts/scripts.test.ts (2 hunks)
  • indexer/services/ender/src/config.ts (1 hunks)
  • indexer/services/ender/src/handlers/abstract-stateful-order-handler.ts (2 hunks)
  • indexer/services/ender/src/handlers/stateful-order/conditional-order-placement-handler.ts (4 hunks)
  • indexer/services/ender/src/handlers/stateful-order/conditional-order-triggered-handler.ts (5 hunks)
  • indexer/services/ender/src/handlers/stateful-order/stateful-order-placement-handler.ts (3 hunks)
  • indexer/services/ender/src/handlers/stateful-order/stateful-order-removal-handler.ts (2 hunks)
  • indexer/services/ender/src/helpers/postgres/postgres-functions.ts (1 hunks)
  • indexer/services/ender/src/scripts/dydx_from_protocol_order_side.sql (1 hunks)
  • indexer/services/ender/src/scripts/dydx_protocol_condition_type_to_order_type.sql (1 hunks)
  • indexer/services/ender/src/scripts/dydx_stateful_order_handler.sql (1 hunks)
Files skipped from review due to trivial changes (3)
  • indexer/services/ender/tests/handlers/stateful-order/stateful-order-removal-handler.test.ts
  • indexer/services/ender/src/helpers/postgres/postgres-functions.ts
  • indexer/services/ender/src/scripts/dydx_from_protocol_order_side.sql
Additional comments: 40
indexer/services/ender/src/config.ts (1)
  • 50-55: The new configuration option USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION is introduced with a default value of true. Ensure that this change is reflected in the system's configuration files and that the system behaves as expected when this flag is toggled.
indexer/services/ender/__tests__/scripts/scripts.test.ts (2)
  • 7-11: The import statement has been updated to include IndexerOrder_ConditionType from @dydxprotocol-indexer/v4-protos. Ensure that this import is being used correctly throughout the file.

  • 178-186: A new test case has been added for the dydx_protocol_condition_type_to_order_type function. This test case checks the function's behavior with various condition types. Ensure that the test case covers all possible condition types and that the function behaves as expected in each case.

indexer/services/ender/src/scripts/dydx_protocol_condition_type_to_order_type.sql (1)
  • 1-25: The SQL function dydx_protocol_condition_type_to_order_type is well written and handles all expected cases. It also throws an exception for unexpected condition types, which is a good practice for error handling. However, ensure that this exception is properly caught and handled in the application code to prevent unexpected crashes or disruptions.
indexer/services/ender/__tests__/handlers/stateful-order/conditional-order-triggered-handler.test.ts (1)
  • 111-124: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [111-160]

The test case 'successfully triggers order and sends to vulcan' has been parameterized to test both the Knex and SQL function-based order handling paths. Ensure that the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION configuration flag is being set correctly in all environments where this code will run.

indexer/services/ender/__tests__/handlers/stateful-order/conditional-order-placement-handler.test.ts (2)
  • 162-185: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [129-173]

The test cases for both Knex and SQL function-based order placements are well-structured and cover the necessary scenarios. However, ensure that the SQL function and Knex queries are correctly implemented and that they handle edge cases and errors properly.

  • 162-185: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [175-240]

The test cases for both Knex and SQL function-based order upserts are well-structured and cover the necessary scenarios. However, ensure that the SQL function and Knex queries are correctly implemented and that they handle edge cases and errors properly.

indexer/services/ender/src/handlers/stateful-order/stateful-order-removal-handler.ts (5)
  • 24-25: Ensure that the orderIdToUuid function handles all edge cases and errors properly. If it fails, it could lead to incorrect parallelization IDs.

  • 28-33: The switch between SQL function and Knex based on the configuration flag is clear and straightforward. However, ensure that the configuration flag is set correctly in all environments where this code will run.

  • 35-39: Ensure that the handleEventViaSqlFunction method properly handles all possible errors and edge cases. If it fails, it could lead to incorrect Kafka events.

  • 41-49: Ensure that the runFuncWithTimingStatAndErrorLogging and updateOrderStatus methods properly handle all possible errors and edge cases. If they fail, it could lead to incorrect Kafka events.

  • 51-54: Ensure that the OffChainUpdateV1.fromPartial method properly handles all possible errors and edge cases. If it fails, it could lead to incorrect Kafka events.

indexer/services/ender/src/handlers/stateful-order/stateful-order-placement-handler.ts (5)
  • 14-20: The import of config is new. Ensure that the configuration is correctly set up and that the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION flag is being properly read.

  • 39-44: The internalHandle method now conditionally calls either handleViaSqlFunction or handleViaKnex based on the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION configuration flag. This is a good approach to switch between different implementations.

  • 46-57: The handleViaSqlFunction method is new. It handles events via SQL function and creates Kafka events. Ensure that the SQL function is correctly implemented and that it handles all edge cases.

  • 82-91: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [59-86]

The handleViaKnex method is new. It handles events via Knex and creates Kafka events. Ensure that the Knex queries are correctly implemented and that they handle all edge cases.

  • 88-91: The createKafkaEvents method is new. It creates Kafka events based on the provided order. Ensure that the Kafka events are correctly formed and that they are being sent to the correct topic.
indexer/packages/postgres/src/lib/order-translations.ts (2)
  • 22-32: The function convertToIndexerOrderWithSubaccount is introduced to handle the conversion of an order to an IndexerOrder with a subaccount. The function checks if the order is a long-term or conditional order and throws an error if it's not. This is a good practice as it ensures that the function is used correctly.

  • 86-101: The function convertToIndexerOrder is updated to fetch the subaccount using the SubaccountTable.findById method and then call the convertToIndexerOrderWithSubaccount function. It also handles the case where the subaccount is not found and throws an error. This is a good practice as it ensures that the function fails early when the required subaccount is not found.

indexer/services/ender/src/handlers/stateful-order/conditional-order-placement-handler.ts (5)
  • 14-20: The import of config is new and is used to check the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION flag. Ensure that the flag is correctly set in the configuration.

  • 33-40: The internalHandle method now checks the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION flag and calls either handleViaSqlFunction or handleViaKnex accordingly. This is a good use of feature flags to control the behavior of the system.

  • 42-51: The handleViaSqlFunction method is new and uses the handleEventViaSqlFunction method from the parent class to handle the event. It then creates Kafka events. Ensure that the SQL function is correctly implemented and that the Kafka events are correctly formed.

  • 77-90: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [53-81]

The handleViaKnex method is essentially the old internalHandle method. It has been renamed and is now only called when the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION flag is false. This maintains backward compatibility when the flag is not set.

  • 83-90: The createKafkaEvents method is new and generates Kafka events. Ensure that the Kafka events are correctly formed.
indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-placement-handler.test.ts (6)
  • 139-150: The test cases have been updated to test both Knex and SQL function-based order placements. Ensure that the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION configuration flag is being set correctly in the production environment.

  • 151-154: Ensure that the createKafkaMessageFromStatefulOrderEvent function is correctly creating Kafka messages from stateful order events.

  • 177-182: The expectTimingStats function is conditionally called based on the value of useSqlFunction. Ensure that this function is correctly measuring the timing stats when useSqlFunction is false.

  • 196-207: The test cases have been updated to test both Knex and SQL function-based order placements. Ensure that the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION configuration flag is being set correctly in the production environment.

  • 208-211: Ensure that the SubaccountTable.subaccountIdToUuid function is correctly converting the subaccount ID to a UUID.

  • 258-263: The expectTimingStats function is conditionally called based on the value of useSqlFunction. Ensure that this function is correctly measuring the timing stats when useSqlFunction is false.

indexer/services/ender/src/scripts/dydx_stateful_order_handler.sql (4)
  • 13-21: The use of constants and variable declarations at the beginning of the function improves readability and maintainability.

  • 24-75: The function handles different types of order placement events and updates the order records accordingly. It's important to ensure that all possible event types are covered and that the function behaves as expected in each case.

  • 77-97: The function uses the INSERT INTO ... ON CONFLICT DO UPDATE construct to upsert order records. This is a good approach to handle both new and existing records.

  • 105-145: The function handles conditionalOrderTriggered and orderRemoval events and updates the order status accordingly. It's important to ensure that the function behaves as expected in each case.

indexer/services/ender/src/handlers/stateful-order/conditional-order-triggered-handler.ts (4)
  • 34-41: The internalHandle method now checks the USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION configuration flag to decide whether to handle events via SQL function or Knex. Ensure that this flag is set correctly in all environments where this code will run.

  • 43-52: The new handleViaSqlFunction method handles events via SQL function. It calls the handleEventViaSqlFunction method from the AbstractStatefulOrderHandler class, which executes a SQL function and handles the result. It then converts the result to an IndexerOrder and creates Kafka events. Ensure that the SQL function is correctly implemented and that it returns the expected result.

  • 76-85: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [54-80]

The new handleViaKnex method handles events via Knex. It updates the order status to OPEN, fetches the PerpetualMarketFromDatabase for the clobPairId of the order, and throws an error if it cannot find the perpetual market. It then converts the order to an IndexerOrder and creates Kafka events. Ensure that the updateOrderStatus method correctly updates the order status in the database and that the PerpetualMarketFromDatabase is correctly fetched for the clobPairId.

  • 89-95: > Note: This review was outside of the patch, so it was mapped to the patch with the greatest overlap. Original lines [82-95]

The new createKafkaEvents method creates Kafka events. It creates an OffChainUpdateV1 with the order and a placementStatus of ORDER_PLACEMENT_STATUS_OPENED, and then generates a consolidated Vulcan Kafka event with the orderIdHash and the offChainUpdate. Ensure that the Kafka events are correctly created and sent.

indexer/services/ender/src/handlers/abstract-stateful-order-handler.ts (2)
  • 1-29: The import statements have been reorganized and expanded. Ensure that all the imported modules are used in the code and that there are no unused imports.

  • 42-71: A new method handleEventViaSqlFunction has been added. This method executes a SQL function and handles the result. Ensure that the SQL function dydx_stateful_order_handler exists and works as expected. Also, make sure that the error handling is sufficient and that the function handles all possible edge cases.

+  protected async handleEventViaSqlFunction():
+  Promise<[OrderFromDatabase,
+    PerpetualMarketFromDatabase,
+    SubaccountFromDatabase | undefined]> {
+    const eventDataBinary: Uint8Array = this.indexerTendermintEvent.dataBytes;
+    const result: pg.QueryResult = await storeHelpers.rawQuery(
+      `SELECT dydx_stateful_order_handler(
+        ${this.block.height},
+        '${this.block.time?.toISOString()}',
+        '${JSON.stringify(StatefulOrderEventV1.decode(eventDataBinary))}'
+      ) AS result;`,
+      { txId: this.txId },
+    ).catch((error: Error) => {
+      logger.error({
+        at: 'AbstractStatefulOrderHandler#handleEventViaSqlFunction',
+        message: 'Failed to handle StatefulOrderEventV1',
+        error,
+      });
+      throw error;
+    });
+
+    return [
+      OrderModel.fromJson(result.rows[0].result.order) as OrderFromDatabase,
+      PerpetualMarketModel.fromJson(
+        result.rows[0].result.perpetual_market) as PerpetualMarketFromDatabase,
+      result.rows[0].result.subaccount
+        ? SubaccountModel.fromJson(result.rows[0].result.subaccount) as SubaccountFromDatabase
+        : undefined,
+    ];
+  }

Comment on lines +163 to 178
it.each([
['via knex', false],
['via SQL function', true],
])('throws error when attempting to trigger an order that does not exist (%s)', async (
_name: string,
useSqlFunction: boolean,
) => {
config.USE_STATEFUL_ORDER_HANDLER_SQL_FUNCTION = useSqlFunction;
const kafkaMessage: KafkaMessage = createKafkaMessageFromStatefulOrderEvent(
defaultStatefulOrderEvent,
);

await expect(onMessage(kafkaMessage)).rejects.toThrowError(
new Error(`Unable to update order status with orderId: ${orderId}`),
`Unable to update order status with orderId: ${orderId}`,
);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

The test case 'throws error when attempting to trigger an order that does not exist' has been parameterized to test both the Knex and SQL function-based order handling paths. The error message has been updated to include the orderId which will help in debugging.

@@ -45,6 +45,7 @@ import Long from 'long';
import { producer } from '@dydxprotocol-indexer/kafka';
import { ConditionalOrderPlacementHandler } from '../../../src/handlers/stateful-order/conditional-order-placement-handler';
import { createPostgresFunctions } from '../../../src/helpers/postgres/postgres-functions';
import config from '../../../src/config';
Copy link
Contributor

Choose a reason for hiding this comment

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

The import of config from a specific path might cause issues if the path changes in the future. Consider using dependency injection or a configuration management library to handle configuration.

order.subaccountId,
);

if (!subaccount === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The condition !subaccount === undefined is incorrect and will always evaluate to false because !subaccount will be a boolean and undefined is not a boolean. The correct condition should be subaccount === undefined.

-  if (!subaccount === undefined) {
+  if (subaccount === undefined) {

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
if (!subaccount === undefined) {
if (subaccount === undefined) {

Comment on lines +1 to +9
/**
Parameters:
- block_height: the height of the block being processing.
- block_time: the time of the block being processed.
- event_data: The 'data' field of the IndexerTendermintEvent (https://github.com/dydxprotocol/v4-proto/blob/8d35c86/dydxprotocol/indexer/indexer_manager/event.proto#L25)
converted to JSON format. Conversion to JSON is expected to be done by JSON.stringify.
Returns: JSON object containing fields:
- order: The upserted order in order-model format (https://github.com/dydxprotocol/indexer/blob/cc70982/packages/postgres/src/models/order-model.ts).
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

The function documentation is clear and provides a good overview of the function's purpose, parameters, and return value. However, it would be beneficial to include a brief description of the function's logic and behavior.

Comment on lines +116 to +124
BEGIN
SELECT * INTO STRICT perpetual_market_record FROM perpetual_markets WHERE "clobPairId" = clob_pair_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE EXCEPTION 'Unable to find perpetual market with clobPairId: %', clob_pair_id;
WHEN TOO_MANY_ROWS THEN
/** This should never happen and if it ever were to would indicate that the table has malformed data. */
RAISE EXCEPTION 'Found multiple perpetual markets with clobPairId: %', clob_pair_id;
END;
Copy link
Contributor

Choose a reason for hiding this comment

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

The function queries the perpetual_markets table based on the clobPairId. It's good that the function handles NO_DATA_FOUND and TOO_MANY_ROWS exceptions. However, it's recommended to handle other potential exceptions as well, such as connection errors.

Comment on lines +155 to +157
ELSE
RAISE EXCEPTION 'Unkonwn sub-event type %', event_data;
END IF;
Copy link
Contributor

Choose a reason for hiding this comment

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

The function raises an exception for unknown sub-event types. This is a good practice to handle unexpected inputs.

Overall, the function seems to be well-structured and handles different types of events. However, it's recommended to add more comments to explain the logic and behavior of the function, especially for complex parts of the code. Also, it's important to handle potential exceptions that might occur during the execution of the function.

@lcwik lcwik merged commit 7585cad into main Nov 8, 2023
@lcwik lcwik deleted the lukeind477 branch November 8, 2023 19:30
vincentwschau pushed a commit that referenced this pull request Nov 9, 2023
…rs to use a SQL function to perform reads & updates. (#774)
vincentwschau pushed a commit that referenced this pull request Nov 9, 2023
…rs to use a SQL function to perform reads & updates. (#774)
vincentwschau pushed a commit that referenced this pull request Nov 10, 2023
…rs to use a SQL function to perform reads & updates. (#774)
vincentwschau pushed a commit that referenced this pull request Nov 10, 2023
…rs to use a SQL function to perform reads & updates. (#774)
vincentwschau pushed a commit that referenced this pull request Nov 10, 2023
…rs to use a SQL function to perform reads & updates. (#774)
vincentwschau pushed a commit that referenced this pull request Nov 10, 2023
…rs to use a SQL function to perform reads & updates. (#774)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

2 participants