Skip to content

Conversation

@ibrahimmenem
Copy link
Contributor

@ibrahimmenem ibrahimmenem commented Nov 12, 2025

add extra check to detect v3.x and take into account legacy mode

fixes #11635
fixes #11749

Description of change

  • Redis 3 client is being detected as v5 because the set.length is returning 0.

  • The fix checks if the client has no connect() function and immediately set it as version 3

  • also conserves the legacy mode flag to be used in isRedis5OrHigher , and changes the function
    name to shouldUsePromiseApi to match what it now does.

  • tested locally with redis3 client

  • added 2 unit tests

Pull-Request Checklist

  • Code is up-to-date with the master branch
  • This pull request links relevant issues as Fixes #11635
  • There are new or updated unit tests validating the change
  • Documentation has been updated to reflect this change

Summary by CodeRabbit

Release Notes

  • New Features

    • Improved Redis compatibility with automatic version detection and support for Redis 3.x, 4.x, and 5+ environments
    • Enhanced query result caching reliability across different Redis versions
  • Tests

    • Added comprehensive unit tests for Redis version detection and compatibility scenarios

add extra check to detect v3.x and take into account legacy mode

closes typeorm#11635
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

Walkthrough

Added legacy mode support for Redis client compatibility by introducing runtime version detection (Redis 3.x via connect() presence check) and a shouldUsePromiseApi() method that determines API usage based on both Redis version and legacy mode status, replacing the previous isRedis5OrHigher() check across all cache operations.

Changes

Cohort / File(s) Summary
Redis Legacy Mode Implementation
src/cache/RedisQueryResultCache.ts
Added isLegacyMode protected property for Redis 4.x compatibility; introduced shouldUsePromiseApi() method to conditionally use Promise-based or callback-based API; added Redis 3.x detection via connect() method presence check; replaced all isRedis5OrHigher() calls with shouldUsePromiseApi() throughout cache operations (disconnect, getFromCache, storeInCache, clear, deleteKey).
Unit Tests for Redis Query Result Cache
test/unit/cache/redis-query-result-cache.ts
Added new test suite validating Redis v3 callback-based client detection and legacy mode behavior; tests verify correct redisMajorVersion assignment, shouldUsePromiseApi() return values, and client lifecycle interactions (createClient, legacyQuit calls).

Sequence Diagram

sequenceDiagram
    participant App
    participant RedisQueryResultCache
    participant PlatformTools
    participant RedisClient
    
    App->>RedisQueryResultCache: connect()
    RedisQueryResultCache->>PlatformTools: load("redis")
    PlatformTools-->>RedisQueryResultCache: createClient factory
    RedisQueryResultCache->>RedisClient: createClient()
    RedisQueryResultCache->>RedisQueryResultCache: detectRedisVersion()
    
    alt connect() method present
        RedisQueryResultCache->>RedisClient: client.connect()
        RedisClient-->>RedisQueryResultCache: connected
        RedisQueryResultCache->>RedisQueryResultCache: Set redisMajorVersion >= 4
    else connect() method absent
        RedisQueryResultCache->>RedisQueryResultCache: Set redisMajorVersion = 3
    end
    
    alt legacyMode enabled
        RedisQueryResultCache->>RedisQueryResultCache: Set isLegacyMode = true
    end
    
    App->>RedisQueryResultCache: getFromCache()
    RedisQueryResultCache->>RedisQueryResultCache: shouldUsePromiseApi()?
    
    alt shouldUsePromiseApi() returns true
        RedisQueryResultCache->>RedisClient: client.get() [Promise-based]
        RedisClient-->>RedisQueryResultCache: Promise<value>
    else shouldUsePromiseApi() returns false
        RedisQueryResultCache->>RedisClient: client.get(key, callback) [Callback-based]
        RedisClient-->>RedisQueryResultCache: value via callback
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

  • New protected property and private method require understanding legacy mode semantics
  • Version detection logic (including Redis 3.x check via connect() presence) needs verification against edge cases
  • All Redis operation methods updated consistently, but each call site should be verified for correct shouldUsePromiseApi() usage
  • New test cases introduce mock infrastructure that should be reviewed for completeness and accuracy

Possibly related issues

  • getFromCache method throws error #11635: Directly addressed by this PR—fixes the "Cannot read properties of undefined (reading 'then')" error by detecting when Redis client uses callback-based API (Redis 3.x or legacy mode) and avoiding Promise-based operations in those cases.
  • Bug in detectRedisVersion #11749: Related through Redis v3 detection logic—the added connect() presence guard addresses v3 detection that was previously misidentified.

Possibly related PRs

Suggested reviewers

  • gioboa
  • alumni

Poem

🐰 A Redis tale of modes and versions past,
Legacy and promise, compatible at last!
Three, four, and five—our fuzzy friend detects,
Callbacks bloom where legacy connects! ✨
TypeORM hops on, no more errors—hooray! 🎉

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes directly address issue #11635 by adding Redis v3 detection and legacy mode handling to prevent the 'Cannot read properties of undefined' error when client.get doesn't return a Promise.
Out of Scope Changes check ✅ Passed All code changes are tightly scoped to Redis version detection and legacy mode support, directly related to fixing the reported issue without introducing unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(redis): version detection logic' directly addresses the main change in the PR—fixing Redis version detection to properly handle Redis 3.x and legacy mode compatibility.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cache/RedisQueryResultCache.ts (1)

