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

Add Orderbook Mid Price Cache #2338

Merged
merged 3 commits into from
Sep 25, 2024
Merged

Add Orderbook Mid Price Cache #2338

merged 3 commits into from
Sep 25, 2024

Conversation

adamfraser
Copy link
Contributor

@adamfraser adamfraser commented Sep 24, 2024

Changelist

  • Adds a redis cache to keep a 5 second sample of orderbook mid prices
  • Adds a roundtable task to sample orderbook mid price every second for each market
  • Use 5 second median from orderbook cache for candle mid price

Test Plan

  • Tested in dev
  • Added unit tests

Summary by CodeRabbit

  • New Features

    • Introduced caching functionality for order book mid prices using Redis.
    • Added methods for setting prices and retrieving median prices.
    • Implemented new Lua scripts for managing market prices and calculating medians.
    • Added configuration options for enabling and controlling the caching interval.
    • New task for periodically caching order book mid prices.
  • Bug Fixes

    • Adjusted handling of undefined prices in caching logic.
  • Tests

    • Added unit tests for caching functionality and price retrieval.
    • Established comprehensive testing framework for various scenarios.

Copy link
Contributor

coderabbitai bot commented Sep 24, 2024

Walkthrough

The changes introduce a new caching mechanism for orderbook mid prices using Redis, encapsulating functionality in a dedicated module. This includes the addition of two main functions, setPrice and getMedianPrice, along with corresponding Lua scripts for atomic operations. A series of tests validate the functionality, and configuration parameters are added to control the caching behavior. The overall structure is enhanced to improve the efficiency of price management in the system.

Changes

Files Change Summary
indexer/packages/redis/__tests__/caches/orderbook-mid-prices-cache.test.ts Introduced unit tests for setPrice and getMedianPrice functions, validating caching logic.
indexer/packages/redis/src/caches/orderbook-mid-prices-cache.ts Added setPrice and getMedianPrice functions for managing mid prices in Redis.
indexer/packages/redis/src/caches/scripts.ts Added two Lua scripts: addMarketPriceScript and getMarketMedianScript for price management.
indexer/packages/redis/src/index.ts Exported OrderbookMidPricesCache for broader access to caching functionalities.
indexer/packages/redis/src/scripts/add_market_price.lua Implemented Lua script for adding market prices to a Redis sorted set.
indexer/packages/redis/src/scripts/get_market_median_price.lua Created Lua script for calculating median prices from a Redis sorted set.
indexer/services/ender/__tests__/lib/candles-generator.test.ts Updated tests to use OrderbookMidPricesCache.setPrice for price updates.
indexer/services/ender/src/lib/candles-generator.ts Modified logic to retrieve mid prices using OrderbookMidPricesCache.getMedianPrice.
indexer/services/roundtable/__tests__/tasks/cache-orderbook-mid-prices.test.ts Introduced tests for runTask function, validating caching behavior for mid prices.
indexer/services/roundtable/src/config.ts Added configuration parameters for enabling and setting intervals for caching mid prices.
indexer/services/roundtable/src/index.ts Integrated new task cacheOrderbookMidPrices into the start function based on configuration.
indexer/services/roundtable/src/tasks/cache-orderbook-mid-prices.ts Defined runTask function to update OrderbookMidPricesCache with current market prices.

Possibly related PRs

Suggested reviewers

  • dydxwill
  • Christopher-Li

Poem

🐰 In the garden where prices bloom,
A cache of mid prices makes room.
With scripts and tests, we hop with glee,
Redis holds our prices, just wait and see!
So let’s celebrate this caching delight,
For order books now shine so bright! 🌟


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.

@adamfraser adamfraser marked this pull request as ready for review September 24, 2024 21:31
@adamfraser adamfraser requested a review from a team as a code owner September 24, 2024 21:31
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: 7

Outside diff range and nitpick comments (17)
indexer/packages/redis/src/scripts/add_market_price.lua (2)

1-8: LGTM! Consider adding a comment for the time window constant.

The variable declarations and input processing look good. The use of KEYS and ARGV is correct, and converting inputs to numbers is a good practice.

Consider adding a comment to explain the significance of the 5-second time window:

-- Time window (5 seconds) for maintaining recent price data
local fiveSeconds = 5

13-15: LGTM! Consider using ZREMRANGEBYSCORE with ZADD for atomicity.

The implementation for removing old entries is correct. However, we can optimize this operation by combining it with the ZADD command for atomicity.

Consider using a single EVAL call to perform both operations atomically:

redis.call("zadd", priceCacheKey, nowSeconds, price)
redis.call("zremrangebyscore", priceCacheKey, "-inf", nowSeconds - fiveSeconds)

This ensures that no other operations can occur between adding the new price and removing old entries, maintaining data consistency.

indexer/packages/redis/src/scripts/get_market_median_price.lua (3)

12-13: Consider adjusting the middle index calculation.

The current calculation of the middle index is correct for even numbers of elements, but it might be off by one for odd numbers. To handle both cases correctly, consider using:

