diff --git a/src/networking.c b/src/networking.c index 191f41e4e..2279289ad 100644 --- a/src/networking.c +++ b/src/networking.c @@ -4194,11 +4194,17 @@ int isClientConnIpV6(client *c) { /* The cached client peer id is on the form "[IPv6]:port" for IPv6 * addresses, so we just check for '[' here. */ if (c->flag.fake && server.current_client) { - /* Fake client? Use current client instead. - * Noted that in here we are assuming server.current_client is set - * and real (aof has already violated this in loadSingleAppendOnlyFil). */ + /* Fake client? Use current client instead, if we have one. */ c = server.current_client; } + + if (c->flag.fake || !c->conn) { + /* If we still don't have a client with a real connection (e.g., called + * from module timer with no real current client), default to IPv4 to + * avoid crashing. */ + return 0; + } + return getClientPeerId(c)[0] == '['; } diff --git a/tests/modules/cluster.c b/tests/modules/cluster.c index 2ba626b0e..7c9668982 100644 --- a/tests/modules/cluster.c +++ b/tests/modules/cluster.c @@ -3,6 +3,35 @@ #define UNUSED(x) (void)(x) +void cluster_timer_handler(ValkeyModuleCtx *ctx, void *data) { + VALKEYMODULE_NOT_USED(data); + + ValkeyModuleCallReply *rep = ValkeyModule_Call(ctx, "CLUSTER", "c", "SLOTS"); + + if (rep) { + if (ValkeyModule_CallReplyType(rep) == VALKEYMODULE_REPLY_ARRAY) { + ValkeyModule_Log(ctx, "notice", "Timer: CLUSTER SLOTS success"); + } else { + ValkeyModule_Log(ctx, "notice", + "Timer: CLUSTER SLOTS unexpected reply type %d", + ValkeyModule_CallReplyType(rep)); + } + ValkeyModule_FreeCallReply(rep); + } else { + ValkeyModule_Log(ctx, "warning", "Timer: CLUSTER SLOTS failed"); + } +} + +int test_start_cluster_timer(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) { + VALKEYMODULE_NOT_USED(argv); + VALKEYMODULE_NOT_USED(argc); + + ValkeyModule_CreateTimer(ctx, 1, cluster_timer_handler, NULL); + + return ValkeyModule_ReplyWithSimpleString(ctx, "OK"); +} + + int test_cluster_slots(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int argc) { UNUSED(argv); @@ -77,8 +106,12 @@ int ValkeyModule_OnLoad(ValkeyModuleCtx *ctx, ValkeyModuleString **argv, int arg if (ValkeyModule_CreateCommand(ctx, "test.cluster_shards", test_cluster_shards, "", 0, 0, 0) == VALKEYMODULE_ERR) return VALKEYMODULE_ERR; + if (ValkeyModule_CreateCommand(ctx, "test.start_cluster_timer", test_start_cluster_timer, "", 0, 0, 0) == VALKEYMODULE_ERR) + return VALKEYMODULE_ERR; + /* Register our handlers for different message types. */ ValkeyModule_RegisterClusterMessageReceiver(ctx, MSGTYPE_DING, DingReceiver); ValkeyModule_RegisterClusterMessageReceiver(ctx, MSGTYPE_DONG, DongReceiver); + return VALKEYMODULE_OK; } diff --git a/tests/unit/moduleapi/cluster.tcl b/tests/unit/moduleapi/cluster.tcl index 51ce68e3f..1bd53d64b 100644 --- a/tests/unit/moduleapi/cluster.tcl +++ b/tests/unit/moduleapi/cluster.tcl @@ -285,6 +285,18 @@ start_cluster 3 0 [list config_lines $modules] { assert_equal [lsort [$node2 cluster shards]] [lsort [$node2 test.cluster_shards]] assert_equal [lsort [$node3 cluster shards]] [lsort [$node3 test.cluster_shards]] } + + test "VM_CALL CLUSTER SLOTS from Module Timer" { + assert_equal {OK} [$node1 test.start_cluster_timer] + assert_equal {OK} [$node2 test.start_cluster_timer] + assert_equal {OK} [$node3 test.start_cluster_timer] + + wait_for_condition 50 100 { + [count_log_message 0 "* Timer: CLUSTER SLOTS success*"] >= 1 + } else { + fail "Timer did not execute CLUSTER SLOTS or server crashed" + } + } } } ;# end tag