From a2520aee6ee851ddbe17cd63eff8f0010377284f Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Thu, 9 Apr 2026 20:11:58 -0700 Subject: [PATCH 01/13] Fix bug with cluster connect and prompted connection Signed-off-by: Ravjot Brar --- apps/frontend/src/components/connection/Connection.tsx | 2 +- apps/server/src/connection.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/connection/Connection.tsx b/apps/frontend/src/components/connection/Connection.tsx index 30462e79..129c7fd3 100644 --- a/apps/frontend/src/components/connection/Connection.tsx +++ b/apps/frontend/src/components/connection/Connection.tsx @@ -57,7 +57,7 @@ export function Connection() { const promptedConnection = connections[passwordPromptConnectionId as string] const isPromptConnecting = promptedConnection?.status === CONNECTING - const promptErrorMessage = promptedConnection.errorMessage + const promptErrorMessage = promptedConnection?.errorMessage const promptConnectionLabel = promptedConnection ? promptedConnection.connectionDetails.alias || `${promptedConnection.connectionDetails.host}:${promptedConnection.connectionDetails.port}` diff --git a/apps/server/src/connection.ts b/apps/server/src/connection.ts index e19ef0f1..bef85213 100644 --- a/apps/server/src/connection.ts +++ b/apps/server/src/connection.ts @@ -338,6 +338,7 @@ export async function connectToCluster( clusterSlotStatsEnabled, jsonModuleAvailable, }, + address: addresses[0], }, }), ) From 4bb6e52cb6a20294871d2b9f7f929147b019ae3c Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Thu, 9 Apr 2026 20:21:25 -0700 Subject: [PATCH 02/13] Add address to the other connectClusterFulfilled action Signed-off-by: Ravjot Brar --- apps/server/src/connection.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/server/src/connection.ts b/apps/server/src/connection.ts index bef85213..d8793236 100644 --- a/apps/server/src/connection.ts +++ b/apps/server/src/connection.ts @@ -316,6 +316,7 @@ export async function connectToCluster( payload: { connectionId, connectedNode: addresses[0], + address: addresses[0], connectionDetails: { clusterId, keyEvictionPolicy, From d318527af5074cb77562cff7996a0e85199d1443 Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Thu, 9 Apr 2026 20:24:39 -0700 Subject: [PATCH 03/13] Remove redundant clusterConnectFulfilled Signed-off-by: Ravjot Brar --- .../connection/connectionSlice.ts | 4 ++-- apps/server/src/connection.ts | 18 +----------------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts b/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts index dee2247c..e23f5b64 100644 --- a/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts +++ b/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts @@ -152,7 +152,7 @@ const connectionSlice = createSlice({ clusterConnectFulfilled: (state, action) => { const { connectionId, - connectedNode, + address, connectionDetails } = action.payload const { clusterId, keyEvictionPolicy, clusterSlotStatsEnabled, jsonModuleAvailable } = connectionDetails @@ -163,7 +163,7 @@ const connectionSlice = createSlice({ connectionState.connectionDetails.keyEvictionPolicy = keyEvictionPolicy connectionState.connectionDetails.clusterSlotStatsEnabled = clusterSlotStatsEnabled connectionState.connectionDetails.jsonModuleAvailable = jsonModuleAvailable - if (connectedNode) connectionState.connectedNode = connectedNode + if (address) connectionState.connectedNode = address delete connectionState.reconnect connectionState.connectionHistory ??= [] connectionState.connectionHistory.push({ timestamp: Date.now(), event: CONNECTED }) diff --git a/apps/server/src/connection.ts b/apps/server/src/connection.ts index d8793236..8710daf1 100644 --- a/apps/server/src/connection.ts +++ b/apps/server/src/connection.ts @@ -310,24 +310,8 @@ export async function connectToCluster( if (!clusterNodesMap.get(clusterId)?.includes(nodeConnectionId)) clusterNodesMap.get(clusterId)?.push(nodeConnectionId) if (!metricsServerMap.has(connectionId)) await startMetricsServer(payload.connectionDetails, connectionId) // Add connectedNode to payload - ws.send( - JSON.stringify({ - type: VALKEY.CONNECTION.clusterConnectFulfilled, - payload: { - connectionId, - connectedNode: addresses[0], - address: addresses[0], - connectionDetails: { - clusterId, - keyEvictionPolicy, - clusterSlotStatsEnabled, - jsonModuleAvailable, - }, - }, - }), - ) - return clusterClient } + ws.send( JSON.stringify({ type: VALKEY.CONNECTION.clusterConnectFulfilled, From 17ca113df528771499692a636eb451c7fffd0d82 Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Thu, 9 Apr 2026 20:26:26 -0700 Subject: [PATCH 04/13] Revert last change Signed-off-by: Ravjot Brar --- .../connection/connectionSlice.ts | 4 ++-- apps/server/src/connection.ts | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts b/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts index e23f5b64..dee2247c 100644 --- a/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts +++ b/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts @@ -152,7 +152,7 @@ const connectionSlice = createSlice({ clusterConnectFulfilled: (state, action) => { const { connectionId, - address, + connectedNode, connectionDetails } = action.payload const { clusterId, keyEvictionPolicy, clusterSlotStatsEnabled, jsonModuleAvailable } = connectionDetails @@ -163,7 +163,7 @@ const connectionSlice = createSlice({ connectionState.connectionDetails.keyEvictionPolicy = keyEvictionPolicy connectionState.connectionDetails.clusterSlotStatsEnabled = clusterSlotStatsEnabled connectionState.connectionDetails.jsonModuleAvailable = jsonModuleAvailable - if (address) connectionState.connectedNode = address + if (connectedNode) connectionState.connectedNode = connectedNode delete connectionState.reconnect connectionState.connectionHistory ??= [] connectionState.connectionHistory.push({ timestamp: Date.now(), event: CONNECTED }) diff --git a/apps/server/src/connection.ts b/apps/server/src/connection.ts index 8710daf1..d8793236 100644 --- a/apps/server/src/connection.ts +++ b/apps/server/src/connection.ts @@ -310,8 +310,24 @@ export async function connectToCluster( if (!clusterNodesMap.get(clusterId)?.includes(nodeConnectionId)) clusterNodesMap.get(clusterId)?.push(nodeConnectionId) if (!metricsServerMap.has(connectionId)) await startMetricsServer(payload.connectionDetails, connectionId) // Add connectedNode to payload + ws.send( + JSON.stringify({ + type: VALKEY.CONNECTION.clusterConnectFulfilled, + payload: { + connectionId, + connectedNode: addresses[0], + address: addresses[0], + connectionDetails: { + clusterId, + keyEvictionPolicy, + clusterSlotStatsEnabled, + jsonModuleAvailable, + }, + }, + }), + ) + return clusterClient } - ws.send( JSON.stringify({ type: VALKEY.CONNECTION.clusterConnectFulfilled, From d8cd018b77a9cb6d388d83796bb606564184ea1c Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Fri, 10 Apr 2026 11:43:53 -0700 Subject: [PATCH 05/13] Add iam guard and store empty passwords when no password required Signed-off-by: Ravjot Brar --- .../src/components/connection/ClusterConnectionGroup.tsx | 3 ++- apps/frontend/src/components/connection/ConnectionEntry.tsx | 3 ++- .../src/state/valkey-features/connection/connectionSlice.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/connection/ClusterConnectionGroup.tsx b/apps/frontend/src/components/connection/ClusterConnectionGroup.tsx index 78d815d3..d0b9691d 100644 --- a/apps/frontend/src/components/connection/ClusterConnectionGroup.tsx +++ b/apps/frontend/src/components/connection/ClusterConnectionGroup.tsx @@ -96,7 +96,8 @@ export const ClusterConnectionGroup = ({ clusterId, connections, highlight = "", const handleConnectLatest = () => { if (!lastOpenedNode) return - if (lastOpenedNode.connection.connectionDetails.password === undefined && onPasswordRequired) { + const { password, authType } = lastOpenedNode.connection.connectionDetails + if (authType !== "iam" && password === undefined && onPasswordRequired) { onPasswordRequired(lastOpenedNode.connectionId) return } diff --git a/apps/frontend/src/components/connection/ConnectionEntry.tsx b/apps/frontend/src/components/connection/ConnectionEntry.tsx index bfbd7e66..d41701dd 100644 --- a/apps/frontend/src/components/connection/ConnectionEntry.tsx +++ b/apps/frontend/src/components/connection/ConnectionEntry.tsx @@ -41,7 +41,8 @@ export const ConnectionEntry = ({ const handleDisconnect = () => dispatch(closeConnection({ connectionId })) const handleConnect = () => { - if (connection.connectionDetails.password === undefined && onPasswordRequired) { + const { password, authType } = connection.connectionDetails + if (authType !== "iam" && password === undefined && onPasswordRequired) { onPasswordRequired(connectionId) return } diff --git a/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts b/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts index dee2247c..9523639d 100644 --- a/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts +++ b/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts @@ -109,7 +109,7 @@ const connectionSlice = createSlice({ connectionDetails: { ...connectionDetails, // Strip password from state if secure storage is unavailable to prevent unencrypted persistence. - password: connectionDetails.password && secureStorage.isAvailable() ? connectionDetails.password : undefined, + password: connectionDetails.password != undefined && secureStorage.isAvailable() ? connectionDetails.password : undefined, clusterSlotStatsEnabled: false, jsonModuleAvailable: false, }, From 7ebf5151b2cfb3ea034b38e03369ebdce3f9dc49 Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Fri, 10 Apr 2026 16:00:01 -0700 Subject: [PATCH 06/13] Bump up timeouts for cluster client creation Signed-off-by: Ravjot Brar --- apps/server/src/connection.ts | 2 +- apps/server/src/metrics-orchestrator.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/server/src/connection.ts b/apps/server/src/connection.ts index d8793236..ccef2fa8 100644 --- a/apps/server/src/connection.ts +++ b/apps/server/src/connection.ts @@ -226,7 +226,7 @@ export async function connectToCluster( const { connectionId } = payload const { verifyTlsCertificate, tls: useTLS } = payload.connectionDetails try { - const CONNECTION_TIMEOUT_MS = 10000 + const CONNECTION_TIMEOUT_MS = 30000 let clusterClient = await Promise.race([ createClusterValkeyClient({ addresses, credentials, useTLS, verifyTlsCertificate }), new Promise((_, reject) => diff --git a/apps/server/src/metrics-orchestrator.ts b/apps/server/src/metrics-orchestrator.ts index 6eaca617..4bf0b9d8 100644 --- a/apps/server/src/metrics-orchestrator.ts +++ b/apps/server/src/metrics-orchestrator.ts @@ -160,7 +160,13 @@ async function createClusterClient(connectionDetails: ConnectionDetails) { } : password ? { username, password } : undefined - return await createOrchestratorValkeyClient({ addresses, credentials, useTLS: tls, verifyTlsCertificate }) + const CONNECTION_TIMEOUT_MS = 30000 + return await Promise.race([ + createOrchestratorValkeyClient({ addresses, credentials, useTLS: tls, verifyTlsCertificate }), + new Promise((_, reject) => + setTimeout(() => reject(new Error("Connection timed out")), CONNECTION_TIMEOUT_MS), + ), + ]) } async function getClusterTopology(client: GlideClusterClient | null, node: ConnectionDetails) { From ae910e0671feb32fd73c72c0fc06ba786d018226 Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Fri, 10 Apr 2026 16:04:35 -0700 Subject: [PATCH 07/13] Bump request timeout as well Signed-off-by: Ravjot Brar --- apps/metrics/src/valkey-client.js | 2 +- apps/metrics/src/valkey-client.test.js | 2 +- apps/server/src/__tests__/connection.test.ts | 5 +++-- apps/server/src/valkey-client.ts | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/metrics/src/valkey-client.js b/apps/metrics/src/valkey-client.js index 19988267..7a075ebb 100644 --- a/apps/metrics/src/valkey-client.js +++ b/apps/metrics/src/valkey-client.js @@ -47,7 +47,7 @@ export const createValkeyClient = async (cfg = {}) => { }, }, }), - requestTimeout: 5000, + requestTimeout: 30000, } const mode = getValkeyMode(cfg) diff --git a/apps/metrics/src/valkey-client.test.js b/apps/metrics/src/valkey-client.test.js index befd9922..b2f5b844 100644 --- a/apps/metrics/src/valkey-client.test.js +++ b/apps/metrics/src/valkey-client.test.js @@ -84,7 +84,7 @@ describe("valkey client mode selection", () => { insecure: true, }, }, - requestTimeout: 5000, + requestTimeout: 30000, clientName: "valkey_admin_metrics_cluster_client", }) }) diff --git a/apps/server/src/__tests__/connection.test.ts b/apps/server/src/__tests__/connection.test.ts index dfd4b7a4..7db92813 100644 --- a/apps/server/src/__tests__/connection.test.ts +++ b/apps/server/src/__tests__/connection.test.ts @@ -295,7 +295,7 @@ describe("connectToValkey", () => { host: alternate_payload.connectionDetails.host, port: Number(alternate_payload.connectionDetails.port), }]) - assert.strictEqual(config.requestTimeout, 5000) + assert.strictEqual(config.requestTimeout, 30000) assert.strictEqual(config.clientName, "valkey_server_standalone_client") return mockStandaloneClient as any }) @@ -309,6 +309,7 @@ describe("connectToValkey", () => { tls: false, verifyTlsCertificate: false, connectionId: "conn-456", + endpointType: "node", } as ConnectionDetails, connectionId: "", } @@ -323,7 +324,7 @@ describe("connectToValkey", () => { host: alternate_payload.connectionDetails.host, port: Number(alternate_payload.connectionDetails.port), }]) - assert.strictEqual(config.requestTimeout, 5000) + assert.strictEqual(config.requestTimeout, 30000) assert.strictEqual(config.clientName, "valkey_server_standalone_client") } finally { GlideClient.createClient = originalCreateClient diff --git a/apps/server/src/valkey-client.ts b/apps/server/src/valkey-client.ts index cd11b529..917f6061 100644 --- a/apps/server/src/valkey-client.ts +++ b/apps/server/src/valkey-client.ts @@ -28,7 +28,7 @@ const buildSharedOptions = ({ }, }, }), - requestTimeout: 5000, + requestTimeout: 30000, }) export const createStandaloneValkeyClient = ({ From 15bcb24791079dc9a7475e6fe0b6d4a0ddc63ace Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Fri, 10 Apr 2026 16:16:58 -0700 Subject: [PATCH 08/13] Add connectionTimeout Signed-off-by: Ravjot Brar --- apps/metrics/src/valkey-client.js | 3 ++- apps/metrics/src/valkey-client.test.js | 2 +- apps/server/src/__tests__/connection.test.ts | 2 +- apps/server/src/connection.ts | 8 +------- apps/server/src/metrics-orchestrator.ts | 8 +------- apps/server/src/valkey-client.ts | 3 ++- 6 files changed, 8 insertions(+), 18 deletions(-) diff --git a/apps/metrics/src/valkey-client.js b/apps/metrics/src/valkey-client.js index 7a075ebb..5372e8bc 100644 --- a/apps/metrics/src/valkey-client.js +++ b/apps/metrics/src/valkey-client.js @@ -47,7 +47,8 @@ export const createValkeyClient = async (cfg = {}) => { }, }, }), - requestTimeout: 30000, + requestTimeout: 5000, + connectionTimeout: 60000, } const mode = getValkeyMode(cfg) diff --git a/apps/metrics/src/valkey-client.test.js b/apps/metrics/src/valkey-client.test.js index b2f5b844..befd9922 100644 --- a/apps/metrics/src/valkey-client.test.js +++ b/apps/metrics/src/valkey-client.test.js @@ -84,7 +84,7 @@ describe("valkey client mode selection", () => { insecure: true, }, }, - requestTimeout: 30000, + requestTimeout: 5000, clientName: "valkey_admin_metrics_cluster_client", }) }) diff --git a/apps/server/src/__tests__/connection.test.ts b/apps/server/src/__tests__/connection.test.ts index 7db92813..bf4af550 100644 --- a/apps/server/src/__tests__/connection.test.ts +++ b/apps/server/src/__tests__/connection.test.ts @@ -324,7 +324,7 @@ describe("connectToValkey", () => { host: alternate_payload.connectionDetails.host, port: Number(alternate_payload.connectionDetails.port), }]) - assert.strictEqual(config.requestTimeout, 30000) + assert.strictEqual(config.requestTimeout, 5000) assert.strictEqual(config.clientName, "valkey_server_standalone_client") } finally { GlideClient.createClient = originalCreateClient diff --git a/apps/server/src/connection.ts b/apps/server/src/connection.ts index ccef2fa8..4014f4c6 100644 --- a/apps/server/src/connection.ts +++ b/apps/server/src/connection.ts @@ -226,13 +226,7 @@ export async function connectToCluster( const { connectionId } = payload const { verifyTlsCertificate, tls: useTLS } = payload.connectionDetails try { - const CONNECTION_TIMEOUT_MS = 30000 - let clusterClient = await Promise.race([ - createClusterValkeyClient({ addresses, credentials, useTLS, verifyTlsCertificate }), - new Promise((_, reject) => - setTimeout(() => reject(new Error("Connection timed out")), CONNECTION_TIMEOUT_MS), - ), - ]) + let clusterClient = await createClusterValkeyClient({ addresses, credentials, useTLS, verifyTlsCertificate }) // TODO: Optimize to not call discoverCluster when configEndpointId is available // It implies we already discovered cluster nodes once diff --git a/apps/server/src/metrics-orchestrator.ts b/apps/server/src/metrics-orchestrator.ts index 4bf0b9d8..6eaca617 100644 --- a/apps/server/src/metrics-orchestrator.ts +++ b/apps/server/src/metrics-orchestrator.ts @@ -160,13 +160,7 @@ async function createClusterClient(connectionDetails: ConnectionDetails) { } : password ? { username, password } : undefined - const CONNECTION_TIMEOUT_MS = 30000 - return await Promise.race([ - createOrchestratorValkeyClient({ addresses, credentials, useTLS: tls, verifyTlsCertificate }), - new Promise((_, reject) => - setTimeout(() => reject(new Error("Connection timed out")), CONNECTION_TIMEOUT_MS), - ), - ]) + return await createOrchestratorValkeyClient({ addresses, credentials, useTLS: tls, verifyTlsCertificate }) } async function getClusterTopology(client: GlideClusterClient | null, node: ConnectionDetails) { diff --git a/apps/server/src/valkey-client.ts b/apps/server/src/valkey-client.ts index 917f6061..ca0dcfc0 100644 --- a/apps/server/src/valkey-client.ts +++ b/apps/server/src/valkey-client.ts @@ -28,7 +28,8 @@ const buildSharedOptions = ({ }, }, }), - requestTimeout: 30000, + requestTimeout: 5000, + connectionTimeout: 30000, }) export const createStandaloneValkeyClient = ({ From 35354df7244d4fe35f4671f5b4345d4d8e84d9d3 Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Fri, 10 Apr 2026 16:47:49 -0700 Subject: [PATCH 09/13] Add connectionTimeout under advanced config Signed-off-by: Ravjot Brar --- apps/metrics/src/valkey-client.js | 2 +- apps/server/src/valkey-client.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/metrics/src/valkey-client.js b/apps/metrics/src/valkey-client.js index 5372e8bc..bb05a227 100644 --- a/apps/metrics/src/valkey-client.js +++ b/apps/metrics/src/valkey-client.js @@ -45,10 +45,10 @@ export const createValkeyClient = async (cfg = {}) => { tlsAdvancedConfiguration: { insecure: true, }, + connectionTimeout: 30000, }, }), requestTimeout: 5000, - connectionTimeout: 60000, } const mode = getValkeyMode(cfg) diff --git a/apps/server/src/valkey-client.ts b/apps/server/src/valkey-client.ts index ca0dcfc0..732b0a25 100644 --- a/apps/server/src/valkey-client.ts +++ b/apps/server/src/valkey-client.ts @@ -26,10 +26,10 @@ const buildSharedOptions = ({ tlsAdvancedConfiguration: { insecure: true, }, + connectionTimeout: 30000, }, }), requestTimeout: 5000, - connectionTimeout: 30000, }) export const createStandaloneValkeyClient = ({ From c83be40bbabefa1e7901c5121f22f59bc8d603e4 Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Fri, 10 Apr 2026 16:54:56 -0700 Subject: [PATCH 10/13] Move advancedConfig outside of conditional Signed-off-by: Ravjot Brar --- apps/metrics/src/valkey-client.js | 10 +++++----- apps/server/src/valkey-client.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/metrics/src/valkey-client.js b/apps/metrics/src/valkey-client.js index bb05a227..e1e91ff0 100644 --- a/apps/metrics/src/valkey-client.js +++ b/apps/metrics/src/valkey-client.js @@ -40,14 +40,14 @@ export const createValkeyClient = async (cfg = {}) => { addresses, credentials, useTLS, - ...(useTLS && process.env.VALKEY_VERIFY_CERT === "false" && { - advancedConfiguration: { + advancedConfiguration: { + ...(useTLS && process.env.VALKEY_VERIFY_CERT === "false" && { tlsAdvancedConfiguration: { insecure: true, }, - connectionTimeout: 30000, - }, - }), + }), + connectionTimeout: 30000, + }, requestTimeout: 5000, } diff --git a/apps/server/src/valkey-client.ts b/apps/server/src/valkey-client.ts index 732b0a25..18894ac8 100644 --- a/apps/server/src/valkey-client.ts +++ b/apps/server/src/valkey-client.ts @@ -21,14 +21,14 @@ const buildSharedOptions = ({ addresses, credentials, useTLS, - ...(useTLS && verifyTlsCertificate === false && { - advancedConfiguration: { + advancedConfiguration: { + ...(useTLS && verifyTlsCertificate === false && { tlsAdvancedConfiguration: { insecure: true, }, - connectionTimeout: 30000, - }, - }), + }), + connectionTimeout: 30000, + }, requestTimeout: 5000, }) From 9463e5f2d9b6b1004eb078216d147b0b89a7eefe Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Fri, 10 Apr 2026 17:48:51 -0700 Subject: [PATCH 11/13] Add R.isNil checks Signed-off-by: Ravjot Brar --- apps/frontend/src/components/ValkeyReconnect.tsx | 3 ++- .../src/components/connection/ClusterConnectionGroup.tsx | 2 +- apps/frontend/src/components/connection/ConnectionEntry.tsx | 3 ++- apps/frontend/src/state/epics/valkeyEpics.ts | 4 ++-- .../src/state/valkey-features/connection/connectionSlice.ts | 6 ++++-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/frontend/src/components/ValkeyReconnect.tsx b/apps/frontend/src/components/ValkeyReconnect.tsx index b118d203..b61e0e6d 100644 --- a/apps/frontend/src/components/ValkeyReconnect.tsx +++ b/apps/frontend/src/components/ValkeyReconnect.tsx @@ -7,6 +7,7 @@ import RetryProgress from "./ui/retry-progress" import { PasswordPromptModal } from "./ui/password-prompt-modal" import type { RootState } from "@/store" import { connectPending } from "@/state/valkey-features/connection/connectionSlice" +import * as R from "ramda" import { secureStorage } from "@/utils/secureStorage" export function ValkeyReconnect() { @@ -20,7 +21,7 @@ export function ValkeyReconnect() { const { status, errorMessage, reconnect } = connection || {} const [showPasswordPrompt, setShowPasswordPrompt] = useState(false) - const needsPassword = connection?.connectionDetails.password === undefined + const needsPassword = R.isNil(connection?.connectionDetails.password) // Close password prompt on successful connection useEffect(() => { diff --git a/apps/frontend/src/components/connection/ClusterConnectionGroup.tsx b/apps/frontend/src/components/connection/ClusterConnectionGroup.tsx index d0b9691d..ab812daf 100644 --- a/apps/frontend/src/components/connection/ClusterConnectionGroup.tsx +++ b/apps/frontend/src/components/connection/ClusterConnectionGroup.tsx @@ -97,7 +97,7 @@ export const ClusterConnectionGroup = ({ clusterId, connections, highlight = "", const handleConnectLatest = () => { if (!lastOpenedNode) return const { password, authType } = lastOpenedNode.connection.connectionDetails - if (authType !== "iam" && password === undefined && onPasswordRequired) { + if (authType !== "iam" && R.isNil(password) && onPasswordRequired) { onPasswordRequired(lastOpenedNode.connectionId) return } diff --git a/apps/frontend/src/components/connection/ConnectionEntry.tsx b/apps/frontend/src/components/connection/ConnectionEntry.tsx index d41701dd..86fe8f1c 100644 --- a/apps/frontend/src/components/connection/ConnectionEntry.tsx +++ b/apps/frontend/src/components/connection/ConnectionEntry.tsx @@ -1,4 +1,5 @@ import { CONNECTED, ERROR, CONNECTING } from "@common/src/constants.ts" +import * as R from "ramda" import { CircleChevronRight, Server } from "lucide-react" import { Link } from "react-router" import { @@ -42,7 +43,7 @@ export const ConnectionEntry = ({ const handleDisconnect = () => dispatch(closeConnection({ connectionId })) const handleConnect = () => { const { password, authType } = connection.connectionDetails - if (authType !== "iam" && password === undefined && onPasswordRequired) { + if (authType !== "iam" && R.isNil(password) && onPasswordRequired) { onPasswordRequired(connectionId) return } diff --git a/apps/frontend/src/state/epics/valkeyEpics.ts b/apps/frontend/src/state/epics/valkeyEpics.ts index c0c184ba..21f910dc 100644 --- a/apps/frontend/src/state/epics/valkeyEpics.ts +++ b/apps/frontend/src/state/epics/valkeyEpics.ts @@ -166,7 +166,7 @@ export const valkeyRetryEpic = (store: Store) => return EMPTY } - if (connection.connectionDetails.password === undefined) { + if (R.isNil(connection.connectionDetails.password)) { console.debug(`Password unavailable for ${connectionId}, skipping auto-retry`) store.dispatch(stopRetry({ connectionId })) return EMPTY @@ -223,7 +223,7 @@ export const autoReconnectEpic = (store: Store) => const disconnectedConnections = Object.entries(connections) .filter(([, connection]) => connection.status === DISCONNECTED) - .filter(([, connection]) => connection.connectionDetails.password !== undefined && connection.connectionDetails.password !== "") + .filter(([, connection]) => R.isNotNil(connection.connectionDetails.password)) if (disconnectedConnections.length > 0) { console.log(`Auto-reconnecting ${disconnectedConnections.length} connection(s)`) diff --git a/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts b/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts index 9523639d..bc0775b4 100644 --- a/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts +++ b/apps/frontend/src/state/valkey-features/connection/connectionSlice.ts @@ -108,8 +108,10 @@ const connectionSlice = createSlice({ errorMessage: isRetry && existingConnection?.errorMessage ? existingConnection.errorMessage : null, connectionDetails: { ...connectionDetails, - // Strip password from state if secure storage is unavailable to prevent unencrypted persistence. - password: connectionDetails.password != undefined && secureStorage.isAvailable() ? connectionDetails.password : undefined, + // Preserve "" (no-password connections) but strip real passwords if secure storage is unavailable + password: (R.isNotNil(connectionDetails.password) && secureStorage.isAvailable()) || R.isEmpty(connectionDetails.password) + ? connectionDetails.password + : undefined, clusterSlotStatsEnabled: false, jsonModuleAvailable: false, }, From 4def75a14640671d3bf227d959c77b902376555e Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Fri, 10 Apr 2026 17:52:54 -0700 Subject: [PATCH 12/13] Fix test/lint Signed-off-by: Ravjot Brar --- apps/frontend/src/components/ValkeyReconnect.tsx | 2 +- apps/server/src/__tests__/connection.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/ValkeyReconnect.tsx b/apps/frontend/src/components/ValkeyReconnect.tsx index b61e0e6d..242bfd24 100644 --- a/apps/frontend/src/components/ValkeyReconnect.tsx +++ b/apps/frontend/src/components/ValkeyReconnect.tsx @@ -3,11 +3,11 @@ import { useDispatch, useSelector } from "react-redux" import { useNavigate, useParams } from "react-router" import { CONNECTED, CONNECTING, ERROR } from "@common/src/constants" import { Loader2, Database, AlertCircle } from "lucide-react" +import * as R from "ramda" import RetryProgress from "./ui/retry-progress" import { PasswordPromptModal } from "./ui/password-prompt-modal" import type { RootState } from "@/store" import { connectPending } from "@/state/valkey-features/connection/connectionSlice" -import * as R from "ramda" import { secureStorage } from "@/utils/secureStorage" export function ValkeyReconnect() { diff --git a/apps/server/src/__tests__/connection.test.ts b/apps/server/src/__tests__/connection.test.ts index bf4af550..d4d24e9a 100644 --- a/apps/server/src/__tests__/connection.test.ts +++ b/apps/server/src/__tests__/connection.test.ts @@ -295,7 +295,7 @@ describe("connectToValkey", () => { host: alternate_payload.connectionDetails.host, port: Number(alternate_payload.connectionDetails.port), }]) - assert.strictEqual(config.requestTimeout, 30000) + assert.strictEqual(config.requestTimeout, 5000) assert.strictEqual(config.clientName, "valkey_server_standalone_client") return mockStandaloneClient as any }) From 6996afca9cc9621491aa2d7b84b0014f5062fd01 Mon Sep 17 00:00:00 2001 From: Ravjot Brar Date: Fri, 10 Apr 2026 17:59:41 -0700 Subject: [PATCH 13/13] Fix metrics test Signed-off-by: Ravjot Brar --- apps/metrics/src/valkey-client.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/metrics/src/valkey-client.test.js b/apps/metrics/src/valkey-client.test.js index befd9922..680f65e0 100644 --- a/apps/metrics/src/valkey-client.test.js +++ b/apps/metrics/src/valkey-client.test.js @@ -83,6 +83,7 @@ describe("valkey client mode selection", () => { tlsAdvancedConfiguration: { insecure: true, }, + connectionTimeout: 30000, }, requestTimeout: 5000, clientName: "valkey_admin_metrics_cluster_client",