local middle = math.ceil(#prices / 2)

This ensures that for odd numbers of elements, we get the correct middle index, and for even numbers, we get the upper middle index, which works with the subsequent logic.


15-22: LGTM: Correct median calculation logic with a minor optimization opportunity.

The script correctly handles both even and odd number of elements, returning appropriate values for median calculation. The consistency in returning an array for both cases is good for external processing.

A minor optimization could be:

if #prices % 2 == 0 then
  return {prices[middle], prices[middle + 1]}
else
  return {prices[middle]}
end

This assumes the suggested change to the middle calculation is implemented. It simplifies the odd case and reduces the need for the + 1 adjustment.


1-22: Overall: Well-implemented script with minor improvement opportunities.

The script effectively implements the core functionality for retrieving median prices from Redis, aligning well with the PR objectives. It handles different scenarios appropriately and leverages Redis's sorted set capabilities efficiently.

Consider the following suggestions for further improvement:

  1. Implement the suggested changes for middle index calculation and median retrieval for odd numbers of elements.
  2. Add error handling, particularly for cases where the key might not exist or other potential Redis errors.
  3. Consider adding comments explaining the external division for even number of elements, to improve maintainability.

To enhance the robustness of the system:

  1. Implement monitoring for the cache hit rate and latency of this script.
  2. Set up alerts for scenarios where the price cache is consistently empty or contains unexpected values.
  3. Consider implementing a fallback mechanism in case of Redis failures or unexpected errors.

These suggestions will help ensure the reliability and performance of the orderbook mid price cache in production.

indexer/services/roundtable/src/tasks/cache-orderbook-mid-prices.ts (1)

27-37: Enhance error handling and logging.

  1. For undefined prices, consider adding more context to help diagnose why the price might be missing:
logger.info({
  at: 'cache-orderbook-mid-prices#runTask',
  message: 'Undefined price for market',
  ticker: market.ticker,
  marketId: market.id,
  // Add any other relevant market information
});
  1. For error logging, include more details about the market being processed:
logger.error({
  at: 'cache-orderbook-mid-prices#runTask',
  message: 'Failed to process market',
  ticker: market.ticker,
  marketId: market.id,
  error: error.message,
  stack: error.stack,
});

These changes will provide more context for debugging and monitoring purposes.

indexer/services/roundtable/__tests__/tasks/cache-orderbook-mid-prices.test.ts (2)

67-83: Consider adding more assertions for undefined price handling.

The test case for handling undefined prices is good, but could be improved:

  1. Verify that the mock was called with the correct arguments.
  2. Check if the cache was attempted to be set even with an undefined price.
  3. Consider testing this scenario for multiple markets to ensure consistent behavior.

Here's a suggested improvement:

it('handles undefined prices', async () => {
  const market1 = testConstants.defaultPerpetualMarket;
  const market2 = testConstants.defaultPerpetualMarket2;

  const mockGetOrderBookMidPrice = jest.spyOn(OrderbookLevelsCache, 'getOrderBookMidPrice');
  mockGetOrderBookMidPrice.mockResolvedValueOnce(undefined);
  mockGetOrderBookMidPrice.mockResolvedValueOnce(undefined);

  await runTask();

  expect(mockGetOrderBookMidPrice).toHaveBeenCalledWith(market1.ticker, redisClient);
  expect(mockGetOrderBookMidPrice).toHaveBeenCalledWith(market2.ticker, redisClient);

  const price1 = await OrderbookMidPricesCache.getMedianPrice(redisClient, market1.ticker);
  const price2 = await OrderbookMidPricesCache.getMedianPrice(redisClient, market2.ticker);
  expect(price1).toBeNull();
  expect(price2).toBeNull();

  expect(jest.requireMock('@dydxprotocol-indexer/base').logger.info).toHaveBeenCalledWith({
    at: 'cache-orderbook-mid-prices#runTask',
    message: `undefined price for ${market1.ticker}`,
  });
  expect(jest.requireMock('@dydxprotocol-indexer/base').logger.info).toHaveBeenCalledWith({
    at: 'cache-orderbook-mid-prices#runTask',
    message: `undefined price for ${market2.ticker}`,
  });
});

85-97: Enhance error handling test case.

The error handling test case is good but could be improved:

  1. Verify that the mock was called with the correct arguments.
  2. Check if the cache remains unchanged after an error.
  3. Consider testing this scenario for multiple markets to ensure consistent behavior.

Here's a suggested improvement:

it('handles errors', async () => {
  const market1 = testConstants.defaultPerpetualMarket;
  const market2 = testConstants.defaultPerpetualMarket2;

  const mockGetOrderBookMidPrice = jest.spyOn(OrderbookLevelsCache, 'getOrderBookMidPrice');
  mockGetOrderBookMidPrice.mockRejectedValueOnce(new Error('Test error for market1'));
  mockGetOrderBookMidPrice.mockRejectedValueOnce(new Error('Test error for market2'));

  await runTask();

  expect(mockGetOrderBookMidPrice).toHaveBeenCalledWith(market1.ticker, redisClient);
  expect(mockGetOrderBookMidPrice).toHaveBeenCalledWith(market2.ticker, redisClient);

  const price1 = await OrderbookMidPricesCache.getMedianPrice(redisClient, market1.ticker);
  const price2 = await OrderbookMidPricesCache.getMedianPrice(redisClient, market2.ticker);
  expect(price1).toBeNull();
  expect(price2).toBeNull();

  expect(jest.requireMock('@dydxprotocol-indexer/base').logger.error).toHaveBeenCalledWith({
    at: 'cache-orderbook-mid-prices#runTask',
    message: 'Test error for market1',
    error: expect.any(Error),
  });
  expect(jest.requireMock('@dydxprotocol-indexer/base').logger.error).toHaveBeenCalledWith({
    at: 'cache-orderbook-mid-prices#runTask',
    message: 'Test error for market2',
    error: expect.any(Error),
  });
});
indexer/services/roundtable/src/config.ts (1)

141-143: LGTM: New configuration parameter for orderbook mid price caching interval.

The addition of LOOPS_INTERVAL_MS_CACHE_ORDERBOOK_MID_PRICES is well-implemented and aligns with the PR objectives. The naming convention is consistent with other loop interval parameters, and the default value of ONE_SECOND_IN_MILLISECONDS correctly sets the sampling interval to one second as specified in the PR summary.

Consider adding a comment above this configuration to explicitly state its purpose, similar to comments present for some other configurations in this file. For example:

  // Interval for caching orderbook mid prices
  LOOPS_INTERVAL_MS_CACHE_ORDERBOOK_MID_PRICES: parseInteger({
    default: ONE_SECOND_IN_MILLISECONDS,
  }),

This would enhance the readability and maintainability of the configuration file.

indexer/services/roundtable/src/index.ts (1)

276-282: LGTM: New conditional block for caching orderbook mid prices

The implementation of the new conditional block for caching orderbook mid prices is well-structured and aligns with the existing patterns in the file. It correctly uses configuration values for enabling the feature and setting the interval, providing flexibility and ease of management.

This addition fulfills the PR objective of implementing a caching mechanism for orderbook mid prices, with the ability to sample the orderbook mid price every second.

For consistency with other similar blocks in this file, consider adding a newline before this block (i.e., between lines 275 and 276).

indexer/services/ender/src/lib/candles-generator.ts (1)

Action Required: Missing 5-Second Cache Duration Configuration

While the OrderbookMidPricesCache is implemented and utilized across various parts of the codebase, the specific configuration to maintain a 5-second sample of orderbook mid prices was not found. This configuration is crucial to ensure that the caching mechanism operates as intended and meets the PR objectives.

Please address the following:

  • Implement Cache Duration Configuration: Ensure that the cache duration is explicitly set to 5 seconds to maintain the required sample interval.
  • Verify Interval Settings: Confirm that any interval-related settings, especially those pertaining to orderbook mid price sampling, are correctly configured to support the 5-second duration.
Analysis chain

Line range hint 1-554: Verify completeness of implementation

The changes implemented in this file focus on using the new OrderbookMidPricesCache to get median prices. However, the PR objectives mention implementing a 5-second sample of orderbook mid prices and a roundtable task to sample the orderbook mid price every second for each market.

Could you please verify if additional changes are needed in other parts of the codebase to fully implement these features? Specifically:

  1. Is there a separate implementation for the roundtable task that samples the orderbook mid price every second?
  2. Are there any configurations or additional code needed to ensure the 5-second sample is maintained in the cache?

To help verify this, please run the following script:

This will help ensure that all aspects of the PR objectives have been addressed in the codebase.


Follow-Up: Correct Shell Script Execution Required

The previous shell script encountered a syntax error, preventing a complete verification of the implementation related to orderbook mid price sampling and caching. To ensure all aspects are thoroughly checked, please execute the following corrected shell scripts:

[/run_scripts]


Further Verification Required: Missing Cache Duration Configuration

While the OrderbookMidPricesCache and related roundtable tasks are implemented, the configuration for maintaining a 5-second sample of orderbook mid prices was not found. It's essential to ensure that the cache duration is correctly set to meet the PR objectives.

Please execute the following shell scripts to search for cache duration configurations using alternative patterns and file types:

[/run_scripts]

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for implementations related to orderbook mid price sampling and caching

echo "Checking for roundtable task implementations:"
rg "roundtable.*orderbook.*mid.*price" --type ts

echo "Checking for cache duration configurations:"
rg "cache.*duration.*5.*second" --type ts

echo "Checking for OrderbookMidPricesCache usage:"
rg "OrderbookMidPricesCache" --type ts

Length of output: 3641


Script:

#!/bin/bash
# Description: Check for implementations related to orderbook mid price sampling and caching

echo "Checking for roundtable task implementations:"
rg "roundtable.*orderbook.*mid.*price" --type ts

echo "Checking for cache duration configurations:"
rg "cache.*duration.*5.*second" --type ts

echo "Checking for OrderbookMidPricesCache usage:"
rg "OrderbookMidPricesCache" --type ts

Length of output: 3546


Script:

#!/bin/bash
# Description: Search for cache duration configurations with varied patterns and in different file types

echo "Checking for cache duration configurations with alternative patterns:"
rg -i "duration[:=]\s*['\"]?5[sS]?[eE][cC][oO][nN][dD]['\"]?" --type ts --type json --type yaml

echo "Checking for cache timeout settings related to orderbook mid prices:"
rg -i "timeout[:=]\s*['\"]?5[sS]?[eE][cC][oO][nN][dD]['\"]?" --type ts --type json --type yaml

echo "Checking for interval settings related to orderbook mid price sampling:"
rg -i "interval[:=]\s*['\"]?1[sS]?" --type ts --type json --type yaml

Length of output: 1014

indexer/services/ender/__tests__/lib/candles-generator.test.ts (6)

117-122: LGTM! Test case updated to use Redis cache for orderbook prices.

The test case now uses OrderbookMidPricesCache.setPrice to set multiple prices for 'BTC-USD' in parallel, which better simulates real-world scenarios. This change aligns with the new Redis caching strategy for orderbook mid prices.

Consider extracting the ticker 'BTC-USD' into a constant at the top of the file, as it's used multiple times throughout the tests. This would improve maintainability and reduce the risk of typos.


162-167: LGTM! Test case updated to use Redis cache for orderbook prices.

This test case has been updated to use OrderbookMidPricesCache.setPrice for setting multiple prices for 'BTC-USD', consistent with the new Redis caching strategy. The change provides a more comprehensive test scenario by setting multiple prices.

For consistency with the previous test case, consider using the same price values (100000, 105000, 110000) instead of the current values (80000, 81000, 80500). This would make it easier to compare the behavior across different test cases.


503-503: LGTM! Test case updated to use Redis cache for orderbook price.

The test case has been updated to use OrderbookMidPricesCache.setPrice for setting the orderbook mid price, which is consistent with the new Redis caching strategy.

Consider setting multiple prices as done in previous test cases to maintain consistency and provide a more comprehensive test scenario. This could help ensure the behavior is correct when there are multiple price updates within the time frame.


601-601: LGTM! Test case updated to use Redis cache for orderbook price.

The test case has been updated to use OrderbookMidPricesCache.setPrice for setting the orderbook mid price, which aligns with the new Redis caching strategy.

For consistency with earlier test cases, consider setting multiple prices using Promise.all. This would provide a more comprehensive test scenario and ensure the behavior is correct when there are multiple price updates within the time frame.


665-677: LGTM! Test case updated to use Redis cache for multiple markets.

The test case has been improved to set prices for multiple markets ('BTC-USD', 'ISO-USD', 'ETH-USD') using OrderbookMidPricesCache.setPrice. This provides a more comprehensive test scenario. The expected output now correctly includes null values for markets without set prices ('ISO2-USD', 'SHIB-USD'), which is a good test for handling missing data.

Consider adding a comment explaining why 'ISO2-USD' and 'SHIB-USD' are expected to have null values. This would improve the clarity of the test case for future readers.


Line range hint 1-824: Overall LGTM! Test suite updated to use Redis cache for orderbook mid prices.

The test suite has been successfully updated to use OrderbookMidPricesCache.setPrice for managing orderbook mid prices, reflecting the shift to a Redis-based caching strategy. This change improves the realism of the tests and aligns them with the new implementation.

Consider standardizing the approach for setting prices across all test cases. Some tests set multiple prices using Promise.all, while others set only a single price. Adopting a consistent approach (preferably setting multiple prices) across all tests would improve the comparability of test cases and ensure comprehensive coverage of scenarios involving multiple price updates.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 1a33334 and 5a6ddd4.

Files selected for processing (12)
  • indexer/packages/redis/tests/caches/orderbook-mid-prices-cache.test.ts (1 hunks)
  • indexer/packages/redis/src/caches/orderbook-mid-prices-cache.ts (1 hunks)
  • indexer/packages/redis/src/caches/scripts.ts (2 hunks)
  • indexer/packages/redis/src/index.ts (1 hunks)
  • indexer/packages/redis/src/scripts/add_market_price.lua (1 hunks)
  • indexer/packages/redis/src/scripts/get_market_median_price.lua (1 hunks)
  • indexer/services/ender/tests/lib/candles-generator.test.ts (7 hunks)
  • indexer/services/ender/src/lib/candles-generator.ts (2 hunks)
  • indexer/services/roundtable/tests/tasks/cache-orderbook-mid-prices.test.ts (1 hunks)
  • indexer/services/roundtable/src/config.ts (2 hunks)
  • indexer/services/roundtable/src/index.ts (2 hunks)
  • indexer/services/roundtable/src/tasks/cache-orderbook-mid-prices.ts (1 hunks)
Additional comments not posted (19)
indexer/packages/redis/src/scripts/add_market_price.lua (2)

10-11: LGTM! Correct usage of ZADD command.

The implementation for adding the price to the sorted set is correct. Using the timestamp as the score is a good approach for maintaining time-ordered data.


1-17: Overall, the script effectively implements the required functionality.

The Lua script successfully implements a caching mechanism for orderbook mid prices using Redis, as described in the PR objectives. It maintains a 5-second sample of prices and efficiently manages the data using a sorted set.

Key points:

  1. The script correctly adds new prices to the sorted set.
  2. It removes entries older than 5 seconds, maintaining the desired time window.
  3. The implementation is concise and efficient.

Consider the suggested optimizations:

  1. Combining ZADD and ZREMRANGEBYSCORE for atomicity.
  2. Returning the number of elements in the sorted set for better monitoring.

These changes will further improve the script's robustness and usefulness.

indexer/packages/redis/src/scripts/get_market_median_price.lua (2)

1-5: LGTM: Correct key usage and price retrieval.

The script correctly uses KEYS[1] to get the sorted set key and retrieves all prices in ascending order, which is appropriate for median calculation.


7-10: LGTM: Proper handling of empty set.

The script correctly checks for an empty price set and returns nil, which is an appropriate way to handle this edge case.

indexer/services/roundtable/src/tasks/cache-orderbook-mid-prices.ts (2)

1-13: LGTM: Imports are well-organized and relevant.

The imports are appropriately structured, bringing in necessary components from different modules. This demonstrates good separation of concerns and modularity in the project structure.


18-40: LGTM: Well-structured implementation of the caching task.

The runTask function is well-implemented, covering the main requirements:

  1. Fetches all perpetual markets.
  2. Retrieves and caches the mid price for each market.
  3. Handles errors gracefully, ensuring one market's failure doesn't affect others.
  4. Uses structured logging for better observability.

The error handling ensures robustness, and the use of async/await makes the code readable and maintainable.

indexer/packages/redis/src/index.ts (1)

15-15: LGTM! The new export aligns with the PR objectives.

The addition of the OrderbookMidPricesCache export is consistent with the existing file structure and supports the implementation of the orderbook mid prices caching mechanism mentioned in the PR objectives.

To ensure the correct implementation of the OrderbookMidPricesCache module, please run the following script:

This script will help verify:

  1. The existence and structure of the OrderbookMidPricesCache module.
  2. The use of Redis for caching operations.
  3. The presence of unit tests for the new cache.
Verification successful

LGTM! The OrderbookMidPricesCache export and implementation are correctly integrated.

  • The OrderbookMidPricesCache is properly exported in index.ts.
  • Redis is utilized for caching within the module as intended.
  • Comprehensive unit tests are present and cover key functionalities.
Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of the OrderbookMidPricesCache module

# Test: Check if the OrderbookMidPricesCache module exists and contains expected functions
ast-grep --lang typescript --pattern $'export const OrderbookMidPricesCache = {
  $$$
  updateMidPrice: $_,
  getMidPrice: $_,
  $$$
}' indexer/packages/redis/src/caches/orderbook-mid-prices-cache.ts

# Test: Verify that the cache is using Redis
rg --type typescript 'redis\.hSet|redis\.hGet' indexer/packages/redis/src/caches/orderbook-mid-prices-cache.ts

# Test: Check for the presence of unit tests
fd -e test.ts orderbook-mid-prices-cache indexer/packages/redis/test/caches

Length of output: 548


Script:

#!/bin/bash
# Description: Re-verify the implementation of the OrderbookMidPricesCache module with corrected commands

# Test 1: Check if the OrderbookMidPricesCache module exists and contains expected functions with a more flexible pattern
ast-grep --lang typescript --pattern $'export\s+\*\s+as\s+OrderbookMidPricesCache\s+from\s+\'\./caches/orderbook-mid-prices-cache\';' indexer/packages/redis/src/index.ts

# Test 2: Verify that the cache is using Redis with the correct file type identifier
rg -t ts 'redis\.hSet|redis\.hGet' indexer/packages/redis/src/caches/orderbook-mid-prices-cache.ts

# Test 3: Locate unit tests for OrderbookMidPricesCache without assuming the directory structure
fd -e test.ts 'orderbook-mid-prices-cache' indexer/packages/redis/

Length of output: 417


Script:

#!/bin/bash
# Description: Further verify the OrderbookMidPricesCache module implementation

# Test 1: Display the contents of the OrderbookMidPricesCache module to verify exports and functions
cat indexer/packages/redis/src/caches/orderbook-mid-prices-cache.ts

# Test 2: Check for Redis-related imports in the OrderbookMidPricesCache module
rg -t ts 'import\s+.*redis.*' indexer/packages/redis/src/caches/orderbook-mid-prices-cache.ts

# Test 3: Display the contents of the test file to verify unit tests for OrderbookMidPricesCache
cat indexer/packages/redis/__tests__/caches/orderbook-mid-prices-cache.test.ts

Length of output: 7763


Script:

#!/bin/bash
# Description: Inspect the contents of index.ts to verify the OrderbookMidPricesCache export

# Display the contents of index.ts
cat indexer/packages/redis/src/index.ts

Length of output: 1423

indexer/packages/redis/src/caches/scripts.ts (1)

80-81: LGTM! New scripts correctly added to allLuaScripts array.

The addMarketPriceScript and getMarketMedianScript are properly included in the allLuaScripts array. This ensures they will be considered in any operations involving all Lua scripts.

indexer/services/roundtable/__tests__/tasks/cache-orderbook-mid-prices.test.ts (3)

1-27: LGTM: Imports and mock setups are well-structured.

The imports and mock setups are appropriate for the test file. The mocking of the logger and OrderbookLevelsCache is done correctly, which will allow for proper testing of the cache-orderbook-mid-prices task.


