Skip to content

Redis client factory (error handling and good defaults) #1761

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

Merged
merged 2 commits into from
Mar 5, 2025

Conversation

matt-aitken
Copy link
Member

@matt-aitken matt-aitken commented Mar 5, 2025

@internal/redis package for creating Redis clients with sensible defaults and error handling

Summary by CodeRabbit

  • New Features

    • Integrated a new Redis connectivity utility that enhances client initialization and error resilience.
  • Refactor

    • Standardized the process for establishing Redis connections across components to improve overall reliability.
  • Documentation

    • Added user-facing documentation outlining the setup and usage of the new Redis utility.
  • Tests

    • Updated test suites to ensure the new Redis client behavior is accurate and robust.

Copy link

changeset-bot bot commented Mar 5, 2025

⚠️ No Changeset found

Latest commit: defadae

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Mar 5, 2025

Walkthrough

The pull request introduces a new internal Redis package and updates dependencies across multiple modules to utilize it. It adds new path mappings in TypeScript configurations and refactors existing Redis client initializations to use a unified createRedisClient function. The changes include error handling improvements, documentation updates, and modifications in tests and engine implementations for consistent Redis client management.

Changes

Files Changes Summary
apps/webapp/package.json
apps/webapp/tsconfig.json
Added dependency "@internal/redis": "workspace:*" and new TS path mappings for the internal Redis package.
internal-packages/redis-worker/package.json
internal-packages/redis-worker/tsconfig.json
Added dependency on "@internal/redis" and updated TS paths to resolve the Redis package.
internal-packages/redis-worker/src/queue.ts
internal-packages/redis-worker/src/worker.ts
internal-packages/redis-worker/src/worker.test.ts
Refactored Redis client instantiation to call createRedisClient, replacing direct instantiation and event listeners with consolidated error handling.
internal-packages/redis/README.md
internal-packages/redis/package.json
internal-packages/redis/src/index.ts
internal-packages/redis/tsconfig.json
Introduced a new internal Redis package that implements createRedisClient, provides error callback support, and includes a README and TS configuration.
internal-packages/run-engine/package.json
internal-packages/run-engine/tsconfig.json
Added dependency on "@internal/redis" and updated TS paths for Redis resolution.
internal-packages/run-engine/src/engine/index.ts
internal-packages/run-engine/src/engine/locking.test.ts
internal-packages/run-engine/src/run-queue/index.ts
internal-packages/run-engine/src/run-queue/index.test.ts
Modified engine and queue implementations and related tests to use createRedisClient for standardized Redis client creation and error handling.

Sequence Diagram(s)

sequenceDiagram
    participant Module as Module/Engine/Test
    participant CR as createRedisClient
    participant Redis as Redis Client

    Module->>CR: Call createRedisClient(options, { onError })
    CR->>Redis: Initialize Redis client with default & provided options
    alt VITEST mode active
        Redis-->>CR: Swallow errors
    else
        Redis-->>CR: Errors trigger onError callback
    end
    CR-->>Module: Return configured Redis client
Loading

Possibly related PRs

Suggested reviewers

  • ericallam

Poem

I'm a rabbit, quick and bright,
Hopping through code with new insight.
Redis now sings a unified tune,
With error handling that comes so soon.
In our forest of code, leaps are bold,
Celebrating changes as stories unfold!
🐇🌟

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (7)
internal-packages/redis-worker/src/queue.ts (2)

54-55: Verify if empty lines removal was intentional.

The code has fewer empty lines compared to the original pattern. While this doesn't affect functionality, confirm this style change was intentional for consistency.


56-65: Consider adding a comment explaining the Redis configuration.