71-77: Redis v5 incorrectly receives legacy mode flag; Promise API inaccessible

The code detects Redis 4+ by checking for the connect() method (line 69), which is present in both Redis v4 and v5. However, it unconditionally sets legacyMode: true for all matches (line 74) before precise version detection occurs (line 97).

Redis v5 does not support legacyMode at createClient time; instead it uses client.legacy() method on an existing client. The project supports Redis v3, v4, and v5 (per package.json: "redis": "^3.1.1 || ^4.0.0 || ^5.0.14"), so the code must distinguish between them.

Critical issues:

  • Line 74 sets legacyMode: true for Redis v5, which is unsupported and causes Redis v5 clients to never use Promise-based API (line 345-348)
  • Version detection at line 97 (detectRedisVersion()) occurs after the legacyMode flag is already set, making it impossible to apply Redis 5-specific handling
  • Line 327 correctly identifies Redis v5, but by then isLegacyMode is already true, permanently disabling Promise API for v5 users

Fix required: Refactor lines 71-77 to detect Redis version before applying legacyMode. Only set legacyMode: true for Redis v4; use an alternative approach for Redis v5 (e.g., call client.legacy() after connection if needed for compatibility) or skip legacy mode entirely for v5.

🧹 Nitpick comments (1)
src/cache/RedisQueryResultCache.ts (1)

324-331: Minor: Comment slightly imprecise.

The comment on line 329 mentions "Redis 3/4 set method" but line 330 sets redisMajorVersion = 3. While this is functionally correct (Redis 4 would have isLegacyMode = true anyway, forcing callback-based API), the version number could be misleading for debugging.

Consider updating the comment for clarity:

             } else {
-                // Redis 3/4 set method requires more parameters (key, value, flag, duration, callback)
-                this.redisMajorVersion = 3
+                // Redis 3/4 set method requires more parameters (key, value, flag, duration, callback)
+                // Set to 3 as fallback (Redis 4 would have isLegacyMode=true anyway)
+                this.redisMajorVersion = 3
             }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e04ffd3 and 41206bf.

📒 Files selected for processing (2)
  • src/cache/RedisQueryResultCache.ts (9 hunks)
  • test/unit/cache/redis-query-result-cache.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
test/unit/cache/redis-query-result-cache.ts (2)
src/platform/PlatformTools.ts (1)
  • PlatformTools (17-279)
src/cache/RedisQueryResultCache.ts (1)
  • RedisQueryResultCache (11-350)
🔇 Additional comments (6)
test/unit/cache/redis-query-result-cache.ts (3)

1-21: LGTM!

Test setup is well-structured with proper cleanup and type definitions for Redis callbacks.


23-86: LGTM!

Test accurately simulates a Redis v3 client (lacking connect() method) and verifies correct version detection and callback-based API usage.


88-149: LGTM, but consider testing Redis 5 without legacy mode.

The test validates legacy mode behavior correctly. However, the current implementation always enables legacyMode for any Redis client with a connect() method (Redis 4+). This means Redis 5 users never get the native promise-based API even though they should.

Consider adding a test case for Redis 5 clients that should use the native promise-based API without legacy mode. The current flow at line 71-76 in src/cache/RedisQueryResultCache.ts unconditionally enables legacy mode for all Redis 4+ clients, which may prevent Redis 5 users from benefiting from the native promise API.

src/cache/RedisQueryResultCache.ts (3)

36-39: LGTM!

The isLegacyMode property correctly tracks whether the Redis client requires callback-based API when legacy mode is enabled for Redis 4.x compatibility.


318-323: LGTM! This is the core fix.

Detecting Redis v3 by checking for the absence of the connect() method is correct and addresses the root cause reported in issue #11635, where Redis 3 clients were incorrectly detected as v5 when set.length returned 0.


338-349: LGTM! Method rename improves clarity.

The rename from isRedis5OrHigher() to shouldUsePromiseApi() better reflects the actual behavior, which considers both version and legacy mode. The implementation correctly prioritizes legacy mode over version detection.

@ibrahimmenem ibrahimmenem mentioned this pull request Nov 13, 2025
18 tasks
@pkuczynski pkuczynski changed the title fix: fix redis version detection logic fix(redis): version detection logic Nov 13, 2025
@pkuczynski pkuczynski mentioned this pull request Nov 13, 2025
4 tasks
@pkuczynski
Copy link
Collaborator

Have you seen #11767? Can you elaborate why do you think your solution is better then what's presented in #11767?

@ibrahimmenem
Copy link
Contributor Author

ibrahimmenem commented Nov 14, 2025

@pkuczynski no I didn't see that! my approach is a continuation of the first approach, but probably the one in #11767 is more robust. the only possible downside is This operation only supports on node platform. but for me anyone works. thanks

@mguida22
Copy link
Collaborator

@ibrahimmenem thank you for your work on this.

I think we are going to go with the approach in #11767 since it sets us up to better handle different redis versions going forward, but let's leave this open until we have a working fix merged in.

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.

Bug in detectRedisVersion getFromCache method throws error

3 participants