29-43: LGTM: Test suite setup and teardown are comprehensive.

The test suite setup and teardown processes are well-implemented:

  • Database migrations are run before all tests.
  • Data is cleared, Redis cache is deleted, and test data is seeded before each test.
  • Proper teardown of the database and resetting of mocks after all tests.

This ensures a clean and consistent test environment for each test case.


45-65: LGTM: Test case for caching mid prices is well-structured.

This test case effectively verifies the caching of mid prices for multiple markets. It correctly mocks the getOrderBookMidPrice function, checks if it's called with the right arguments, and verifies that the prices are cached correctly.

indexer/services/roundtable/src/config.ts (2)

63-63: LGTM: New configuration parameter for caching orderbook mid prices.

The addition of LOOPS_ENABLED_CACHE_ORDERBOOK_MID_PRICES is well-implemented and aligns with the PR objectives. The naming convention is consistent with other loop enablement parameters, and the default value of true correctly enables the new caching mechanism by default.


Line range hint 1-143: Overall assessment: Changes are well-implemented and align with PR objectives.

The additions to the configuration file are focused and consistent with the existing structure. They provide the necessary parameters to control the new orderbook mid price caching feature, allowing for easy enablement/disablement and interval adjustment. These changes support the PR's goal of implementing a caching mechanism for orderbook mid prices using Redis.

To ensure these new configurations are properly utilized, please run the following verification script:

This script will help ensure that the new configuration parameters are being used appropriately in the codebase and that any necessary new files for implementing the caching mechanism have been created.

Verification successful

It appears there was an issue recognizing the typescript file type in the previous search. Please run the following corrected verification scripts to ensure the new configuration parameters are properly utilized:


Verification Successful: New Configuration Parameters Are Properly Utilized

The new configuration parameters LOOPS_ENABLED_CACHE_ORDERBOOK_MID_PRICES and LOOPS_INTERVAL_MS_CACHE_ORDERBOOK_MID_PRICES are actively used in index.ts and config.ts. Additionally, the implementation and corresponding test files for orderbook mid price caching have been correctly added within the redis package.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of new configuration parameters in the codebase.

# Test 1: Check for the usage of LOOPS_ENABLED_CACHE_ORDERBOOK_MID_PRICES
echo "Checking usage of LOOPS_ENABLED_CACHE_ORDERBOOK_MID_PRICES:"
rg "LOOPS_ENABLED_CACHE_ORDERBOOK_MID_PRICES" --type typescript

# Test 2: Check for the usage of LOOPS_INTERVAL_MS_CACHE_ORDERBOOK_MID_PRICES
echo "Checking usage of LOOPS_INTERVAL_MS_CACHE_ORDERBOOK_MID_PRICES:"
rg "LOOPS_INTERVAL_MS_CACHE_ORDERBOOK_MID_PRICES" --type typescript

# Test 3: Look for any new files related to orderbook mid price caching
echo "Checking for new files related to orderbook mid price caching:"
fd -e ts -e js "orderbook.*mid.*price.*cache"

Length of output: 789


Script:

#!/bin/bash
# Description: Corrected verification of new configuration parameters usage.

# Test 1: Check for the usage of LOOPS_ENABLED_CACHE_ORDERBOOK_MID_PRICES
echo "Checking usage of LOOPS_ENABLED_CACHE_ORDERBOOK_MID_PRICES:"
rg "LOOPS_ENABLED_CACHE_ORDERBOOK_MID_PRICES" --type ts

# Test 2: Check for the usage of LOOPS_INTERVAL_MS_CACHE_ORDERBOOK_MID_PRICES
echo "Checking usage of LOOPS_INTERVAL_MS_CACHE_ORDERBOOK_MID_PRICES:"
rg "LOOPS_INTERVAL_MS_CACHE_ORDERBOOK_MID_PRICES" --type ts

# Test 3: Look for any new files related to orderbook mid price caching
echo "Checking for new files related to orderbook mid price caching:"
fd -e ts -e js "orderbook.*mid.*price.*cache"