The Redis configuration includes important settings like the retry strategy and max retries. Consider adding a brief comment explaining these choices for future maintainers.

 this.redis = createRedisClient(
   {
     ...redisOptions,
     keyPrefix: `${redisOptions.keyPrefix ?? ""}{queue:${name}:}`,
+    // Exponential backoff with 50ms base and 1s max
     retryStrategy(times) {
       const delay = Math.min(times * 50, 1000);
       return delay;
     },
+    // Increase max retries to prevent operation failures during temporary disconnections
     maxRetriesPerRequest: 20,
   },
internal-packages/redis/src/index.ts (3)

12-12: Consider making the logger level configurable.

The logger is initialized with a hardcoded "debug" level. For better flexibility, consider accepting the log level as a parameter to the createRedisClient function.

-const logger = new Logger("Redis", "debug");
+const logger = new Logger("Redis", process.env.REDIS_LOG_LEVEL || "debug");

1-40: Consider adding connection timeouts to the default configuration.

The current default configuration doesn't include connection timeout settings. For improved reliability, especially in containerized environments, consider adding reasonable timeouts.

const defaultOptions: Partial<RedisOptions> = {
  retryStrategy: (times: number) => {
    const delay = Math.min(times * 50, 1000);
    return delay;
  },
  maxRetriesPerRequest: 20,
+  connectTimeout: 10000,
+  commandTimeout: 5000,
};

1-40: Consider adding health checking capability.

For systems that need health monitoring, you might want to provide a simple method to check if the Redis connection is healthy. This could be exposed as part of the returned client.

internal-packages/run-engine/src/engine/index.ts (2)

145-150: Consider adding recovery logic for critical Redis errors.

While the error logging is good, consider adding some recovery logic or escalation for critical Redis errors that might require intervention. For example, you might want to trigger alerts for persistent connection issues or implement a circuit breaker pattern.

onError: (error) => {
  this.logger.error(`RunLock redis client error:`, {
    error,
    keyPrefix: options.runLock.redis.keyPrefix,
  });
+  
+  // Implement additional handling for persistent errors
+  if (this.redisErrorCount === undefined) this.redisErrorCount = 0;
+  this.redisErrorCount++;
+  
+  // Alert on persistent errors
+  if (this.redisErrorCount > 10) {
+    // Could trigger an alert or notification here
+    this.logger.critical("Persistent Redis connection errors detected", {
+      errorCount: this.redisErrorCount,
+      keyPrefix: options.runLock.redis.keyPrefix,
+    });
+    // Reset counter to avoid flooding alerts
+    this.redisErrorCount = 0;
+  }
},

2513-2514: Ensure Redis connections are properly closed.

The quit() method on Redis clients is being called in the quit() method of the RunEngine class, which is good. However, consider adding a check to ensure the client exists before trying to quit.

// This is just a failsafe
-await this.runLockRedis.quit();
+if (this.runLockRedis) {
+  try {
+    await this.runLockRedis.quit();
+  } catch (error) {
+    this.logger.warn("Error while quitting Redis connection", { error });
+  }
+}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4358e2d and defadae.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • apps/webapp/package.json (2 hunks)
  • apps/webapp/tsconfig.json (1 hunks)
  • internal-packages/redis-worker/package.json (2 hunks)
  • internal-packages/redis-worker/src/queue.ts (2 hunks)
  • internal-packages/redis-worker/src/worker.test.ts (2 hunks)
  • internal-packages/redis-worker/src/worker.ts (2 hunks)
  • internal-packages/redis-worker/tsconfig.json (1 hunks)
  • internal-packages/redis/README.md (1 hunks)
  • internal-packages/redis/package.json (1 hunks)
  • internal-packages/redis/src/index.ts (1 hunks)
  • internal-packages/redis/tsconfig.json (1 hunks)
  • internal-packages/run-engine/package.json (1 hunks)
  • internal-packages/run-engine/src/engine/index.ts (2 hunks)
  • internal-packages/run-engine/src/engine/locking.test.ts (2 hunks)
  • internal-packages/run-engine/src/run-queue/index.test.ts (6 hunks)
  • internal-packages/run-engine/src/run-queue/index.ts (2 hunks)
  • internal-packages/run-engine/tsconfig.json (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • internal-packages/redis/README.md
  • internal-packages/redis/tsconfig.json
  • internal-packages/redis/package.json
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: units / 🧪 Unit Tests
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (34)
internal-packages/run-engine/package.json (1)

8-8: Good addition of the Redis client factory dependency.

Adding the @internal/redis package as a dependency supports the refactoring to use a centralized Redis client factory with improved error handling and sensible defaults.

apps/webapp/package.json (2)

55-55: Good addition of the Redis client factory dependency.

Adding the @internal/redis package as a dependency allows the webapp to use the centralized Redis client creation with improved error handling.


262-262: Added proper EOF newline.

Adding the trailing newline at the end of the file follows good file formatting practices.

internal-packages/redis-worker/package.json (2)

10-10: Good addition of the Redis client factory dependency.

Adding the @internal/redis package as a dependency enables the redis-worker to use the centralized Redis client factory with consistent error handling and default configuration.


27-27: Added proper EOF newline.

Adding the trailing newline at the end of the file follows good file formatting practices.

internal-packages/run-engine/src/engine/locking.test.ts (3)

1-1: Good import of the Redis client factory.

Importing the createRedisClient function from the newly created @internal/redis package aligns with the PR objective to centralize Redis client creation.


8-8: Good refactoring to use the Redis client factory.

Replacing direct Redis instantiation with the factory function createRedisClient is a positive change that will benefit from:

  1. Consistent error handling across all Redis client creations
  2. Standardized default configurations
  3. Centralized management of Redis client options

This refactoring maintains the same interface while improving the underlying implementation.

Also applies to: 26-26


1-48:

✅ Verification successful

Verify Redis client cleanup during test failures.

The tests correctly call redis.quit() in the finally block, which is good practice for resource cleanup. However, it's worth verifying that the new createRedisClient function doesn't change how connections are managed.


🏁 Script executed:

#!/bin/bash
# Check if the createRedisClient function handles connection failures gracefully
# and if it implements any additional cleanup mechanisms beyond what's in the tests

# First, look for the implementation of createRedisClient
echo "Looking for createRedisClient implementation..."
rg -t ts "export\s+function\s+createRedisClient" --after-context=20

Length of output: 2568


Redis Client Cleanup and Management Verified

The tests correctly invoke redis.quit() in the finally block, ensuring that connection cleanup is explicit. I confirmed that the createRedisClient implementation in internal-packages/redis/src/index.ts behaves as expected in test environments (using the process.env.VITEST flag to simply swallow errors) and does not change connection management. No additional cleanup mechanism interferes with the tests’ resource management.

internal-packages/run-engine/tsconfig.json (1)

22-23: Well-organized TypeScript path mappings added

The path mappings for the new @internal/redis package are correctly configured, following the established pattern in the project for internal packages.

internal-packages/redis-worker/src/worker.test.ts (2)

8-8: Appropriate import for the new Redis client factory

The import statement for createRedisClient from the new @internal/redis package is correctly placed.


245-249: LGTM! Standardized Redis client creation with improved error handling

The direct Redis instantiation is replaced with the createRedisClient factory function, providing consistency with the rest of the codebase and potentially better error handling.

internal-packages/run-engine/src/run-queue/index.ts (3)

24-24: Import for the new Redis client factory added correctly

The import for createRedisClient is properly placed at the top of the file with other imports.


75-82: Excellent enhancement: Redis client with proper error handling

This change replaces direct Redis instantiation with the createRedisClient factory, adding robust error handling that logs Redis client errors. This is a significant improvement for debugging and monitoring in production environments.


88-95: Consistent error handling for Redis subscriber client

The subscriber Redis client now uses the same factory function with similar error handling, ensuring consistent behavior between the main client and the subscriber.

internal-packages/run-engine/src/run-queue/index.test.ts (6)

10-10: Proper import of the Redis client factory

The import statement for createRedisClient is correctly placed.


472-472: Standardized Redis client creation in tests

The test now uses the createRedisClient factory function instead of direct Redis instantiation, maintaining consistency with the implementation code.


602-602: Consistent use of Redis client factory

Continued consistency in using the createRedisClient factory across multiple tests.


693-693: Maintained consistency in Redis client creation

The standardized approach to Redis client creation is maintained throughout all test cases.


807-807: Consistent application of Redis client factory pattern

The consistent application of the createRedisClient pattern throughout the test suite helps ensure that all Redis clients are created with the same configuration and error handling.


862-866: Well-implemented Redis client for redrive testing

The Redis client for testing redrive functionality is properly created using the new factory function, maintaining consistency with the other Redis client instantiations in the tests.

internal-packages/redis-worker/src/worker.ts (2)

12-12: Good import of the new Redis client factory.

The addition of the import from @internal/redis allows for more standardized Redis client creation.


112-119: Great implementation of robust error handling for Redis client.

The refactoring from direct Redis instantiation to using the createRedisClient factory function with an error handler is a significant improvement. This approach:

  • Centralizes Redis client creation logic
  • Provides explicit error handling through the onError callback
  • Logs Redis client errors with helpful context including the keyPrefix

The error handler will help with debugging Redis connection issues in production.

apps/webapp/tsconfig.json (1)

41-43: Correctly configured path aliases for the new Redis package.

The path mappings are properly configured to allow importing from the new @internal/redis package. This follows the project's established pattern for internal packages.

internal-packages/redis-worker/tsconfig.json (1)

22-23: Correctly configured path aliases for Redis package dependencies.

The path mappings are properly configured to allow the redis-worker package to import from the new @internal/redis package. This follows the project's established pattern for dependency resolution.

internal-packages/redis-worker/src/queue.ts (3)

1-1: Good import of the Redis client factory function.

Importing createRedisClient from the new internal Redis package will help standardize Redis client creation across the codebase.


56-74: Excellent refactoring of Redis client creation with proper error handling.

The refactoring to use createRedisClient provides several benefits:

  1. Consistent Redis client creation pattern across the codebase
  2. Proper error handling with detailed error logging
  3. Preservation of the important Redis configuration:
    • Key prefix with queue name
    • Retry strategy
    • Max retries per request

The error handler provides valuable context by including the original keyPrefix in the error logs, which will help with debugging Redis issues in production.


67-72: Good error handling with contextual logging.

The error handler provides useful context by including both the error object and the Redis keyPrefix. This will be helpful for diagnosing Redis connection issues in production.

internal-packages/redis/src/index.ts (5)

1-2: Good choice of importing specific types from 'ioredis'.

The specific import of Redis and RedisOptions types from 'ioredis' follows best practices for TypeScript imports.


4-10: Well-structured default Redis configuration with sensible retry values.

The default retry strategy using exponential backoff with a cap at 1000ms and a maximum of 20 retries per request represents a good balance between resilience and avoiding excessive resource consumption. This follows Redis connection best practices by not giving up too quickly on transient errors while preventing infinite retry loops.


14-21: LGTM! Well-designed function signature.

The function provides good flexibility by accepting both Redis configuration options and custom error handlers. The implementation correctly merges the default options with user-provided options.


23-29: Good test environment handling.

The special case for Vitest environment is important to prevent test failures due to Redis connection errors. This is a good practice for making tests more reliable.


31-37: Robust error handling with fallback.

The error handling logic first tries to use the user-provided handler if available, then falls back to logging the error with helpful context. This provides flexibility while ensuring errors aren't silently dropped.

internal-packages/run-engine/src/engine/index.ts (2)

1-1: Good modularization by importing the Redis client factory.

The import of createRedisClient from the newly created @internal/redis package demonstrates good adherence to modular design principles.


139-152: Improved Redis client initialization with proper error handling.

The updated code correctly uses the new createRedisClient function with appropriate error handling configuration. This change centralizes Redis client creation logic and ensures consistent error handling across the application.

The error handler logs useful context including the keyPrefix, which will help with debugging Redis issues.

@matt-aitken matt-aitken merged commit 983bb41 into main Mar 5, 2025
11 of 13 checks passed
@matt-aitken matt-aitken deleted the redis-client-factory branch March 5, 2025 18:32
@coderabbitai coderabbitai bot mentioned this pull request Apr 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants