-
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
feat(11528): add Redis 5.x support with backward compatibility wite peer dependency to allow #11585
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
Conversation
- Add version detection for Redis client to handle API differences - Support Redis 5 Promise-based API while maintaining backward compatibility - Update methods to use appropriate API based on Redis version - Add tests for Redis 5 compatibility Redis 5 introduced Promise-based API as default, replacing the callback-based API. This change detects the Redis version and uses the appropriate API calls to ensure compatibility with Redis 3, 4, and 5. Closes typeorm#11528
Implement automatic version detection for Redis client libraries to support Redis 3, 4, and 5 seamlessly. The implementation uses runtime API testing to determine the appropriate Redis client behavior without breaking existing functionality. Changes include: - Dynamic Redis version detection based on client API characteristics - Promise-based API support for Redis 5.x - Backward compatibility with Redis 3.x and 4.x callback-based APIs - Safe fallback mechanism defaulting to Redis 3 behavior - Updated peer dependency to include Redis 5.x versions The cache implementation now automatically adapts to the installed Redis version, ensuring optimal performance and compatibility across all supported Redis client versions while maintaining full backward compatibility.
WalkthroughThe changes update Redis dependency versions in Changes
Sequence Diagram(s)sequenceDiagram
participant App
participant RedisQueryResultCache
participant RedisClient
App->>RedisQueryResultCache: connect()
RedisQueryResultCache->>RedisClient: create client (no legacyMode)
RedisQueryResultCache->>RedisClient: check for connect method
alt Redis 4+
RedisQueryResultCache->>RedisClient: recreate client (legacyMode: true)
end
RedisQueryResultCache->>RedisClient: connect()
RedisQueryResultCache->>RedisQueryResultCache: detectRedisVersion()
App->>RedisQueryResultCache: getFromCache()/storeInCache()/clear()/deleteKey()
alt Redis 5+
RedisQueryResultCache->>RedisClient: use Promise-based API
else Redis 3/4
RedisQueryResultCache->>RedisClient: use callback-based API
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~35 minutes Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (1)📓 Common learnings
🔇 Additional comments (9)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
package.json (1)
179-179
: Consider using a more flexible version range for Redis 5.xThe peer dependency specifies
^5.0.14
which requires at least version 5.0.14. Consider using^5.0.0
instead to allow any Redis 5.x version, unless there's a specific reason why versions 5.0.0-5.0.13 are incompatible.- "redis": "^3.1.1 || ^4.0.0 || ^5.0.14", + "redis": "^3.1.1 || ^4.0.0 || ^5.0.0",src/cache/RedisQueryResultCache.ts (2)
313-313
: Enhance test key uniqueness to prevent collisionsThe test key uses only timestamp which could collide if multiple cache instances are initialized simultaneously.
- const testKey = `__typeorm_redis_version_test_${Date.now()}` + const testKey = `__typeorm_redis_version_test_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
137-343
: Consider refactoring version-specific logic into separate strategiesWhile the current implementation works, having version checks in every method makes the code harder to maintain. Consider refactoring into a strategy pattern where version-specific implementations are encapsulated.
For future improvements, consider:
- Creating separate adapter classes for Redis 3/4 and Redis 5+ that implement a common interface
- Using factory pattern to instantiate the appropriate adapter based on detected version
- This would eliminate the repeated
if (this.isRedis5OrHigher())
checks throughout the codeExample structure:
interface RedisAdapter { get(key: string): Promise<string | null> set(key: string, value: string, duration: number): Promise<void> // ... other methods } class Redis3Adapter implements RedisAdapter { /* ... */ } class Redis5Adapter implements RedisAdapter { /* ... */ }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
package.json
(2 hunks)src/cache/RedisQueryResultCache.ts
(8 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: alumni
PR: typeorm/typeorm#11581
File: docs/docs/drivers/mongodb.md:9-13
Timestamp: 2025-07-27T17:29:18.547Z
Learning: TypeORM supports MongoDB driver versions "^5.8.0 || ^6.0.0" according to the peerDependencies in package.json. The v4 driver is NOT supported. Always check package.json for accurate peer dependency information.
Learnt from: alumni
PR: typeorm/typeorm#11581
File: docs/docs/drivers/mongodb.md:9-13
Timestamp: 2025-07-27T17:29:18.547Z
Learning: TypeORM is compatible only with mongodb@^6, not v4 as previously suggested. The package.json file contains the authoritative peer dependency information for the MongoDB driver version compatibility.
📚 Learning: typeorm is compatible only with mongodb@^6, not v4 as previously suggested. the package.json file co...
Learnt from: alumni
PR: typeorm/typeorm#11581
File: docs/docs/drivers/mongodb.md:9-13
Timestamp: 2025-07-27T17:29:18.547Z
Learning: TypeORM is compatible only with mongodb@^6, not v4 as previously suggested. The package.json file contains the authoritative peer dependency information for the MongoDB driver version compatibility.
Applied to files:
package.json
📚 Learning: typeorm supports mongodb driver versions "^5.8.0 || ^6.0.0" according to the peerdependencies in pac...
Learnt from: alumni
PR: typeorm/typeorm#11581
File: docs/docs/drivers/mongodb.md:9-13
Timestamp: 2025-07-27T17:29:18.547Z
Learning: TypeORM supports MongoDB driver versions "^5.8.0 || ^6.0.0" according to the peerDependencies in package.json. The v4 driver is NOT supported. Always check package.json for accurate peer dependency information.
Applied to files:
package.json
📚 Learning: the correct data source type for aurora postgresql in typeorm is `aurora-postgres`, not `aurora-data...
Learnt from: alumni
PR: typeorm/typeorm#11581
File: docs/docs/drivers/postgres.md:23-23
Timestamp: 2025-07-27T20:50:10.364Z
Learning: The correct data source type for Aurora PostgreSQL in TypeORM is `aurora-postgres`, not `aurora-data-api-pg`. The `aurora-data-api-pg` driver was renamed to `aurora-postgres` according to the CHANGELOG.md. This is defined in the DatabaseType union type and AuroraPostgresConnectionOptions interface.
Applied to files:
package.json
🔇 Additional comments (2)
src/cache/RedisQueryResultCache.ts (2)
340-343
: LGTM!The helper method correctly handles version checking and appropriately returns false for ioredis clients, ensuring they use the callback-based API path.
237-237
: Ensure correct method name for flushing Redis databaseThe Redis 5.x Node.js clients (both
node-redis
andioredis
) expose theflushdb
command in all lowercase. Update the promise-based call to useflushdb()
so it matches the actual API.Locations to update:
- src/cache/RedisQueryResultCache.ts
- Line 237: change
flushDb()
toflushdb()
Diff:
- await this.client.flushDb() + await this.client.flushdb()No change needed for the callback branch at line 243, which already uses
flushdb()
.Likely an incorrect or invalid review comment.
src/cache/RedisQueryResultCache.ts
Outdated
const clientOptions = { | ||
...cacheOptions?.options, | ||
legacyMode: true, | ||
}) | ||
} | ||
|
||
// Create client first | ||
this.client = this.redis.createClient(clientOptions) | ||
|
||
// Check if this is Redis 4.x by looking for connect method before connecting | ||
const isRedis4Plus = typeof this.client.connect === 'function' | ||
|
||
if (isRedis4Plus) { | ||
// For Redis 4.x, we need to recreate the client with legacyMode | ||
// First check if we're dealing with Redis 4 (has connect but uses callbacks) | ||
// or Redis 5 (has connect and uses promises) | ||
this.client = this.redis.createClient({ | ||
...clientOptions, | ||
legacyMode: true | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize client creation to avoid recreating for Redis 4+
The current implementation creates the Redis client twice when dealing with Redis 4+. Additionally, the code unconditionally enables legacyMode
for all Redis 4+ clients, including Redis 5, which may not require it based on the version detection logic that follows.
- const clientOptions = {
- ...cacheOptions?.options,
- }
-
- // Create client first
- this.client = this.redis.createClient(clientOptions)
-
- // Check if this is Redis 4.x by looking for connect method before connecting
- const isRedis4Plus = typeof this.client.connect === 'function'
-
- if (isRedis4Plus) {
- // For Redis 4.x, we need to recreate the client with legacyMode
- // First check if we're dealing with Redis 4 (has connect but uses callbacks)
- // or Redis 5 (has connect and uses promises)
- this.client = this.redis.createClient({
- ...clientOptions,
- legacyMode: true
- })
- }
+ // Detect Redis version by checking the library API
+ const testClient = this.redis.createClient({ ...cacheOptions?.options })
+ const isRedis4Plus = typeof testClient.connect === 'function'
+
+ // Clean up test client if it was created
+ if (testClient && typeof testClient.quit === 'function') {
+ testClient.quit().catch(() => {})
+ }
+
+ const clientOptions = {
+ ...cacheOptions?.options,
+ // Only Redis 4.x needs legacyMode, Redis 5.x doesn't
+ ...(isRedis4Plus ? { legacyMode: true } : {})
+ }
+
+ this.client = this.redis.createClient(clientOptions)
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/cache/RedisQueryResultCache.ts around lines 58 to 76, the Redis client is
created twice unnecessarily for Redis 4+ versions, and legacyMode is enabled
unconditionally for all Redis 4+ clients. Refactor the code to create the client
only once by detecting the Redis version before client creation, and enable
legacyMode only if the client is Redis 4 (not Redis 5), avoiding redundant
client recreation.
Eliminate unnecessary Redis client recreation by using explicit tempClient variable management, reducing potential client instances while maintaining full Redis 3/4/5 compatibility and accurate version detection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🔭 Outside diff range comments (2)
src/cache/RedisQueryResultCache.ts (2)
58-91
: Critical issue: Potential resource leak in version detectionThe current implementation creates a temporary client for version detection but doesn't properly clean it up before creating the main client. This could lead to resource leaks, especially with Redis 4+ clients that maintain connections.
Apply this diff to properly handle the temporary client cleanup:
- // Create initial client to test Redis version - let tempClient = this.redis.createClient(clientOptions) - const isRedis4Plus = typeof tempClient.connect === 'function' - - if (isRedis4Plus) { - // Redis 4+ detected, recreate with legacyMode for Redis 4.x - // (Redis 5 will ignore legacyMode if not needed) - clientOptions.legacyMode = true - tempClient = this.redis.createClient(clientOptions) - } - - // Set as the main client - this.client = tempClient + // Detect Redis version without creating unnecessary connections + const isRedis4Plus = typeof this.redis.createClient({}).connect === 'function' + + if (isRedis4Plus) { + // Redis 4+ detected, use legacyMode for compatibility + clientOptions.legacyMode = true + } + + // Create the main client once with correct options + this.client = this.redis.createClient(clientOptions)
233-245
: Inconsistent Redis command casing with Redis v4 support
Redis 3’s callback API usesflushdb()
, but Redis 4+ (v4 and v5) expose the promise‐basedflushDb()
. YourisRedis5OrHigher()
guard only applies to v5, so when consumers install Redis 4 (which you list in peerDependencies), the fallback toflushdb()
will throw “flushdb is not a function.” Please update to cover v4 as well.• File:
src/cache/RedisQueryResultCache.ts
Lines 233–245
- Either change the check to “Redis 4+” and call
await this.client.flushDb()
for both v4/v5- Or detect method availability at runtime:
if (typeof this.client.flushDb === 'function') { await this.client.flushDb(); } else { return new Promise<void>((ok, fail) => this.client.flushdb((err, _) => (err ? fail(err) : ok())) ); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.json
is excluded by!**/package-lock.json
📒 Files selected for processing (1)
src/cache/RedisQueryResultCache.ts
(8 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: alumni
PR: typeorm/typeorm#11581
File: docs/docs/drivers/mongodb.md:9-13
Timestamp: 2025-07-27T17:29:18.547Z
Learning: TypeORM supports MongoDB driver versions "^5.8.0 || ^6.0.0" according to the peerDependencies in package.json. The v4 driver is NOT supported. Always check package.json for accurate peer dependency information.
Learnt from: alumni
PR: typeorm/typeorm#11581
File: docs/docs/drivers/mongodb.md:9-13
Timestamp: 2025-07-27T17:29:18.547Z
Learning: TypeORM is compatible only with mongodb@^6, not v4 as previously suggested. The package.json file contains the authoritative peer dependency information for the MongoDB driver version compatibility.
🔇 Additional comments (6)
src/cache/RedisQueryResultCache.ts (6)
31-34
: LGTM!The new property declaration is well-documented and follows TypeScript conventions. The optional typing properly handles the undefined state during initialization.
135-149
: LGTM! Proper version-specific disconnect handlingThe implementation correctly handles the different quit behaviors between Redis versions. Redis 5+ returns a Promise while older versions use callbacks.
166-182
: LGTM! Clean version branching for cache retrievalThe method properly handles both Promise-based (Redis 5+) and callback-based (Redis 3/4) APIs. The key selection logic is correctly consolidated.
269-283
: LGTM! Consistent delete operation handlingThe method properly handles version differences for the delete operation using the same branching pattern as other methods.
338-341
: LGTM! Clean helper method implementationThe helper method provides a clear abstraction for version checking with proper type safety and client type validation.
200-226
: ✅ Confirmed:client.set(key, value, { PX })
is valid in the promise-based Redis clientThe Node Redis v4 client (which exposes the modern, promise-based API) supports calling:
await client.set('myKey', 'myValue', { PX: 5000 });to set a key with a millisecond TTL. No changes needed here.
Replace test key creation method with client method signature analysis to prevent potential cache pollution and improve performance.
There was a problem hiding this 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 (1)
src/cache/RedisQueryResultCache.ts (1)
58-74
: Significant improvement over previous version detection approach.The current implementation addresses the previous concerns about creating multiple clients unnecessarily. However, there's still room for optimization by avoiding the temporary client creation entirely.
Consider detecting Redis 4+ capabilities before any client creation:
- // Create initial client to test Redis version - let tempClient = this.redis.createClient(clientOptions) - const isRedis4Plus = typeof tempClient.connect === 'function' - - if (isRedis4Plus) { - // Redis 4+ detected, recreate with legacyMode for Redis 4.x - // (Redis 5 will ignore legacyMode if not needed) - clientOptions.legacyMode = true - tempClient = this.redis.createClient(clientOptions) - } - - // Set as the main client - this.client = tempClient + // Detect Redis 4+ by checking if createClient accepts legacyMode option + const testClientFactory = this.redis.createClient + const isRedis4Plus = testClientFactory.toString().includes('legacyMode') || + this.redis.createClient({}).connect !== undefined + + if (isRedis4Plus) { + clientOptions.legacyMode = true + } + + this.client = this.redis.createClient(clientOptions)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/cache/RedisQueryResultCache.ts
(8 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: alumni
PR: typeorm/typeorm#11581
File: docs/docs/drivers/mongodb.md:9-13
Timestamp: 2025-07-27T17:29:18.547Z
Learning: TypeORM supports MongoDB driver versions "^5.8.0 || ^6.0.0" according to the peerDependencies in package.json. The v4 driver is NOT supported. Always check package.json for accurate peer dependency information.
Learnt from: alumni
PR: typeorm/typeorm#11581
File: docs/docs/drivers/mongodb.md:9-13
Timestamp: 2025-07-27T17:29:18.547Z
Learning: TypeORM is compatible only with mongodb@^6, not v4 as previously suggested. The package.json file contains the authoritative peer dependency information for the MongoDB driver version compatibility.
🧬 Code Graph Analysis (1)
src/cache/RedisQueryResultCache.ts (2)
src/cache/QueryResultCacheOptions.ts (1)
QueryResultCacheOptions
(4-30)src/find-options/FindOperator.ts (1)
value
(105-110)
🔇 Additional comments (10)
src/cache/RedisQueryResultCache.ts (10)
31-34
: LGTM! Clean property addition for version tracking.The new
redisMajorVersion
property properly tracks the Redis version for branching logic throughout the class.
85-91
: Good separation of connection and version detection logic.The approach to connect first (for Redis 4+) then detect the precise version is sound and follows the expected Redis client lifecycle.
135-150
: Excellent version-aware disconnect implementation.The Promise-based approach for Redis 5+ and callback wrapper for older versions is implemented correctly with proper cleanup.
166-183
: Clean cache retrieval with proper version branching.The simplified key selection logic and version-specific API usage is well implemented. The code correctly handles both Promise and callback patterns.
200-227
: Well-structured cache storage with correct Redis 5+ options syntax.The implementation properly uses the
PX
option object syntax for Redis 5+ and the positional parameter approach for older versions.
233-246
: Correct implementation of version-aware cache clearing.Both Redis 5+ Promise-based
flushDb()
and Redis 3/4 callback-basedflushdb()
are handled appropriately.
269-283
: Proper key deletion with version compatibility.The
deleteKey
method correctly implements both Promise and callback patterns for different Redis versions.
306-324
: Excellent improvement! Safe version detection without database pollution.This implementation successfully addresses the critical concern from previous reviews about creating test keys in the Redis database. The method signature inspection approach is much safer and more reliable.
The detection logic using
setMethod.length <= 3
is clever and avoids any side effects on the Redis instance.
329-332
: Clean helper method for version checking.The boolean helper method provides a clear and consistent way to determine Redis version throughout the class.
306-332
: Verify Redis clientset
method parameter count covers all supported versionsThe current detection logic assumes Redis 5+’s
set
method has ≤ 3 parameters, while Redis 3/4 has more. Since ourpeerDependencies
span Redis 3.x, 4.x, and 5.x—and we didn’t find a bundledredis
package locally—you’ll need to confirm that:
- For Redis 3.x:
client.set.length
is > 3- For Redis 4.x: what value does
client.set.length
return?- For Redis 5.x+:
client.set.length
is ≤ 3If any overlap exists (e.g., v4 returning ≤ 3), this detection could misclassify versions. Consider either:
- Adding runtime tests against each client version to assert
set.length
, or- Switching to an explicit version-check (e.g.,
await client.sendCommand(["INFO"])
and parsingredis_version
), which avoids relying on method arity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks great @par333k
Thanks for your amazing help 👏
commit: |
Thanks for the contribution @par333k :) Would be great if you could fix the formatting for that file :) |
Description of change
This pull request adds Redis 5.x support to TypeORM's cache implementation while maintaining full backward compatibility with Redis 3.x and 4.x.
What the change is intended to do:
Why this change is needed:
How it's been verified:
Implementation approach:
Pull-Request Checklist
master
branchFeat #11528
Summary by CodeRabbit
Summary by CodeRabbit
New Features
Chores