Skip to content

fix(valkey/redis) fix tls (includes pub/sub) - #22981

Merged
cirospaciari merged 33 commits into
mainfrom
ciro/fix-valkey-tls-with-pubsub
Sep 27, 2025
Merged

cirospaciari merged 33 commits into
mainfrom
ciro/fix-valkey-tls-with-pubsub

Conversation

@cirospaciari

@cirospaciari cirospaciari commented Sep 25, 2025 •

Copy link
Copy Markdown
Member

What does this PR do?

Fix tls property not being properly set
Fixes #22186

How did you verify your code works?

Tests + Manually test with upstash using rediss protocol and tls: true options

@robobun

robobun commented Sep 25, 2025 •

Copy link
Copy Markdown
Collaborator
Updated 6:00 PM PT - Sep 26th, 2025

❌ @cirospaciari, your commit 4363e7e has 2 failures in Build #27207 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 22981

That installs a local version of the PR into your bun-22981 executable, so you can run:

bun-22981 --bun

Comment thread src/valkey/js_valkey.zig Outdated
@cirospaciari cirospaciari changed the title wip fix(valkey/redis) fix tls (includes pub/sub) Sep 25, 2025
@cirospaciari
cirospaciari marked this pull request as ready for review September 26, 2025 01:33
@cirospaciari
cirospaciari force-pushed the ciro/fix-valkey-tls-with-pubsub branch from f83d969 to ab22a82 Compare September 26, 2025 01:33
@cirospaciari
cirospaciari force-pushed the ciro/fix-valkey-tls-with-pubsub branch from 213b21a to f5e4cec Compare September 26, 2025 03:51
Base automatically changed from user/marko/pub-sub-2.0 to main September 26, 2025 10:06
@Jarred-Sumner
Jarred-Sumner requested a review from alii as a code owner September 26, 2025 10:06
@coderabbitai

coderabbitai Bot commented Sep 26, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Adds deep-clone support for SSLConfig/TLS, exposes socket error messages, integrates per-instance TLS/socket-context management into Valkey clients (cloning, lifecycle, start visibility), and refactors TLS test assets and test scaffolding to run tests for both TLS and TCP.

Changes

Cohort / File(s) Summary
SSL/TLS config cloning
src/bun.js/api/server/SSLConfig.zig, src/valkey/valkey.zig
Add pub fn clone(this: *const SSLConfig) SSLConfig to deep-copy allocator-backed strings/arrays and pub fn clone(this: *const TLS) TLS to clone .custom SSLConfig.
Valkey client lifecycle & socket context
src/valkey/js_valkey.zig, src/valkey/js_valkey_functions.zig, src/valkey/valkey.zig
Add _socket_ctx: ?*uws.SocketContext to JSValkeyClient; propagate/clone TLS on client clone; adjust cloning/init to use weak this_value, make start public, change send parameter naming, add socket-context deinit helper, and alter connection/handshake/lifecycle sequencing.
Socket error messages
src/deps/uws.zig
Add pub fn message(this: create_bun_socket_error_t) ?[]const u8 to map enum variants to literal error strings.
TLS test scaffolding & helpers
test/js/valkey/test-utils.ts, test/js/valkey/valkey.test.ts, test/js/valkey/valkey.failing-subscriber.ts
Replace flat TLS flags with nested tls object (cert, key, ca as Bun.file) plus tlsPaths; add RedisTestStartMessage; parameterize tests to run for both TLS and TCP and wire TLS options through test startup/resolver.
Docker TLS assets
test/js/valkey/docker-tls/Dockerfile, test/js/valkey/docker-tls/server.crt, test/js/valkey/docker-tls/server.key
Remove --cacert usage from redis-cli invocations in init script; replace PEM certificate and private key files with new contents.

Possibly related PRs

  • Redis PUB/SUB #21728: Overlaps Valkey subscription/duplicate APIs and client cloning/duplicate logic in src/valkey/*.
  • Redis PUB/SUB 2.0 #22568: Modifies Valkey/Redis client connection and lifecycle behavior similar to this PR.

Suggested reviewers

  • Jarred-Sumner
  • markovejnovic
  • dylan-conway

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR adds a new public method message() to the create_bun_socket_error_t enum in src/deps/uws.zig, which is unrelated to fixing the TLS property for Redis and was not requested by the linked issue. Please remove or extract the create_bun_socket_error_t.message addition into a separate PR so that this changeset remains focused solely on fixing the Redis TLS support.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly identifies the primary change—fixing the TLS property in the valkey Redis implementation and including pub/sub support—so it accurately reflects the main purpose of the PR. Although it repeats “fix,” it remains concise and directly tied to the core update without introducing unrelated details.
Linked Issues Check ✅ Passed The changes implement the core requirement from issue #22186 by properly setting and propagating the TLS configuration in the Redis client, updating connection logic and tests to successfully establish TLS connections, thereby directly addressing the linked issue’s objectives.
Description Check ✅ Passed The pull request description follows the repository template by clearly stating what the PR does and how the changes were verified, including both automated tests and manual TLS testing against Upstash, so it fulfills the required sections.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ciro/fix-valkey-tls-with-pubsub

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4678bf4 and 4363e7e.