Length of output: 1224

indexer/services/roundtable/src/index.ts (2)

13-13: LGTM: New import statement for cacheOrderbookMidPrices

The import statement for cacheOrderbookMidPrices is correctly placed and follows the existing pattern in the file. This import aligns with the PR objective of implementing a caching mechanism for orderbook mid prices.


Line range hint 1-287: Overall assessment: Well-implemented feature addition

The changes to this file successfully integrate the new orderbook mid price caching feature into the existing structure. The implementation follows established patterns, making it consistent with the rest of the codebase. The use of configuration values for enabling the feature and setting the interval provides flexibility and ease of management.

These changes align well with the PR objectives and appear to be a solid foundation for the new caching mechanism. No significant issues or inconsistencies were introduced by these changes.

indexer/services/ender/src/lib/candles-generator.ts (2)

23-23: Import statement updated to use OrderbookMidPricesCache

The import statement has been correctly updated to use OrderbookMidPricesCache instead of OrderbookLevelsCache. This change aligns with the PR objective of implementing a caching mechanism for orderbook mid prices.


541-543: Updated to use OrderbookMidPricesCache.getMedianPrice

The function has been correctly updated to use OrderbookMidPricesCache.getMedianPrice instead of OrderbookLevelsCache.getOrderBookMidPrice. This change aligns with the PR objective of implementing a caching mechanism for orderbook mid prices and using a 5-second median.

To ensure consistency across the codebase, please run the following script:

#!/bin/bash
# Description: Check for any remaining usage of OrderbookLevelsCache.getOrderBookMidPrice
# and verify that OrderbookMidPricesCache.getMedianPrice is used consistently.

echo "Checking for remaining usage of OrderbookLevelsCache.getOrderBookMidPrice:"
rg "OrderbookLevelsCache\.getOrderBookMidPrice" --type ts

echo "Verifying consistent usage of OrderbookMidPricesCache.getMedianPrice:"
rg "OrderbookMidPricesCache\.getMedianPrice" --type ts
indexer/services/ender/__tests__/lib/candles-generator.test.ts (1)

35-38: LGTM! Imports updated to reflect new Redis caching strategy.

The changes in the import statements reflect the shift from using updatePriceLevel to OrderbookMidPricesCache.setPrice for managing orderbook prices. This suggests an improvement in how orderbook mid prices are cached and retrieved, potentially leading to better performance and consistency.

indexer/packages/redis/__tests__/caches/orderbook-mid-prices-cache.test.ts (1)

77-100: Verify compatibility of Jest fake timers with jest.setSystemTime()

The use of jest.setSystemTime() requires the modern fake timers in Jest. By default, Jest 27 and above use modern timers, but if your configuration specifies legacy timers, this could cause issues.

Please ensure that modern timers are enabled. You can explicitly set them by modifying your test:

- jest.useFakeTimers();
+ jest.useFakeTimers('modern');

Alternatively, check your Jest configuration to confirm that modern timers are being used.

local cutoffTime = nowSeconds - fiveSeconds
redis.call("zremrangebyscore", priceCacheKey, "-inf", cutoffTime)

return true
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

Consider returning more meaningful information.

While returning true indicates successful execution, it doesn't provide any actionable information about the operation.

Consider returning the number of elements remaining in the sorted set after the operations:

return redis.call("zcard", priceCacheKey)

This provides more context about the state of the sorted set after the operation, which could be useful for monitoring or debugging purposes.