📒 Files selected for processing (1)
  • src/valkey/js_valkey.zig (17 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Implement debug logs in Zig using const log = bun.Output.scoped(.${SCOPE}, false); and invoking log("...", .{})

Files:

  • src/valkey/js_valkey.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

**/*.zig: Format Zig files with zig-format (bun run zig-format)
In Zig, manage memory carefully with allocators and use defer for cleanup

Files:

  • src/valkey/js_valkey.zig
src/**/js_*.zig

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

src/**/js_*.zig: Implement JavaScript bindings in a Zig file named with a js_ prefix (e.g., js_smtp.zig, js_your_feature.zig)
Handle reference counting correctly with ref()/deref() in JS-facing Zig code
Always implement proper cleanup in deinit() and finalize() for JS-exposed types

Files:

  • src/valkey/js_valkey.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/valkey/js_valkey.zig
🧠 Learnings (8)
📓 Common learnings
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: test/js/valkey/valkey.test.ts:264-271
Timestamp: 2025-09-03T01:30:58.001Z
Learning: For test/js/valkey/valkey.test.ts PUB/SUB tests, avoid arbitrary sleeps and async-forEach. Instead, resolve a Promise from the subscriber callback when the expected number of messages is observed and await it with a bounded timeout (e.g., withTimeout + Promise.withResolvers) to account for Redis server→subscriber propagation.
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:561-564
Timestamp: 2025-09-20T00:58:38.042Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods (like ctx.redis.set(), ctx.redis.unsubscribe(), etc.) - Bun's Redis client implementation differs from Node.js and can throw synchronously even for async methods. The maintainer has explicitly requested to stop looking at this error pattern.
📚 Learning: 2025-09-05T18:45:29.200Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/js_valkey.zig:0-0
Timestamp: 2025-09-05T18:45:29.200Z
Learning: In JSValkeyClient.cloneWithoutConnecting() in src/valkey/js_valkey.zig, the address/username/password fields must be repointed to the duplicated connection_strings buffer to avoid use-after-free when the original client is destroyed. The original client properly frees connection_strings in ValkeyClient.deinit().

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/**/js_*.zig : Handle reference counting correctly with ref()/deref() in JS-facing Zig code

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-19T21:17:04.690Z
Learnt from: cirospaciari
PR: oven-sh/bun#22756
File: src/js/node/_http_server.ts:945-947
Timestamp: 2025-09-19T21:17:04.690Z
Learning: JSNodeHTTPServerSocket in src/bun.js/bindings/NodeHTTP.cpp implements both end() and close() methods. The end() method properly handles socket termination for both HTTP responses and raw CONNECT sockets by setting ended=true, checking buffered data, and flushing remaining data through the response context.

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : In finalize() for objects holding JS references, release them using .deref() before destroy

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to **/*.zig : In Zig, manage memory carefully with allocators and use defer for cleanup

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-15T20:47:57.118Z
Learnt from: cirospaciari
PR: oven-sh/bun#22696
File: src/sql/mysql/js/JSMySQLConnection.zig:306-322
Timestamp: 2025-09-15T20:47:57.118Z
Learning: bun.Async.KeepAlive already maintains internal state to prevent issues from multiple ref/unref calls, so additional idempotence guards are not needed when calling poll_ref.ref() and poll_ref.unref().

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-05T19:48:44.481Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/valkey.zig:0-0
Timestamp: 2025-09-05T19:48:44.481Z
Learning: In ValkeyCommand.Promise, when you need to resolve with already-converted JSValues (not RESPValues), use promise_ptr.promise.resolve() to bypass the RESP-to-JS conversion wrapper. The ValkeyCommand.Promise.resolve() method is for converting RESPValues, while direct .promise access is for pre-converted JSValues.

Applied to files:

  • src/valkey/js_valkey.zig
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (1)
src/valkey/js_valkey.zig (1)

1020-1025: Fix refcount underflow on SocketContext creation failure

When uws.SocketContext.create* returns null we jump into failWithInvalidSocketContext(). On that path we never reach the this.ref() that sits after the switch, yet onValkeyClose() still executes its deferred this.deref(). Combined with the deferred deref in doConnect(), this underflows the refcount and can free the client while it is still on the stack. Please take a ref here so onValkeyClose() consumes it.

 fn failWithInvalidSocketContext(this: *JSValkeyClient) void {
     // if the context is invalid is not worth retrying
     this.client.flags.enable_auto_reconnect = false;
+    this.ref();
     this.clientFail(if (this.client.tls == .none) "Failed to create TCP context" else "Failed to create TLS context", protocol.RedisError.ConnectionClosed);
     this.client.onValkeyClose();
 }

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (2)
src/valkey/js_valkey.zig (2)

1298-1331: Fix TLS hostname verification gating and avoid risky logging.

  • The identity check currently runs only when ssl_error.error_no != 0. That’s backwards: you should fail if verification failed (error_no != 0), and independently enforce hostname verification when rejectUnauthorized is true.
  • Logging reason/code unconditionally risks dereferencing non-sentinel pointers. Gate it behind error_no != 0.

Apply this diff:

-            debug("onHandshake: {d} {d} {s} {s}", .{
-                success,
-                ssl_error.error_no,
-                if (ssl_error.reason != null) bun.span(ssl_error.reason[0..bun.len(ssl_error.reason) :0]) else "no reason",
-                if (ssl_error.code != null) bun.span(ssl_error.code[0..bun.len(ssl_error.code) :0]) else "no code",
-            });
+            if (ssl_error.error_no != 0) {
+                debug("onHandshake failed: success={d} err={d}", .{ success, ssl_error.error_no });
+            } else {
+                debug("onHandshake ok: success={d}", .{ success });
+            }
@@
-            if (handshake_success) {
+            if (handshake_success) {
                 const vm = this.client.vm;
-                if (this.client.tls.rejectUnauthorized(vm)) {
-                    if (ssl_error.error_no != 0) {
-                        // only reject the connection if reject_unauthorized == true
-
-                        const ssl_ptr: *BoringSSL.c.SSL = @ptrCast(this.client.socket.getNativeHandle());
-                        if (BoringSSL.c.SSL_get_servername(ssl_ptr, 0)) |servername| {
-                            const hostname = servername[0..bun.len(servername)];
-                            if (!BoringSSL.checkServerIdentity(ssl_ptr, hostname)) {
-                                this.client.flags.is_authenticated = false;
-                                const loop = vm.eventLoop();
-                                loop.enter();
-                                defer loop.exit();
-                                this.client.status = .failed;
-                                this.client.flags.is_manually_closed = true;
-                                this.client.failWithJSValue(this.globalObject, ssl_error.toJS(this.globalObject));
-                                this.client.close();
-                                return;
-                            }
-                        }
-                    }
-                }
+                if (this.client.tls.rejectUnauthorized(vm)) {
+                    // Fail on verification error
+                    if (ssl_error.error_no != 0) {
+                        this.client.flags.is_authenticated = false;
+                        const loop = vm.eventLoop();
+                        loop.enter();
+                        defer loop.exit();
+                        this.client.status = .failed;
+                        this.client.flags.is_manually_closed = true;
+                        this.client.failWithJSValue(this.globalObject, ssl_error.toJS(this.globalObject));
+                        this.client.close();
+                        return;
+                    }
+                    // Enforce hostname check when available
+                    const ssl_ptr: *BoringSSL.c.SSL = @ptrCast(this.client.socket.getNativeHandle());
+                    if (BoringSSL.c.SSL_get_servername(ssl_ptr, 0)) |servername| {
+                        const hostname = servername[0..bun.len(servername)];
+                        if (!BoringSSL.checkServerIdentity(ssl_ptr, hostname)) {
+                            this.client.flags.is_authenticated = false;
+                            const loop = vm.eventLoop();
+                            loop.enter();
+                            defer loop.exit();
+                            this.client.status = .failed;
+                            this.client.flags.is_manually_closed = true;
+                            this.client.failWithJSValue(this.globalObject, ssl_error.toJS(this.globalObject));
+                            this.client.close();
+                            return;
+                        }
+                    }
+                }
                 this.client.start();
             }

This aligns with typical TLS semantics and avoids reading potentially invalid reason/code buffers.


995-1057: Remove unpaired this.ref() in connect()
The this.ref() at line 1049 in src/valkey/js_valkey.zig isn’t matched by a defer this.deref(), leaking references on repeated reconnects. Apply:

-        this.ref();
-
-        if (own_ctx) {
+        if (own_ctx) {
🧹 Nitpick comments (3)
test/js/valkey/test-utils.ts (1)

83-92: TLS_REDIS_OPTIONS shape update: LGTM.

  • tls holds Bun.file refs (cert/key/ca) as required by SSLConfig.fromJS.
  • tlsPaths mirrors file paths for child processes that need to re-open files.

Consider adding serverName if hostname verification becomes enforced in tests.

Apply after TLS handshake changes land.

src/bun.js/api/server/SSLConfig.zig (1)

230-314: Deep clone looks correct; minor cleanup possible.

  • Correctly deep-copies strings and key/cert/ca arrays; avoids double-free with is_using_default_ciphers.
  • Minor nit: you set .protos in the initializer and also dupe it later; initializing .protos to null in the struct literal would avoid redundant assignment.
-    var cloned: SSLConfig = .{
+    var cloned: SSLConfig = .{
         .secure_options = this.secure_options,
         .request_cert = this.request_cert,
         .reject_unauthorized = this.reject_unauthorized,
         .ssl_ciphers = this.ssl_ciphers,
-        .protos = this.protos,
+        .protos = null,
         .protos_len = this.protos_len,
         .client_renegotiation_limit = this.client_renegotiation_limit,
         .client_renegotiation_window = this.client_renegotiation_window,
         .requires_custom_request_ctx = this.requires_custom_request_ctx,
         .is_using_default_ciphers = this.is_using_default_ciphers,
         .low_memory_mode = this.low_memory_mode,
     };
src/deps/uws.zig (1)

84-94: De-duplicate error strings via message()

Now that message() exists, we can route toJS() through it. That keeps the human-readable text in one place and guarantees future edits stay in sync.

-    pub fn toJS(this: create_bun_socket_error_t, globalObject: *jsc.JSGlobalObject) jsc.JSValue {
-        return switch (this) {
-            .none => brk: {
-                bun.debugAssert(false);
-                break :brk .null;
-            },
-            .load_ca_file => globalObject.ERR(.BORINGSSL, "Failed to load CA file", .{}).toJS(),
-            .invalid_ca_file => globalObject.ERR(.BORINGSSL, "Invalid CA file", .{}).toJS(),
-            .invalid_ca => globalObject.ERR(.BORINGSSL, "Invalid CA", .{}).toJS(),
-            .invalid_ciphers => globalObject.ERR(.BORINGSSL, "Invalid ciphers", .{}).toJS(),
-        };
-    }
+    pub fn toJS(this: create_bun_socket_error_t, globalObject: *jsc.JSGlobalObject) jsc.JSValue {
+        const msg = this.message();
+        if (msg == null) {
+            bun.debugAssert(false);
+            return .null;
+        }
+        return globalObject.ERR(.BORINGSSL, msg.?, .{}).toJS();
+    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a329da9 and c5465a4.

📒 Files selected for processing (11)
  • src/bun.js/api/server/SSLConfig.zig (1 hunks)
  • src/deps/uws.zig (1 hunks)
  • src/valkey/js_valkey.zig (9 hunks)
  • src/valkey/valkey.zig (3 hunks)
  • test/js/valkey/docker-tls/Dockerfile (1 hunks)
  • test/js/valkey/docker-tls/server.crt (1 hunks)
  • test/js/valkey/docker-tls/server.key (1 hunks)
  • test/js/valkey/memleak/valkey-leaks.ts (1 hunks)
  • test/js/valkey/test-utils.ts (1 hunks)
  • test/js/valkey/valkey.failing-subscriber.ts (3 hunks)
  • test/js/valkey/valkey.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (12)
test/**

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place all tests under the test/ directory

Files:

  • test/js/valkey/docker-tls/server.crt
  • test/js/valkey/docker-tls/server.key
  • test/js/valkey/valkey.test.ts
  • test/js/valkey/memleak/valkey-leaks.ts
  • test/js/valkey/docker-tls/Dockerfile
  • test/js/valkey/test-utils.ts
  • test/js/valkey/valkey.failing-subscriber.ts
test/js/**

📄 CodeRabbit inference engine (test/CLAUDE.md)

Organize unit tests for specific features under test/js/ by module

Files:

  • test/js/valkey/docker-tls/server.crt
  • test/js/valkey/docker-tls/server.key
  • test/js/valkey/valkey.test.ts
  • test/js/valkey/memleak/valkey-leaks.ts
  • test/js/valkey/docker-tls/Dockerfile
  • test/js/valkey/test-utils.ts
  • test/js/valkey/valkey.failing-subscriber.ts
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Implement debug logs in Zig using const log = bun.Output.scoped(.${SCOPE}, false); and invoking log("...", .{})

Files:

  • src/deps/uws.zig
  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/js_valkey.zig
  • src/valkey/valkey.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

**/*.zig: Format Zig files with zig-format (bun run zig-format)
In Zig, manage memory carefully with allocators and use defer for cleanup

Files:

  • src/deps/uws.zig
  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/js_valkey.zig
  • src/valkey/valkey.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/js_valkey.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/server/SSLConfig.zig
test/js/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place JavaScript and TypeScript tests under test/js/

Files:

  • test/js/valkey/valkey.test.ts
  • test/js/valkey/memleak/valkey-leaks.ts
  • test/js/valkey/test-utils.ts
  • test/js/valkey/valkey.failing-subscriber.ts
test/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable

Files:

  • test/js/valkey/valkey.test.ts
  • test/js/valkey/memleak/valkey-leaks.ts
  • test/js/valkey/test-utils.ts
  • test/js/valkey/valkey.failing-subscriber.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (test/CLAUDE.md)

test/**/*.test.ts: Name test files *.test.ts and use bun:test
Do not write flaky tests: never wait for arbitrary time; wait for conditions instead
Never hardcode port numbers in tests; use port: 0 to get a random port
When spawning Bun in tests, use bunExe() and bunEnv from harness
Prefer async/await in tests; for a single callback, use Promise.withResolvers()
Do not set explicit test timeouts; rely on Bun’s built-in timeouts
Use tempDir/tempDirWithFiles from harness for temporary files and directories in tests
For large/repetitive strings in tests, prefer Buffer.alloc(count, fill).toString() over "A".repeat(count)
Import common test utilities from harness (e.g., bunExe, bunEnv, tempDirWithFiles, tmpdirSync, platform checks, GC helpers)
In error tests, assert non-zero exit codes for failing processes and use toThrow for synchronous errors
Use describe blocks for grouping, describe.each for parameterized tests, snapshots with toMatchSnapshot, and lifecycle hooks (beforeAll, beforeEach, afterEach); track resources for cleanup in afterEach
Use using/await using with Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests

Files:

  • test/js/valkey/valkey.test.ts
test/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

test/**/*.test.{ts,tsx}: Test files must be placed under test/ and end with .test.ts or .test.tsx
In tests, always use port: 0; do not hardcode ports or use custom random port functions
In tests, use normalizeBunSnapshot when asserting snapshots
Never write tests that merely assert absence of "panic" or "uncaught exception" in output
Avoid shell commands (e.g., find, grep) in tests; use Bun.Glob and built-ins instead
Prefer snapshot tests over exact stdout equality assertions

Files:

  • test/js/valkey/valkey.test.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Format JavaScript/TypeScript files with Prettier (bun run prettier)

Files:

  • test/js/valkey/valkey.test.ts
  • test/js/valkey/memleak/valkey-leaks.ts
  • test/js/valkey/test-utils.ts
  • test/js/valkey/valkey.failing-subscriber.ts
src/**/js_*.zig

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

src/**/js_*.zig: Implement JavaScript bindings in a Zig file named with a js_ prefix (e.g., js_smtp.zig, js_your_feature.zig)
Handle reference counting correctly with ref()/deref() in JS-facing Zig code
Always implement proper cleanup in deinit() and finalize() for JS-exposed types

Files:

  • src/valkey/js_valkey.zig
🧠 Learnings (10)
📓 Common learnings
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:561-564
Timestamp: 2025-09-20T00:58:38.042Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods (like ctx.redis.set(), ctx.redis.unsubscribe(), etc.) - Bun's Redis client implementation differs from Node.js and can throw synchronously even for async methods. The maintainer has explicitly requested to stop looking at this error pattern.
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: test/js/valkey/valkey.test.ts:264-271
Timestamp: 2025-09-03T01:30:58.001Z
Learning: For test/js/valkey/valkey.test.ts PUB/SUB tests, avoid arbitrary sleeps and async-forEach. Instead, resolve a Promise from the subscriber callback when the expected number of messages is observed and await it with a bounded timeout (e.g., withTimeout + Promise.withResolvers) to account for Redis server→subscriber propagation.
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/{**/js_*.zig,bun.js/api/**/*.zig} : Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Applied to files:

  • src/deps/uws.zig
📚 Learning: 2025-09-05T18:45:29.200Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/js_valkey.zig:0-0
Timestamp: 2025-09-05T18:45:29.200Z
Learning: In JSValkeyClient.cloneWithoutConnecting() in src/valkey/js_valkey.zig, the address/username/password fields must be repointed to the duplicated connection_strings buffer to avoid use-after-free when the original client is destroyed. The original client properly frees connection_strings in ValkeyClient.deinit().

Applied to files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/js_valkey.zig
  • src/valkey/valkey.zig
📚 Learning: 2025-09-03T01:30:58.001Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: test/js/valkey/valkey.test.ts:264-271
Timestamp: 2025-09-03T01:30:58.001Z
Learning: For test/js/valkey/valkey.test.ts PUB/SUB tests, avoid arbitrary sleeps and async-forEach. Instead, resolve a Promise from the subscriber callback when the expected number of messages is observed and await it with a bounded timeout (e.g., withTimeout + Promise.withResolvers) to account for Redis server→subscriber propagation.

Applied to files:

  • test/js/valkey/valkey.test.ts
  • test/js/valkey/memleak/valkey-leaks.ts
  • test/js/valkey/test-utils.ts
  • test/js/valkey/valkey.failing-subscriber.ts
📚 Learning: 2025-09-20T00:58:38.042Z
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:561-564
Timestamp: 2025-09-20T00:58:38.042Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods (like ctx.redis.set(), ctx.redis.unsubscribe(), etc.) - Bun's Redis client implementation differs from Node.js and can throw synchronously even for async methods. The maintainer has explicitly requested to stop looking at this error pattern.

Applied to files:

  • test/js/valkey/valkey.test.ts
  • test/js/valkey/memleak/valkey-leaks.ts
  • test/js/valkey/test-utils.ts
  • test/js/valkey/valkey.failing-subscriber.ts
📚 Learning: 2025-09-20T00:57:56.685Z
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:268-276
Timestamp: 2025-09-20T00:57:56.685Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods like ctx.redis.set() - the maintainer has explicitly requested to stop looking at this error pattern.

Applied to files:

  • test/js/valkey/valkey.test.ts
  • test/js/valkey/memleak/valkey-leaks.ts
  • test/js/valkey/test-utils.ts
  • test/js/valkey/valkey.failing-subscriber.ts
📚 Learning: 2025-09-17T23:42:05.812Z
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: docs/api/redis.md:240-246
Timestamp: 2025-09-17T23:42:05.812Z
Learning: The RedisClient.duplicate() method in Bun's Redis implementation returns Promise<RedisClient>, not RedisClient synchronously, so await is required when calling it.

Applied to files:

  • test/js/valkey/valkey.test.ts
  • test/js/valkey/valkey.failing-subscriber.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Use `using`/`await using` with Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests

Applied to files:

  • test/js/valkey/memleak/valkey-leaks.ts
📚 Learning: 2025-09-15T20:47:57.118Z
Learnt from: cirospaciari
PR: oven-sh/bun#22696
File: src/sql/mysql/js/JSMySQLConnection.zig:306-322
Timestamp: 2025-09-15T20:47:57.118Z
Learning: bun.Async.KeepAlive already maintains internal state to prevent issues from multiple ref/unref calls, so additional idempotence guards are not needed when calling poll_ref.ref() and poll_ref.unref().

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-05T20:20:04.858Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/js_valkey.zig:163-178
Timestamp: 2025-09-05T20:20:04.858Z
Learning: Node-redis PUB/SUB callback error handling is broken - when a callback throws an error, it first emits the error as "Redis Client Error", then enters an undefined state with cascading internal errors like "undefined is not an object (evaluating 'this.#waitingForReply.head.value')", making the client unusable for further processing.

Applied to files:

  • test/js/valkey/valkey.failing-subscriber.ts
🧬 Code graph analysis (3)
test/js/valkey/valkey.test.ts (1)
test/js/valkey/test-utils.ts (9)
  • isEnabled (10-27)
  • setupDockerContainer (203-214)
  • createClient (229-288)
  • expectType (495-500)
  • TLS_REDIS_URL (123-123)
  • DEFAULT_REDIS_URL (122-122)
  • TLS_REDIS_OPTIONS (80-93)
  • awaitableCounter (661-696)
  • randomCoinFlip (654-656)
test/js/valkey/memleak/valkey-leaks.ts (2)
test/harness.ts (1)
  • gc (116-118)
test/js/valkey/test-utils.ts (2)
  • isEnabled (10-27)
  • createClient (229-288)
test/js/valkey/valkey.failing-subscriber.ts (1)
packages/bun-types/redis.d.ts (1)
  • RedisClient (61-815)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (15)
test/js/valkey/valkey.failing-subscriber.ts (5)

23-24: Resolving the full message object is correct.

Switching to resolve(msg) lets the child receive both url and tlsPaths. Matches the updated test harness.


48-49: Off-by-one in the intentional-throw trigger?

With if ((counter++) === 1), the throw happens on the second message, not the first. Confirm this is intended for the failing-subscriber scenario.


6-6: Import from "bun": OK.

Using RedisClient from bun matches current API.


56-58: Uncaught exception forwarding: LGTM.

Propagates errors back to the parent via IPC.


32-41: Approve TLS construction; fixture files verified. Server.crt and server.key are present and readable in test/js/valkey/docker-unified.

src/valkey/valkey.zig (4)

90-96: TLS.clone for union: LGTM.

Cloning custom SSLConfig avoids shared ownership and use-after-free.


467-467: Early socket detach on close: LGTM.

Prevents accidental writes after close during teardown paths.


956-961: Gate start() on TCP only: LGTM.

Matches the TLS flow where start() is invoked post-handshake.


964-967: start() made public: LGTM.

Needed by TLS handshake callback.

src/valkey/js_valkey.zig (4)

230-231: Custom socket context retention: OK; consider lifecycle note.

Holding _socket_ctx for custom TLS avoids re-creating contexts. Ensure it’s deinit’d when the client is finalized (you added deinitSocketContextNextTick) and not reused across distinct client instances inadvertently.

Verify that duplicate() does not copy _socket_ctx across instances.


366-366: TLS selection logic: LGTM.

Respects options.tls when provided; otherwise infers from URL.


414-415: Cloning TLS on duplicate(): LGTM.

Deep copy via valkey.TLS.clone prevents shared SSLConfig state.


1101-1124: Deferred deinit of custom TLS ctx: LGTM.

Enqueuing the deinit avoids JS reentry on close paths.

test/js/valkey/docker-tls/server.crt (1)

2-32: Confirm certificate/key pairing
Automated modulus check failed (openssl unavailable). Manually verify that test/js/valkey/docker-tls/server.crt and server.key share the same modulus and subject parameters to avoid TLS handshake failures in the Docker fixtures.

test/js/valkey/docker-tls/server.key (1)

1-52: Manually confirm the new key/cert pair is consistent.
Run openssl x509 -noout -modulus -in test/js/valkey/docker-tls/server.crt and openssl pkey -noout -modulus -in test/js/valkey/docker-tls/server.key locally to ensure both modulus values match.

Comment thread test/js/valkey/docker-tls/Dockerfile
Comment thread test/js/valkey/memleak/valkey-leaks.ts Outdated
Comment thread test/js/valkey/valkey.test.ts
Comment thread test/js/valkey/valkey.test.ts
Comment thread test/js/valkey/valkey.test.ts
@cirospaciari
cirospaciari marked this pull request as draft September 26, 2025 18:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
src/valkey/js_valkey.zig (1)

1006-1049: Remove the direct onValkeyClose() calls on context-creation failure

this.clientFail(...) already drives the close path (it closes the socket and ends up invoking JSValkeyClient.onValkeyClose() for us). Calling this.client.onValkeyClose() here a second time—without first taking a ref—hits JSValkeyClient.onValkeyClose()’s unconditional defer this.deref(); (Line 909) with no matching ref(), so the refcount underflows and we free the client while it is still on the stack. This is consistent with the ASAN/segfault explosions CI is reporting for this PR. Drop these extra onValkeyClose() calls (in all three branches) and let the failure path you just triggered handle the cleanup.

-            this.client.onValkeyClose();

(also remove the identical line in the TLS and custom branches)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c5465a4 and 23f1e97.

📒 Files selected for processing (2)
  • src/bun.js/api/server/SSLConfig.zig (1 hunks)
  • src/valkey/js_valkey.zig (10 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Implement debug logs in Zig using const log = bun.Output.scoped(.${SCOPE}, false); and invoking log("...", .{})

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/js_valkey.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

**/*.zig: Format Zig files with zig-format (bun run zig-format)
In Zig, manage memory carefully with allocators and use defer for cleanup

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/js_valkey.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/js_valkey.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/server/SSLConfig.zig
src/**/js_*.zig

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

src/**/js_*.zig: Implement JavaScript bindings in a Zig file named with a js_ prefix (e.g., js_smtp.zig, js_your_feature.zig)
Handle reference counting correctly with ref()/deref() in JS-facing Zig code
Always implement proper cleanup in deinit() and finalize() for JS-exposed types

Files:

  • src/valkey/js_valkey.zig
🧠 Learnings (3)
📓 Common learnings
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:561-564
Timestamp: 2025-09-20T00:58:38.042Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods (like ctx.redis.set(), ctx.redis.unsubscribe(), etc.) - Bun's Redis client implementation differs from Node.js and can throw synchronously even for async methods. The maintainer has explicitly requested to stop looking at this error pattern.
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: test/js/valkey/valkey.test.ts:264-271
Timestamp: 2025-09-03T01:30:58.001Z
Learning: For test/js/valkey/valkey.test.ts PUB/SUB tests, avoid arbitrary sleeps and async-forEach. Instead, resolve a Promise from the subscriber callback when the expected number of messages is observed and await it with a bounded timeout (e.g., withTimeout + Promise.withResolvers) to account for Redis server→subscriber propagation.
📚 Learning: 2025-09-05T18:45:29.200Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/js_valkey.zig:0-0
Timestamp: 2025-09-05T18:45:29.200Z
Learning: In JSValkeyClient.cloneWithoutConnecting() in src/valkey/js_valkey.zig, the address/username/password fields must be repointed to the duplicated connection_strings buffer to avoid use-after-free when the original client is destroyed. The original client properly frees connection_strings in ValkeyClient.deinit().

Applied to files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-15T20:47:57.118Z
Learnt from: cirospaciari
PR: oven-sh/bun#22696
File: src/sql/mysql/js/JSMySQLConnection.zig:306-322
Timestamp: 2025-09-15T20:47:57.118Z
Learning: bun.Async.KeepAlive already maintains internal state to prevent issues from multiple ref/unref calls, so additional idempotence guards are not needed when calling poll_ref.ref() and poll_ref.unref().

Applied to files:

  • src/valkey/js_valkey.zig
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format

Comment thread src/bun.js/api/server/SSLConfig.zig
@cirospaciari
cirospaciari marked this pull request as ready for review September 26, 2025 22:47

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (4)
test/js/valkey/valkey.test.ts (3)

209-217: Clarify test.each title placeholder.

$url isn’t a provided param; use a positional placeholder.

Apply this diff:

-      test.each([...Array(16).keys()])("Connecting to database with url $url succeeds", async (dbId: number) => {
+      test.each([...Array(16).keys()])("Connecting to database %s succeeds", async (dbId: number) => {

534-536: Align comment with assertion (it should fail).

Comment contradicts the test name and expectation.

Apply this diff:

-        // Publishing from the same client should work
+        // Publishing from the same client should fail while in subscriber mode

671-673: Remove stray console.log from tests.

Avoid noisy output in CI.

Apply this diff:

-        console.log("Unsubscribing listener2");
src/bun.js/api/server/SSLConfig.zig (1)

275-289: Avoid zero-length allocations for cert/key/ca clones.

If count is 0, we still alloc a 0-length array, keep a non-null pointer, and later free it. It’s safe but unnecessary. Prefer leaving pointer null when count is 0 to simplify lifecycle and reduce allocations.

Apply this diff:

 inline for (array_fields_cloned_by_memcopy) |field| {
     if (@field(this, field)) |array| {
-        const cloned_array = bun.handleOom(bun.default_allocator.alloc([*c]const u8, @field(this, field ++ "_count")));
-        @field(cloned, field) = cloned_array;
-        @field(cloned, field ++ "_count") = @field(this, field ++ "_count");
+        const count = @field(this, field ++ "_count");
+        if (count == 0) {
+            @field(cloned, field) = null;
+            @field(cloned, field ++ "_count") = 0;
+            continue;
+        }
+        const cloned_array = bun.handleOom(bun.default_allocator.alloc([*c]const u8, count));
+        @field(cloned, field) = cloned_array;
+        @field(cloned, field ++ "_count") = count;
         for (0..@field(this, field ++ "_count")) |i| {
             const slice = std.mem.span(array[i]);
             if (slice.len > 0) {
                 cloned_array[i] = bun.handleOom(bun.default_allocator.dupeZ(u8, slice));
             } else {
                 cloned_array[i] = "";
             }
         }
     }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2f137cb and 3fd3840.

📒 Files selected for processing (7)
  • src/bun.js/api/server/SSLConfig.zig (1 hunks)
  • src/valkey/js_valkey.zig (17 hunks)
  • src/valkey/js_valkey_functions.zig (1 hunks)
  • src/valkey/valkey.zig (5 hunks)
  • test/js/valkey/test-utils.ts (1 hunks)
  • test/js/valkey/valkey.failing-subscriber.ts (4 hunks)
  • test/js/valkey/valkey.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/js/valkey/test-utils.ts
  • test/js/valkey/valkey.failing-subscriber.ts
  • src/valkey/js_valkey_functions.zig
🧰 Additional context used
📓 Path-based instructions (12)
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Implement debug logs in Zig using const log = bun.Output.scoped(.${SCOPE}, false); and invoking log("...", .{})

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/valkey.zig
  • src/valkey/js_valkey.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

**/*.zig: Format Zig files with zig-format (bun run zig-format)
In Zig, manage memory carefully with allocators and use defer for cleanup

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/valkey.zig
  • src/valkey/js_valkey.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/js_valkey.zig
src/bun.js/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/zig-javascriptcore-classes.mdc)

src/bun.js/**/*.zig: In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS and re-export toJS/fromJS/fromJSDirect
Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Implement getters as get(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Provide deinit() for resource cleanup and finalize() that calls deinit(); use bun.destroy(this) or appropriate destroy pattern
Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
For properties marked cache: true, use the generated Zig accessors (NameSetCached/GetCached) to work with GC-owned values
In finalize() for objects holding JS references, release them using .deref() before destroy

Files:

  • src/bun.js/api/server/SSLConfig.zig
test/**

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place all tests under the test/ directory

Files:

  • test/js/valkey/valkey.test.ts
test/js/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

Place JavaScript and TypeScript tests under test/js/

Files:

  • test/js/valkey/valkey.test.ts
test/**/*.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)

test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable

Files:

  • test/js/valkey/valkey.test.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (test/CLAUDE.md)

test/**/*.test.ts: Name test files *.test.ts and use bun:test
Do not write flaky tests: never wait for arbitrary time; wait for conditions instead
Never hardcode port numbers in tests; use port: 0 to get a random port
When spawning Bun in tests, use bunExe() and bunEnv from harness
Prefer async/await in tests; for a single callback, use Promise.withResolvers()
Do not set explicit test timeouts; rely on Bun’s built-in timeouts
Use tempDir/tempDirWithFiles from harness for temporary files and directories in tests
For large/repetitive strings in tests, prefer Buffer.alloc(count, fill).toString() over "A".repeat(count)
Import common test utilities from harness (e.g., bunExe, bunEnv, tempDirWithFiles, tmpdirSync, platform checks, GC helpers)
In error tests, assert non-zero exit codes for failing processes and use toThrow for synchronous errors
Use describe blocks for grouping, describe.each for parameterized tests, snapshots with toMatchSnapshot, and lifecycle hooks (beforeAll, beforeEach, afterEach); track resources for cleanup in afterEach
Use using/await using with Bun resources (e.g., Bun.listen/connect/spawn/serve) to ensure cleanup in tests

Files:

  • test/js/valkey/valkey.test.ts
test/js/**

📄 CodeRabbit inference engine (test/CLAUDE.md)

Organize unit tests for specific features under test/js/ by module

Files:

  • test/js/valkey/valkey.test.ts
test/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

test/**/*.test.{ts,tsx}: Test files must be placed under test/ and end with .test.ts or .test.tsx
In tests, always use port: 0; do not hardcode ports or use custom random port functions
In tests, use normalizeBunSnapshot when asserting snapshots
Never write tests that merely assert absence of "panic" or "uncaught exception" in output
Avoid shell commands (e.g., find, grep) in tests; use Bun.Glob and built-ins instead
Prefer snapshot tests over exact stdout equality assertions

Files:

  • test/js/valkey/valkey.test.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Format JavaScript/TypeScript files with Prettier (bun run prettier)

Files:

  • test/js/valkey/valkey.test.ts
src/**/js_*.zig

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

src/**/js_*.zig: Implement JavaScript bindings in a Zig file named with a js_ prefix (e.g., js_smtp.zig, js_your_feature.zig)
Handle reference counting correctly with ref()/deref() in JS-facing Zig code
Always implement proper cleanup in deinit() and finalize() for JS-exposed types

Files:

  • src/valkey/js_valkey.zig
🧠 Learnings (15)
📓 Common learnings
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:561-564
Timestamp: 2025-09-20T00:58:38.042Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods (like ctx.redis.set(), ctx.redis.unsubscribe(), etc.) - Bun's Redis client implementation differs from Node.js and can throw synchronously even for async methods. The maintainer has explicitly requested to stop looking at this error pattern.
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: test/js/valkey/valkey.test.ts:264-271
Timestamp: 2025-09-03T01:30:58.001Z
Learning: For test/js/valkey/valkey.test.ts PUB/SUB tests, avoid arbitrary sleeps and async-forEach. Instead, resolve a Promise from the subscriber callback when the expected number of messages is observed and await it with a bounded timeout (e.g., withTimeout + Promise.withResolvers) to account for Redis server→subscriber propagation.
📚 Learning: 2025-09-05T18:45:29.200Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/js_valkey.zig:0-0
Timestamp: 2025-09-05T18:45:29.200Z
Learning: In JSValkeyClient.cloneWithoutConnecting() in src/valkey/js_valkey.zig, the address/username/password fields must be repointed to the duplicated connection_strings buffer to avoid use-after-free when the original client is destroyed. The original client properly frees connection_strings in ValkeyClient.deinit().

Applied to files:

  • src/bun.js/api/server/SSLConfig.zig
  • src/valkey/valkey.zig
  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-03T01:30:58.001Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: test/js/valkey/valkey.test.ts:264-271
Timestamp: 2025-09-03T01:30:58.001Z
Learning: For test/js/valkey/valkey.test.ts PUB/SUB tests, avoid arbitrary sleeps and async-forEach. Instead, resolve a Promise from the subscriber callback when the expected number of messages is observed and await it with a bounded timeout (e.g., withTimeout + Promise.withResolvers) to account for Redis server→subscriber propagation.

Applied to files:

  • test/js/valkey/valkey.test.ts
📚 Learning: 2025-09-20T00:58:38.042Z
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:561-564
Timestamp: 2025-09-20T00:58:38.042Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods (like ctx.redis.set(), ctx.redis.unsubscribe(), etc.) - Bun's Redis client implementation differs from Node.js and can throw synchronously even for async methods. The maintainer has explicitly requested to stop looking at this error pattern.

Applied to files:

  • test/js/valkey/valkey.test.ts
📚 Learning: 2025-09-20T00:57:56.685Z
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:268-276
Timestamp: 2025-09-20T00:57:56.685Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods like ctx.redis.set() - the maintainer has explicitly requested to stop looking at this error pattern.

Applied to files:

  • test/js/valkey/valkey.test.ts
📚 Learning: 2025-09-03T17:10:13.486Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Prefer `async/await` in tests; for a single callback, use `Promise.withResolvers()`

Applied to files:

  • test/js/valkey/valkey.test.ts
📚 Learning: 2025-08-30T00:09:39.100Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.100Z
Learning: Applies to test/bake/{dev/*.test.ts,dev-and-prod.ts} : Explicitly assert client console logs with c.expectMessage; unasserted logs should not appear

Applied to files:

  • test/js/valkey/valkey.test.ts
📚 Learning: 2025-09-17T23:42:05.812Z
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: docs/api/redis.md:240-246
Timestamp: 2025-09-17T23:42:05.812Z
Learning: The RedisClient.duplicate() method in Bun's Redis implementation returns Promise<RedisClient>, not RedisClient synchronously, so await is required when calling it.

Applied to files:

  • test/js/valkey/valkey.test.ts
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/**/js_*.zig : Handle reference counting correctly with ref()/deref() in JS-facing Zig code

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-19T21:17:04.690Z
Learnt from: cirospaciari
PR: oven-sh/bun#22756
File: src/js/node/_http_server.ts:945-947
Timestamp: 2025-09-19T21:17:04.690Z
Learning: JSNodeHTTPServerSocket in src/bun.js/bindings/NodeHTTP.cpp implements both end() and close() methods. The end() method properly handles socket termination for both HTTP responses and raw CONNECT sockets by setting ended=true, checking buffered data, and flushing remaining data through the response context.

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : In finalize() for objects holding JS references, release them using .deref() before destroy

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to **/*.zig : In Zig, manage memory carefully with allocators and use defer for cleanup

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-15T20:47:57.118Z
Learnt from: cirospaciari
PR: oven-sh/bun#22696
File: src/sql/mysql/js/JSMySQLConnection.zig:306-322
Timestamp: 2025-09-15T20:47:57.118Z
Learning: bun.Async.KeepAlive already maintains internal state to prevent issues from multiple ref/unref calls, so additional idempotence guards are not needed when calling poll_ref.ref() and poll_ref.unref().

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-05T19:48:44.481Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/valkey.zig:0-0
Timestamp: 2025-09-05T19:48:44.481Z
Learning: In ValkeyCommand.Promise, when you need to resolve with already-converted JSValues (not RESPValues), use promise_ptr.promise.resolve() to bypass the RESP-to-JS conversion wrapper. The ValkeyCommand.Promise.resolve() method is for converting RESPValues, while direct .promise access is for pre-converted JSValues.

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/**/js_*.zig : Always implement proper cleanup in deinit() and finalize() for JS-exposed types

Applied to files:

  • src/valkey/js_valkey.zig
🧬 Code graph analysis (1)
test/js/valkey/valkey.test.ts (2)
test/js/valkey/test-utils.ts (9)
  • isEnabled (10-27)
  • setupDockerContainer (203-214)
  • createClient (229-289)
  • expectType (496-501)
  • TLS_REDIS_URL (123-123)
  • DEFAULT_REDIS_URL (122-122)
  • TLS_REDIS_OPTIONS (80-93)
  • awaitableCounter (662-697)
  • randomCoinFlip (655-657)
test/js/valkey/valkey.failing-subscriber.ts (1)
  • RedisTestStartMessage (16-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format
🔇 Additional comments (19)
test/js/valkey/valkey.test.ts (4)

18-20: Fix TLS/TCP ctx selection (enum truthiness bug).

The ternary routes TLS to the TCP client. Compare explicitly against the enum.

Apply this diff:

-  const ctx = { ..._ctx, redis: connectionType ? _ctx.redis : _ctx.redisTLS };
+  const ctx = {
+    ..._ctx,
+    redis: connectionType === ConnectionType.TLS ? _ctx.redisTLS : _ctx.redis,
+  };

285-291: Await the async matcher.

The assertion never runs if you don’t await it.

Apply this diff:

-        expect(ctx.redis.set(testKey(), testValue())).resolves.toEqual("OK");
+        await expect(ctx.redis.set(testKey(), testValue())).resolves.toEqual("OK");

299-304: Fix variable shadowing in subscribe callback.

The expectations compare values to themselves and always pass.

Apply this diff:

-        await subscriber.subscribe(channel, (message, channel) => {
-          counter.increment();
-          expect(channel).toBe(channel);
-          expect(message).toBe(message);
-        });
+        await subscriber.subscribe(channel, (payload, receivedChannel) => {
+          counter.increment();
+          expect(receivedChannel).toBe(channel);
+          expect(payload).toBe(message);
+        });

203-206: Use rejects; toThrow won’t catch async errors.

toThrowErrorMatchingInlineSnapshot on an async function won’t assert the rejection.

Apply this diff:

-expect(async () => {
-  await customRedis.get("test");
-}).toThrowErrorMatchingInlineSnapshot(`"WRONGPASS invalid username-password pair or user is disabled."`);
+await expect(customRedis.get("test")).rejects.toThrow(
+  "WRONGPASS invalid username-password pair or user is disabled.",
+);
⛔ Skipped due to learnings
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:561-564
Timestamp: 2025-09-20T00:58:38.042Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods (like ctx.redis.set(), ctx.redis.unsubscribe(), etc.) - Bun's Redis client implementation differs from Node.js and can throw synchronously even for async methods. The maintainer has explicitly requested to stop looking at this error pattern.
Learnt from: markovejnovic
PR: oven-sh/bun#22568
File: test/js/valkey/valkey.test.ts:268-276
Timestamp: 2025-09-20T00:57:56.685Z
Learning: For test/js/valkey/valkey.test.ts, do not comment on synchronous throw assertions for async Redis methods like ctx.redis.set() - the maintainer has explicitly requested to stop looking at this error pattern.
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : In error tests, assert non-zero exit codes for failing processes and use `toThrow` for synchronous errors
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-09-03T17:10:13.486Z
Learning: Applies to test/**/*.test.ts : Prefer `async/await` in tests; for a single callback, use `Promise.withResolvers()`
src/valkey/valkey.zig (5)

90-96: TLS.clone() looks correct.

Clones custom SSLConfig via deep clone and preserves other variants by value. Matches ownership expectations.


467-469: Detaching socket and marking disconnected on close is appropriate.

Ensures a consistent state before reconnection logic triggers.


957-961: Gate start() on TCP; defer for TLS until handshake.

This prevents sending HELLO before TLS handshake completes. Good fix for TLS hangs.


965-968: Making start() public is reasonable.

Enables invocation from TLS handshake handler while maintaining the same flow.


1080-1092: Queuing during connecting/disconnected is a safer default.

Disallowing immediate writes while connecting (esp. TLS) avoids pre-handshake writes. Ensure tests cover offline-queue semantics for connecting.

Please confirm test coverage for:

  • commands enqueued while .connecting (both pipelinable and non-pipelinable),
  • transition to .connected drains correctly without ordering regressions.
src/valkey/js_valkey.zig (9)

230-231: Per-instance SocketContext storage LGTM.

Storing custom TLS contexts per client avoids re-creation and supports proper lifecycle via deferred deinit.


366-366: TLS option resolution matches URL semantics.

Prefers explicit options.tls, otherwise infers from URI. Correct for rediss/valkeys.


413-415: Cloning TLS into duplicates is the right call.

Avoids shared ownership/lifetime pitfalls for SSLConfig contents.


760-767: Deferred onWritable+updatePollRef after connect is good.

Flushes pending work and refreshes lifecycle without blocking the event loop.


773-780: Hello value conversion with exception cleanup is correct.

Catching toJS error and clearing exception keeps behavior consistent and avoids leaking pending exceptions.


1105-1126: Deferred deinit of custom SocketContext looks good.

Enqueueing deinit avoids JS reentrancy during teardown. Ownership/path are clear.


1303-1308: TLS handshake flow: verify assumptions.

Starting only after handshake success is correct. Please confirm ssl_error.error_no semantics: if it’s non-zero only for hostname issues, current check is fine. If it can indicate other verification failures, we should reject regardless when rejectUnauthorized is true.

Can you confirm what us_bun_verify_error_t.error_no represents across uWS/BoringSSL for TLS client sockets and whether non-hostname verification failures can surface with success == 1?

Also applies to: 1335-1336


912-916: Fix ref/deref imbalance in onValkeyClose().

There’s a deferred deref without a matching ref; can underflow refcount. Add a ref before the defer block.

Apply this diff:

-        defer {
+        this.ref();
+        defer {
             // Update poll reference to allow garbage collection of disconnected clients
             this.updatePollRef();
             this.deref();
         }

1054-1062: Remove unbalanced ref() in connect().

this.ref() here has no matching deref() on all paths and can leak. Liveness is already guarded by poll_ref and JS refs during connect.

Apply this diff:

-        this.ref();
-
         if (own_ctx) {
             // save the context so we deinit it later (if we reconnect we can reuse the same context)
             this._socket_ctx = ctx;
         }
         this.client.status = .connecting;
         this.updatePollRef();
src/bun.js/api/server/SSLConfig.zig (1)

252-261: Default cipher pointer lost on clone() (changes TLS behavior).

When is_using_default_ciphers is true, cloned.ssl_ciphers stays null, so asUSockets() won’t forward Bun’s curated default ciphers. Clones then fall back to uWS/OpenSSL defaults, potentially breaking TLS interoperability versus the source config. Copy the pointer in the default case (no ownership transfer) to preserve behavior.

Apply this diff:

     if (!this.is_using_default_ciphers) {
         if (this.ssl_ciphers) |slice_ptr| {
             const slice = std.mem.span(slice_ptr);
             if (slice.len > 0) {
                 cloned.ssl_ciphers = bun.handleOom(bun.default_allocator.dupeZ(u8, slice));
             } else {
                 cloned.ssl_ciphers = null;
             }
         }
+    } else {
+        // Preserve curated defaults without taking ownership
+        cloned.ssl_ciphers = this.ssl_ciphers;
     }

Comment thread src/valkey/js_valkey.zig
Comment thread src/valkey/js_valkey.zig
Comment thread test/js/valkey/valkey.test.ts
Comment thread test/js/valkey/valkey.test.ts
Comment thread src/valkey/js_valkey.zig

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3fd3840 and 4678bf4.

📒 Files selected for processing (1)
  • src/valkey/js_valkey.zig (16 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)

Implement debug logs in Zig using const log = bun.Output.scoped(.${SCOPE}, false); and invoking log("...", .{})

Files:

  • src/valkey/js_valkey.zig
**/*.zig

📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)

**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue

**/*.zig: Format Zig files with zig-format (bun run zig-format)
In Zig, manage memory carefully with allocators and use defer for cleanup

Files:

  • src/valkey/js_valkey.zig
src/**/js_*.zig

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

src/**/js_*.zig: Implement JavaScript bindings in a Zig file named with a js_ prefix (e.g., js_smtp.zig, js_your_feature.zig)
Handle reference counting correctly with ref()/deref() in JS-facing Zig code
Always implement proper cleanup in deinit() and finalize() for JS-exposed types

Files:

  • src/valkey/js_valkey.zig
src/{**/js_*.zig,bun.js/api/**/*.zig}

📄 CodeRabbit inference engine (.cursor/rules/registering-bun-modules.mdc)

Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions

Files:

  • src/valkey/js_valkey.zig
🧠 Learnings (7)
📚 Learning: 2025-09-05T18:45:29.200Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/js_valkey.zig:0-0
Timestamp: 2025-09-05T18:45:29.200Z
Learning: In JSValkeyClient.cloneWithoutConnecting() in src/valkey/js_valkey.zig, the address/username/password fields must be repointed to the duplicated connection_strings buffer to avoid use-after-free when the original client is destroyed. The original client properly frees connection_strings in ValkeyClient.deinit().

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-08-30T00:11:57.076Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.076Z
Learning: Applies to src/**/js_*.zig : Handle reference counting correctly with ref()/deref() in JS-facing Zig code

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-19T21:17:04.690Z
Learnt from: cirospaciari
PR: oven-sh/bun#22756
File: src/js/node/_http_server.ts:945-947
Timestamp: 2025-09-19T21:17:04.690Z
Learning: JSNodeHTTPServerSocket in src/bun.js/bindings/NodeHTTP.cpp implements both end() and close() methods. The end() method properly handles socket termination for both HTTP responses and raw CONNECT sockets by setting ended=true, checking buffered data, and flushing remaining data through the response context.

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-08-30T00:13:36.815Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.815Z
Learning: Applies to src/bun.js/**/*.zig : In finalize() for objects holding JS references, release them using .deref() before destroy

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-08T04:44:59.101Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-08T04:44:59.101Z
Learning: Applies to **/*.zig : In Zig, manage memory carefully with allocators and use defer for cleanup

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-15T20:47:57.118Z
Learnt from: cirospaciari
PR: oven-sh/bun#22696
File: src/sql/mysql/js/JSMySQLConnection.zig:306-322
Timestamp: 2025-09-15T20:47:57.118Z
Learning: bun.Async.KeepAlive already maintains internal state to prevent issues from multiple ref/unref calls, so additional idempotence guards are not needed when calling poll_ref.ref() and poll_ref.unref().

Applied to files:

  • src/valkey/js_valkey.zig
📚 Learning: 2025-09-05T19:48:44.481Z
Learnt from: markovejnovic
PR: oven-sh/bun#21728
File: src/valkey/valkey.zig:0-0
Timestamp: 2025-09-05T19:48:44.481Z
Learning: In ValkeyCommand.Promise, when you need to resolve with already-converted JSValues (not RESPValues), use promise_ptr.promise.resolve() to bypass the RESP-to-JS conversion wrapper. The ValkeyCommand.Promise.resolve() method is for converting RESPValues, while direct .promise access is for pre-converted JSValues.

Applied to files:

  • src/valkey/js_valkey.zig
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Format

Comment thread src/valkey/js_valkey.zig

@markovejnovic markovejnovic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm, feel free to merge

Comment thread src/valkey/js_valkey.zig
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.

Bun Redis does not work with TLS

4 participants