Comment on lines +18 to +40
export default async function runTask(): Promise<void> {
const markets: PerpetualMarketFromDatabase[] = await PerpetualMarketTable.findAll({}, []);

for (const market of markets) {
try {
const price = await OrderbookLevelsCache.getOrderBookMidPrice(market.ticker, redisClient);
if (price) {
await OrderbookMidPricesCache.setPrice(redisClient, market.ticker, price);
} else {
logger.info({
at: 'cache-orderbook-mid-prices#runTask',
message: `undefined price for ${market.ticker}`,
});
}
} catch (error) {
logger.error({
at: 'cache-orderbook-mid-prices#runTask',
message: error.message,
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

Consider performance optimizations for scaling.

The current implementation processes markets sequentially, which might become a bottleneck as the number of markets grows. Consider the following optimizations:

  1. Process markets in parallel using Promise.all() with a concurrency limit.
  2. Batch Redis operations if possible to reduce network overhead.

Example of processing in parallel with concurrency limit:

import pLimit from 'p-limit';

const limit = pLimit(10); // Adjust the concurrency limit as needed

const tasks = markets.map(market => limit(async () => {
  try {
    const price = await OrderbookLevelsCache.getOrderBookMidPrice(market.ticker, redisClient);
    if (price) {
      await OrderbookMidPricesCache.setPrice(redisClient, market.ticker, price);
    } else {
      logger.info({
        at: 'cache-orderbook-mid-prices#runTask',
        message: `Undefined price for market`,
        ticker: market.ticker,
      });
    }
  } catch (error) {
    logger.error({
      at: 'cache-orderbook-mid-prices#runTask',
      message: 'Failed to process market',
      ticker: market.ticker,
      error,
    });
  }
}));

await Promise.all(tasks);

Comment on lines +66 to +67
export const addMarketPriceScript: LuaScript = newLuaScript('addMarketPrice', '../scripts/add_market_price.lua');
export const getMarketMedianScript: LuaScript = newLuaScript('getMarketMedianPrice', '../scripts/get_market_median_price.lua');
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Issue Found: get_market_median_price.lua Missing KEYS and ARGV

The get_market_median_price.lua script does not contain the required KEYS and ARGV declarations, which are necessary for handling input parameters in Redis Lua scripts.

  • File Path: indexer/packages/redis/src/scripts/get_market_median_price.lua
Analysis chain

LGTM! New Lua scripts added for market price operations.

The addition of addMarketPriceScript and getMarketMedianScript aligns with the PR objectives of implementing a caching mechanism for orderbook mid prices. The naming and structure are consistent with existing patterns in the file.

To ensure the Lua script files exist and contain the expected content, please run the following script:

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence and content of the new Lua script files

# Test: Check if the Lua script files exist
for script in add_market_price.lua get_market_median_price.lua; do
  if [ ! -f "indexer/packages/redis/src/scripts/$script" ]; then
    echo "Error: $script does not exist"
    exit 1
  fi
done

# Test: Check if the Lua scripts contain relevant content
for script in add_market_price.lua get_market_median_price.lua; do
  if ! grep -q "KEYS" "indexer/packages/redis/src/scripts/$script" || ! grep -q "ARGV" "indexer/packages/redis/src/scripts/$script"; then
    echo "Warning: $script may not contain expected Lua script content"
  fi
done

echo "Verification complete"

Length of output: 934

Comment on lines +111 to +126
if (!prices || prices.length === 0) {
return null;
}

if (prices.length === 1) {
return Big(prices[0]).toFixed();
}

if (prices.length === 2) {
const [price1, price2] = prices.map((price) => {
return Big(price);
});
return price1.plus(price2).div(2).toFixed();
}

return null;
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 median calculation for more than two prices

The getMedianPrice function currently returns null when there are more than two prices in the cache. This might not be the intended behavior. To accurately compute the median for any number of prices, modify the function to handle cases where there are more than two prices.

Apply this diff to compute the median for any number of prices:

 if (!prices || prices.length === 0) {
   return null;
 }

-if (prices.length === 1) {
-  return Big(prices[0]).toFixed();
-}
-
-if (prices.length === 2) {
-  const [price1, price2] = prices.map((price) => {
-    return Big(price);
-  });
-  return price1.plus(price2).div(2).toFixed();
-}
-
-return null;
+const priceNumbers = prices.map((price) => Big(price));
+const sortedPrices = priceNumbers.sort((a, b) => a.minus(b).toNumber());
+const middle = Math.floor(sortedPrices.length / 2);
+
+if (sortedPrices.length % 2 === 0) {
+  // Even number of prices, average the two middle prices
+  const median = sortedPrices[middle - 1].plus(sortedPrices[middle]).div(2);
+  return median.toFixed();
+} else {
+  // Odd number of prices, take the middle price
+  return sortedPrices[middle].toFixed();
+}

This adjustment ensures that the median is correctly calculated regardless of the number of prices in the cache.

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 (!prices || prices.length === 0) {
return null;
}
if (prices.length === 1) {
return Big(prices[0]).toFixed();
}
if (prices.length === 2) {
const [price1, price2] = prices.map((price) => {
return Big(price);
});
return price1.plus(price2).div(2).toFixed();
}
return null;
if (!prices || prices.length === 0) {
return null;
}
const priceNumbers = prices.map((price) => Big(price));
const sortedPrices = priceNumbers.sort((a, b) => a.minus(b).toNumber());
const middle = Math.floor(sortedPrices.length / 2);
if (sortedPrices.length % 2 === 0) {
// Even number of prices, average the two middle prices
const median = sortedPrices[middle - 1].plus(sortedPrices[middle]).div(2);
return median.toFixed();
} else {
// Odd number of prices, take the middle price
return sortedPrices[middle].toFixed();
}

Comment on lines +81 to +106
let evalAsync: (
marketCacheKey: string,
) => Promise<string[]> = (
marketCacheKey,
) => {
return new Promise((resolve, reject) => {
const callback: Callback<string[]> = (
err: Error | null,
results: string[],
) => {
if (err) {
return reject(err);
}
return resolve(results);
};

client.evalsha(
getMarketMedianScript.hash,
1,
marketCacheKey,
callback,
);
});
};
evalAsync = evalAsync.bind(client);

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

Refactor to simplify async function handling in getMedianPrice

Similar to setPrice, the evalAsync function within getMedianPrice can be simplified. The use of bind is unnecessary since client is accessible in the outer scope. Simplify the function by removing evalAsync and directly returning a Promise.

Apply this diff to simplify the code:

 export async function getMedianPrice(client: RedisClient, ticker: string): Promise<string | null> {
-  let evalAsync: (
-    marketCacheKey: string,
-  ) => Promise<string[]> = (
-    marketCacheKey,
-  ) => {
-    return new Promise((resolve, reject) => {
-      const callback: Callback<string[]> = (
-        err: Error | null,
-        results: string[],
-      ) => {
-        if (err) {
-          return reject(err);
-        }
-        return resolve(results);
-      };
-
-      client.evalsha(
-        getMarketMedianScript.hash,
-        1,
-        marketCacheKey,
-        callback,
-      );
-    });
-  };
-  evalAsync = evalAsync.bind(client);
-
-  const prices = await evalAsync(
-    getOrderbookMidPriceCacheKey(ticker),
-  );
+  const marketCacheKey = getOrderbookMidPriceCacheKey(ticker);
+  const prices = await new Promise<string[]>((resolve, reject) => {
+    client.evalsha(
+      getMarketMedianScript.hash,
+      1,
+      marketCacheKey,
+      (err: Error | null, results: string[]) => {
+        if (err) {
+          reject(err);
+        } else {
+          resolve(results);
+        }
+      },
+    );
+  });

This refactoring removes unnecessary layers of abstraction, enhancing clarity and maintainability.

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
let evalAsync: (
marketCacheKey: string,
) => Promise<string[]> = (
marketCacheKey,
) => {
return new Promise((resolve, reject) => {
const callback: Callback<string[]> = (
err: Error | null,
results: string[],
) => {
if (err) {
return reject(err);
}
return resolve(results);
};
client.evalsha(
getMarketMedianScript.hash,
1,
marketCacheKey,
callback,
);
});
};
evalAsync = evalAsync.bind(client);
const marketCacheKey = getOrderbookMidPriceCacheKey(ticker);
const prices = await new Promise<string[]>((resolve, reject) => {
client.evalsha(
getMarketMedianScript.hash,
1,
marketCacheKey,
(err: Error | null, results: string[]) => {
if (err) {
reject(err);
} else {
resolve(results);
}
},
);
});

Comment on lines +38 to +69
let evalAsync: (
marketCacheKey: string,
) => Promise<void> = (marketCacheKey) => {

return new Promise<void>((resolve, reject) => {
const callback: Callback<void> = (
err: Error | null,
) => {
if (err) {
return reject(err);
}
return resolve();
};

const nowSeconds = Math.floor(Date.now() / 1000); // Current time in seconds
client.evalsha(
addMarketPriceScript.hash,
numKeys,
marketCacheKey,
price,
nowSeconds,
callback,
);

});
};
evalAsync = evalAsync.bind(client);

return evalAsync(
getOrderbookMidPriceCacheKey(ticker),
);
}
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

Refactor to simplify async function handling in setPrice

The evalAsync function within setPrice is unnecessarily complex. Since client is accessible in the outer scope and this is not used, the use of bind is redundant. Consider simplifying the function by removing evalAsync and directly returning a Promise.

Apply this diff to simplify the code:

 export async function setPrice(
   client: RedisClient,
   ticker: string,
   price: string,
 ): Promise<void> {
-  // Number of keys for the lua script.
-  const numKeys: number = 1;
-
-  let evalAsync: (
-    marketCacheKey: string,
-  ) => Promise<void> = (marketCacheKey) => {
-
-    return new Promise<void>((resolve, reject) => {
-      const callback: Callback<void> = (
-        err: Error | null,
-      ) => {
-        if (err) {
-          return reject(err);
-        }
-        return resolve();
-      };
-
-      const nowSeconds = Math.floor(Date.now() / 1000); // Current time in seconds
-      client.evalsha(
-        addMarketPriceScript.hash,
-        numKeys,
-        marketCacheKey,
-        price,
-        nowSeconds,
-        callback,
-      );
-    });
-  };
-  evalAsync = evalAsync.bind(client);
-
-  return evalAsync(
-    getOrderbookMidPriceCacheKey(ticker),
-  );
+  const marketCacheKey = getOrderbookMidPriceCacheKey(ticker);
+  const nowSeconds = Math.floor(Date.now() / 1000); // Current time in seconds
+  return new Promise<void>((resolve, reject) => {
+    client.evalsha(
+      addMarketPriceScript.hash,
+      1,
+      marketCacheKey,
+      price,
+      nowSeconds,
+      (err: Error | null) => {
+        if (err) {
+          reject(err);
+        } else {
+          resolve();
+        }
+      },
+    );
+  });
 }

This refactoring streamlines the setPrice function by removing unnecessary complexity and improves readability.

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
let evalAsync: (
marketCacheKey: string,
) => Promise<void> = (marketCacheKey) => {
return new Promise<void>((resolve, reject) => {
const callback: Callback<void> = (
err: Error | null,
) => {
if (err) {
return reject(err);
}
return resolve();
};
const nowSeconds = Math.floor(Date.now() / 1000); // Current time in seconds
client.evalsha(
addMarketPriceScript.hash,
numKeys,
marketCacheKey,
price,
nowSeconds,
callback,
);
});
};
evalAsync = evalAsync.bind(client);
return evalAsync(
getOrderbookMidPriceCacheKey(ticker),
);
}
export async function setPrice(
client: RedisClient,
ticker: string,
price: string,
): Promise<void> {
const marketCacheKey = getOrderbookMidPriceCacheKey(ticker);
const nowSeconds = Math.floor(Date.now() / 1000); // Current time in seconds
return new Promise<void>((resolve, reject) => {
client.evalsha(
addMarketPriceScript.hash,
1,
marketCacheKey,
price,
nowSeconds,
(err: Error | null) => {
if (err) {
reject(err);
} else {
resolve();
}
},
);
});
}

Comment on lines +20 to +27
await client.zrange(
`${ORDERBOOK_MID_PRICES_CACHE_KEY_PREFIX}${ticker}`,
0,
-1,
(_: any, response: string[]) => {
expect(response[0]).toBe('50000');
},
);
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

Refactor Redis client calls to use promises and async/await

The test code is currently using callback-based Redis client calls, which can be less readable and more prone to errors. Redis client methods support Promises, allowing you to use async/await syntax for better readability and error handling.

Apply this diff to refactor the Redis calls:

- await client.zrange(
-   `${ORDERBOOK_MID_PRICES_CACHE_KEY_PREFIX}${ticker}`,
-   0,
-   -1,
-   (_: any, response: string[]) => {
-     expect(response[0]).toBe('50000');
-   },
- );
+ const response = await client.zrange(
+   `${ORDERBOOK_MID_PRICES_CACHE_KEY_PREFIX}${ticker}`,
+   0,
+   -1,
+ );
+ expect(response[0]).toBe('50000');

And similarly in the next test:

- await client.zrange(
-   `${ORDERBOOK_MID_PRICES_CACHE_KEY_PREFIX}${ticker}`,
-   0,
-   -1,
-   (_: any, response: string[]) => {
-     expect(response).toEqual(['49000', '50000', '51000']);
-   },
- );
+ const response = await client.zrange(
+   `${ORDERBOOK_MID_PRICES_CACHE_KEY_PREFIX}${ticker}`,
+   0,
+   -1,
+ );
+ expect(response).toEqual(['49000', '50000', '51000']);

Also applies to: 37-45

@adamfraser adamfraser merged commit f346663 into main Sep 25, 2024
16 checks passed
@adamfraser adamfraser deleted the adam/candles-fix branch September 25, 2024 14:25
@adamfraser
Copy link
Contributor Author

@Mergifyio backport release/indexer/v7.x

Copy link
Contributor

mergify bot commented Sep 25, 2024

backport release/indexer/v7.x

✅ Backports have been created

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