From 554481a07b37fae49e667b4c5b90a6f2a24a1b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Fri, 13 Sep 2024 16:25:46 +0200 Subject: [PATCH 01/25] Remove flags from cluster connect APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These flags will be replaced by options and can be set using these new APIs instead: - valkeyClusterConnectWithOptions - valkeyClusterAsyncConnectWithOptions Signed-off-by: Björn Svensson --- examples/cluster-async.c | 3 +- include/valkey/cluster.h | 20 ++------------ src/cluster.c | 46 +++++++++++++++---------------- tests/ct_async_glib.c | 3 +- tests/ct_async_libev.c | 3 +- tests/ct_async_libuv.c | 3 +- tests/ct_out_of_memory_handling.c | 12 ++++---- tests/ut_slotmap_update.c | 10 +++---- 8 files changed, 40 insertions(+), 60 deletions(-) diff --git a/examples/cluster-async.c b/examples/cluster-async.c index 9f52ed8..847828b 100644 --- a/examples/cluster-async.c +++ b/examples/cluster-async.c @@ -50,8 +50,7 @@ int main(int argc, char **argv) { (void)argc; (void)argv; printf("Connecting...\n"); - valkeyClusterAsyncContext *cc = - valkeyClusterAsyncConnect("127.0.0.1:7000", VALKEYCLUSTER_FLAG_NULL); + valkeyClusterAsyncContext *cc = valkeyClusterAsyncConnect("127.0.0.1:7000"); if (!cc) { printf("Error: Allocation failure\n"); exit(-1); diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index 36f2875..111abc8 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -44,18 +44,6 @@ #define VALKEY_ROLE_PRIMARY 1 #define VALKEY_ROLE_REPLICA 2 -/* Configuration flags */ -#define VALKEYCLUSTER_FLAG_NULL 0x0 -/* Flag to enable parsing of slave nodes. Currently not used, but the - information is added to its master node structure. */ -#define VALKEYCLUSTER_FLAG_ADD_SLAVE 0x1000 -/* Flag to enable routing table updates using the command 'cluster slots'. - * Default is the 'cluster nodes' command. */ -#define VALKEYCLUSTER_FLAG_ROUTE_USE_SLOTS 0x4000 -/* Flag specific to the async API which means that the user requested a - * client disconnect or free. */ -#define VALKEYCLUSTER_FLAG_DISCONNECTING 0x8000 - /* Events, for valkeyClusterSetEventCallback() */ #define VALKEYCLUSTER_EVENT_SLOTMAP_UPDATED 1 #define VALKEYCLUSTER_EVENT_READY 2 @@ -160,10 +148,9 @@ typedef struct valkeyClusterNodeIterator { * Synchronous API */ -valkeyClusterContext *valkeyClusterConnect(const char *addrs, int flags); +valkeyClusterContext *valkeyClusterConnect(const char *addrs); valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, - const struct timeval tv, - int flags); + const struct timeval tv); int valkeyClusterConnect2(valkeyClusterContext *cc); valkeyClusterContext *valkeyClusterContextInit(void); @@ -277,8 +264,7 @@ int valkeyClusterAsyncSetDisconnectCallback(valkeyClusterAsyncContext *acc, valkeyDisconnectCallback *fn); /* Connect and update slotmap, will block until complete. */ -valkeyClusterAsyncContext *valkeyClusterAsyncConnect(const char *addrs, - int flags); +valkeyClusterAsyncContext *valkeyClusterAsyncConnect(const char *addrs); /* Connect and update slotmap asynchronously using configured event engine. */ int valkeyClusterAsyncConnect2(valkeyClusterAsyncContext *acc); void valkeyClusterAsyncDisconnect(valkeyClusterAsyncContext *acc); diff --git a/src/cluster.c b/src/cluster.c index c15240a..b04d86a 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -49,6 +49,10 @@ #include #include +#define VALKEY_FLAG_USE_CLUSTER_SLOTS 0x1 +#define VALKEY_FLAG_PARSE_REPLICAS 0x2 +#define VALKEY_FLAG_DISCONNECTING 0x4 + // Cluster errors are offset by 100 to be sufficiently out of range of // standard Valkey errors #define VALKEY_ERR_CLUSTER_TOO_MANY_RETRIES 100 @@ -653,7 +657,7 @@ static dict *parse_cluster_slots(valkeyClusterContext *cc, valkeyReply *reply) { } slot = NULL; - } else if (cc->flags & VALKEYCLUSTER_FLAG_ADD_SLAVE) { + } else if (cc->flags & VALKEY_FLAG_PARSE_REPLICAS) { replica = node_get_with_slots(cc, elem_ip, elem_port, VALKEY_ROLE_REPLICA); if (replica == NULL) { @@ -940,7 +944,7 @@ static int parse_cluster_nodes_line(valkeyClusterContext *cc, valkeyContext *c, static dict *parse_cluster_nodes(valkeyClusterContext *cc, valkeyContext *c, valkeyReply *reply) { dict *nodes = NULL; int slot_ranges_found = 0; - int add_replicas = cc->flags & VALKEYCLUSTER_FLAG_ADD_SLAVE; + int add_replicas = cc->flags & VALKEY_FLAG_PARSE_REPLICAS; dict *replicas = NULL; if (reply->type != VALKEY_REPLY_STRING) { @@ -1028,7 +1032,7 @@ static dict *parse_cluster_nodes(valkeyClusterContext *cc, valkeyContext *c, val /* Sends CLUSTER SLOTS or CLUSTER NODES to the node with context c. */ static int clusterUpdateRouteSendCommand(valkeyClusterContext *cc, valkeyContext *c) { - const char *cmd = (cc->flags & VALKEYCLUSTER_FLAG_ROUTE_USE_SLOTS ? + const char *cmd = (cc->flags & VALKEY_FLAG_USE_CLUSTER_SLOTS ? VALKEY_COMMAND_CLUSTER_SLOTS : VALKEY_COMMAND_CLUSTER_NODES); if (valkeyAppendCommand(c, cmd) != VALKEY_OK) { @@ -1060,7 +1064,7 @@ static int clusterUpdateRouteHandleReply(valkeyClusterContext *cc, } dict *nodes; - if (cc->flags & VALKEYCLUSTER_FLAG_ROUTE_USE_SLOTS) { + if (cc->flags & VALKEY_FLAG_USE_CLUSTER_SLOTS) { nodes = parse_cluster_slots(cc, reply); } else { nodes = parse_cluster_nodes(cc, c, reply); @@ -1310,7 +1314,7 @@ valkeyClusterConnectInternal(valkeyClusterContext *cc, const char *addrs) { return cc; } -valkeyClusterContext *valkeyClusterConnect(const char *addrs, int flags) { +valkeyClusterContext *valkeyClusterConnect(const char *addrs) { valkeyClusterContext *cc; cc = valkeyClusterContextInit(); @@ -1319,14 +1323,11 @@ valkeyClusterContext *valkeyClusterConnect(const char *addrs, int flags) { return NULL; } - cc->flags = flags; - return valkeyClusterConnectInternal(cc, addrs); } valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, - const struct timeval tv, - int flags) { + const struct timeval tv) { valkeyClusterContext *cc; cc = valkeyClusterContextInit(); @@ -1335,8 +1336,6 @@ valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, return NULL; } - cc->flags = flags; - if (cc->connect_timeout == NULL) { cc->connect_timeout = vk_malloc(sizeof(struct timeval)); if (cc->connect_timeout == NULL) { @@ -1542,7 +1541,7 @@ int valkeyClusterSetOptionParseSlaves(valkeyClusterContext *cc) { return VALKEY_ERR; } - cc->flags |= VALKEYCLUSTER_FLAG_ADD_SLAVE; + cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; return VALKEY_OK; } @@ -1553,7 +1552,7 @@ int valkeyClusterSetOptionRouteUseSlots(valkeyClusterContext *cc) { return VALKEY_ERR; } - cc->flags |= VALKEYCLUSTER_FLAG_ROUTE_USE_SLOTS; + cc->flags |= VALKEY_FLAG_USE_CLUSTER_SLOTS; return VALKEY_OK; } @@ -1663,7 +1662,7 @@ int valkeyClusterConnect2(valkeyClusterContext *cc) { } /* Clear a previously set shutdown flag since we allow a * reconnection of an async context using this API (legacy). */ - cc->flags &= ~VALKEYCLUSTER_FLAG_DISCONNECTING; + cc->flags &= ~VALKEY_FLAG_DISCONNECTING; return valkeyClusterUpdateSlotmap(cc); } @@ -2841,13 +2840,12 @@ valkeyClusterAsyncContext *valkeyClusterAsyncContextInit(void) { return acc; } -valkeyClusterAsyncContext *valkeyClusterAsyncConnect(const char *addrs, - int flags) { +valkeyClusterAsyncContext *valkeyClusterAsyncConnect(const char *addrs) { valkeyClusterContext *cc; valkeyClusterAsyncContext *acc; - cc = valkeyClusterConnect(addrs, flags); + cc = valkeyClusterConnect(addrs); if (cc == NULL) { return NULL; } @@ -2981,7 +2979,7 @@ static int updateSlotMapAsync(valkeyClusterAsyncContext *acc, /* Don't allow concurrent slot map updates. */ return VALKEY_ERR; } - if (acc->cc->flags & VALKEYCLUSTER_FLAG_DISCONNECTING) { + if (acc->cc->flags & VALKEY_FLAG_DISCONNECTING) { /* No slot map updates during a cluster client disconnect. */ return VALKEY_ERR; } @@ -3000,7 +2998,7 @@ static int updateSlotMapAsync(valkeyClusterAsyncContext *acc, /* Send a command depending of config */ int status; - if (acc->cc->flags & VALKEYCLUSTER_FLAG_ROUTE_USE_SLOTS) { + if (acc->cc->flags & VALKEY_FLAG_USE_CLUSTER_SLOTS) { status = valkeyAsyncCommand(ac, clusterSlotsReplyCallback, acc, VALKEY_COMMAND_CLUSTER_SLOTS); } else { @@ -3073,7 +3071,7 @@ static void valkeyClusterAsyncCallback(valkeyAsyncContext *ac, void *r, } /* Skip retry handling when not expected, or during a client disconnect. */ - if (cad->retry_count == NO_RETRY || cc->flags & VALKEYCLUSTER_FLAG_DISCONNECTING) + if (cad->retry_count == NO_RETRY || cc->flags & VALKEY_FLAG_DISCONNECTING) goto done; error_type = cluster_reply_error_type(reply); @@ -3186,7 +3184,7 @@ int valkeyClusterAsyncFormattedCommand(valkeyClusterAsyncContext *acc, cc = acc->cc; /* Don't accept new commands when the client is about to disconnect. */ - if (cc->flags & VALKEYCLUSTER_FLAG_DISCONNECTING) { + if (cc->flags & VALKEY_FLAG_DISCONNECTING) { valkeyClusterAsyncSetError(acc, VALKEY_ERR_OTHER, "disconnecting"); return VALKEY_ERR; } @@ -3267,7 +3265,7 @@ int valkeyClusterAsyncFormattedCommandToNode(valkeyClusterAsyncContext *acc, struct cmd *command = NULL; /* Don't accept new commands when the client is about to disconnect. */ - if (cc->flags & VALKEYCLUSTER_FLAG_DISCONNECTING) { + if (cc->flags & VALKEY_FLAG_DISCONNECTING) { valkeyClusterAsyncSetError(acc, VALKEY_ERR_OTHER, "disconnecting"); return VALKEY_ERR; } @@ -3449,7 +3447,7 @@ void valkeyClusterAsyncDisconnect(valkeyClusterAsyncContext *acc) { } cc = acc->cc; - cc->flags |= VALKEYCLUSTER_FLAG_DISCONNECTING; + cc->flags |= VALKEY_FLAG_DISCONNECTING; dictIterator di; dictInitIterator(&di, cc->nodes); @@ -3472,7 +3470,7 @@ void valkeyClusterAsyncFree(valkeyClusterAsyncContext *acc) { return; valkeyClusterContext *cc = acc->cc; - cc->flags |= VALKEYCLUSTER_FLAG_DISCONNECTING; + cc->flags |= VALKEY_FLAG_DISCONNECTING; valkeyClusterFree(cc); vk_free(acc); diff --git a/tests/ct_async_glib.c b/tests/ct_async_glib.c index e6d305f..58c076f 100644 --- a/tests/ct_async_glib.c +++ b/tests/ct_async_glib.c @@ -41,8 +41,7 @@ int main(int argc, char **argv) { GMainContext *context = NULL; mainloop = g_main_loop_new(context, FALSE); - valkeyClusterAsyncContext *acc = - valkeyClusterAsyncConnect(CLUSTER_NODE, VALKEYCLUSTER_FLAG_NULL); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnect(CLUSTER_NODE); assert(acc); ASSERT_MSG(acc->err == 0, acc->errstr); diff --git a/tests/ct_async_libev.c b/tests/ct_async_libev.c index 292df50..4f93c32 100644 --- a/tests/ct_async_libev.c +++ b/tests/ct_async_libev.c @@ -35,8 +35,7 @@ int main(int argc, char **argv) { UNUSED(argc); UNUSED(argv); - valkeyClusterAsyncContext *acc = - valkeyClusterAsyncConnect(CLUSTER_NODE, VALKEYCLUSTER_FLAG_NULL); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnect(CLUSTER_NODE); assert(acc); ASSERT_MSG(acc->err == 0, acc->errstr); diff --git a/tests/ct_async_libuv.c b/tests/ct_async_libuv.c index b2771a1..9c2aca0 100644 --- a/tests/ct_async_libuv.c +++ b/tests/ct_async_libuv.c @@ -36,8 +36,7 @@ int main(int argc, char **argv) { UNUSED(argc); UNUSED(argv); - valkeyClusterAsyncContext *acc = - valkeyClusterAsyncConnect(CLUSTER_NODE, VALKEYCLUSTER_FLAG_NULL); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnect(CLUSTER_NODE); assert(acc); ASSERT_MSG(acc->err == 0, acc->errstr); diff --git a/tests/ct_out_of_memory_handling.c b/tests/ct_out_of_memory_handling.c index 3d7de08..e65a4cf 100644 --- a/tests/ct_out_of_memory_handling.c +++ b/tests/ct_out_of_memory_handling.c @@ -127,7 +127,7 @@ void test_alloc_failure_handling(void) { cc = valkeyClusterContextInit(); assert(cc); } - cc->flags |= VALKEYCLUSTER_FLAG_ADD_SLAVE; + valkeyClusterSetOptionParseSlaves(cc); // Add nodes { @@ -173,14 +173,14 @@ void test_alloc_failure_handling(void) { // Connect { - for (int i = 0; i < 88; ++i) { + for (int i = 0; i < 96; ++i) { prepare_allocation_test(cc, i); result = valkeyClusterConnect2(cc); assert(result == VALKEY_ERR); ASSERT_STR_EQ(cc->errstr, "Out of memory"); } - prepare_allocation_test(cc, 88); + prepare_allocation_test(cc, 96); result = valkeyClusterConnect2(cc); assert(result == VALKEY_OK); } @@ -500,7 +500,7 @@ void test_alloc_failure_handling_async(void) { acc = valkeyClusterAsyncContextInit(); assert(acc); } - acc->cc->flags |= VALKEYCLUSTER_FLAG_ADD_SLAVE; + valkeyClusterSetOptionParseSlaves(acc->cc); // Set callbacks { @@ -527,14 +527,14 @@ void test_alloc_failure_handling_async(void) { // Connect { - for (int i = 0; i < 86; ++i) { + for (int i = 0; i < 94; ++i) { prepare_allocation_test(acc->cc, i); result = valkeyClusterConnect2(acc->cc); assert(result == VALKEY_ERR); ASSERT_STR_EQ(acc->cc->errstr, "Out of memory"); } - prepare_allocation_test(acc->cc, 86); + prepare_allocation_test(acc->cc, 94); result = valkeyClusterConnect2(acc->cc); assert(result == VALKEY_OK); } diff --git a/tests/ut_slotmap_update.c b/tests/ut_slotmap_update.c index a2f43dd..0477337 100644 --- a/tests/ut_slotmap_update.c +++ b/tests/ut_slotmap_update.c @@ -130,7 +130,7 @@ void test_parse_cluster_nodes(bool parse_replicas) { dictIterator di; if (parse_replicas) - cc->flags |= VALKEYCLUSTER_FLAG_ADD_SLAVE; + cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; valkeyReply *reply = create_cluster_nodes_reply( "07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1:30004@31004,hostname4 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238317239 4 connected\n" @@ -384,7 +384,7 @@ void test_parse_cluster_nodes_with_multiple_replicas(void) { dictIterator di; listIter li; - cc->flags |= VALKEYCLUSTER_FLAG_ADD_SLAVE; + cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; valkeyReply *reply = create_cluster_nodes_reply( "07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1:30004@31004,hostname4 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238317239 4 connected\n" @@ -520,7 +520,7 @@ void test_parse_cluster_slots(bool parse_replicas) { dictIterator di; if (parse_replicas) - cc->flags |= VALKEYCLUSTER_FLAG_ADD_SLAVE; + cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; valkeyReply *reply = create_cluster_slots_reply( "[[0, 5460, ['127.0.0.1', 30001, 'e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca', ['hostname', 'localhost']]," @@ -637,7 +637,7 @@ void test_parse_cluster_slots_with_multiple_replicas(void) { dictIterator di; listIter li; - cc->flags |= VALKEYCLUSTER_FLAG_ADD_SLAVE; + cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; valkeyReply *reply = create_cluster_slots_reply( "[[0, 16383, ['127.0.0.1', 30001, 'e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca']," @@ -694,7 +694,7 @@ void test_parse_cluster_slots_with_noncontiguous_slots(void) { dictIterator di; listIter li; - cc->flags |= VALKEYCLUSTER_FLAG_ADD_SLAVE; + cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; valkeyReply *reply = create_cluster_slots_reply( "[[0, 0, ['127.0.0.1', 30001, 'e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca']," From 188e9aba2e427fa1a68e4fbfc54262ef2b88f9ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 16 Oct 2024 15:00:21 +0200 Subject: [PATCH 02/25] Add cluster connect with options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- examples/cluster-async-tls.c | 2 +- examples/cluster-clientside-caching-async.c | 9 +- examples/cluster-simple.c | 11 +- examples/cluster-tls.c | 14 +- include/valkey/cluster.h | 93 ++++++++++---- include/valkey/cluster_tls.h | 8 +- include/valkey/valkey.h | 1 + src/cluster.c | 134 +++++++++++++------- src/cluster_tls.c | 14 +- tests/clusterclient.c | 15 ++- tests/clusterclient_async.c | 2 +- tests/ct_async.c | 2 +- tests/ct_commands.c | 13 +- tests/ct_connection.c | 119 ++++++++--------- tests/ct_connection_ipv6.c | 20 +-- tests/ct_out_of_memory_handling.c | 77 +++-------- tests/ct_pipeline.c | 13 +- tests/ct_specific_nodes.c | 14 +- tests/ut_slotmap_update.c | 63 +++++---- 19 files changed, 328 insertions(+), 296 deletions(-) diff --git a/examples/cluster-async-tls.c b/examples/cluster-async-tls.c index 87a346d..15c1744 100644 --- a/examples/cluster-async-tls.c +++ b/examples/cluster-async-tls.c @@ -73,7 +73,7 @@ int main(int argc, char **argv) { valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE_TLS); valkeyClusterSetOptionRouteUseSlots(acc->cc); valkeyClusterSetOptionParseSlaves(acc->cc); - valkeyClusterSetOptionEnableTLS(acc->cc, tls); + valkeyClusterSetTLSContext(acc->cc, tls); if (valkeyClusterConnect2(acc->cc) != VALKEY_OK) { printf("Error: %s\n", acc->cc->errstr); diff --git a/examples/cluster-clientside-caching-async.c b/examples/cluster-clientside-caching-async.c index 4b8052f..8fdb03d 100644 --- a/examples/cluster-clientside-caching-async.c +++ b/examples/cluster-clientside-caching-async.c @@ -126,11 +126,8 @@ void disconnectCallback(const valkeyAsyncContext *ac, int status) { /* Helper to modify keys using a separate client. */ void modifyKey(const char *key, const char *value) { printf("Modify key: '%s'\n", key); - valkeyClusterContext *cc = valkeyClusterContextInit(); - int status = valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE); - assert(status == VALKEY_OK); - status = valkeyClusterConnect2(cc); - assert(status == VALKEY_OK); + valkeyClusterContext *cc = valkeyClusterConnect(CLUSTER_NODE); + assert(cc); valkeyReply *reply = valkeyClusterCommand(cc, "SET %s %s", key, value); assert(reply != NULL); @@ -150,7 +147,7 @@ int main(int argc, char **argv) { assert(status == VALKEY_OK); status = valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); assert(status == VALKEY_OK); - status = valkeyClusterSetEventCallback(acc->cc, eventCallback, acc); + status = valkeyClusterAsyncSetEventCallback(acc, eventCallback, acc); assert(status == VALKEY_OK); status = valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); assert(status == VALKEY_OK); diff --git a/examples/cluster-simple.c b/examples/cluster-simple.c index 25bfd36..4b8e269 100644 --- a/examples/cluster-simple.c +++ b/examples/cluster-simple.c @@ -8,11 +8,12 @@ int main(int argc, char **argv) { UNUSED(argv); struct timeval timeout = {1, 500000}; // 1.5s - valkeyClusterContext *cc = valkeyClusterContextInit(); - valkeyClusterSetOptionAddNodes(cc, "127.0.0.1:7000"); - valkeyClusterSetOptionConnectTimeout(cc, timeout); - valkeyClusterSetOptionRouteUseSlots(cc); - valkeyClusterConnect2(cc); + valkeyClusterOptions options = {0}; + options.initial_nodes = "127.0.0.1:7000"; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.connect_timeout = &timeout; + + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); if (!cc) { printf("Error: Allocation failure\n"); exit(-1); diff --git a/examples/cluster-tls.c b/examples/cluster-tls.c index 526dadf..9ebab72 100644 --- a/examples/cluster-tls.c +++ b/examples/cluster-tls.c @@ -25,13 +25,13 @@ int main(int argc, char **argv) { struct timeval timeout = {1, 500000}; // 1.5s - valkeyClusterContext *cc = valkeyClusterContextInit(); - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE_TLS); - valkeyClusterSetOptionConnectTimeout(cc, timeout); - valkeyClusterSetOptionRouteUseSlots(cc); - valkeyClusterSetOptionParseSlaves(cc); - valkeyClusterSetOptionEnableTLS(cc, tls); - valkeyClusterConnect2(cc); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_TLS; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.connect_timeout = &timeout; + valkeyClusterOptionsEnableTLS(&options, tls); + + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); if (!cc) { printf("Error: Allocation failure\n"); exit(-1); diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index 111abc8..4ec204a 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -44,7 +44,7 @@ #define VALKEY_ROLE_PRIMARY 1 #define VALKEY_ROLE_REPLICA 2 -/* Events, for valkeyClusterSetEventCallback() */ +/* Events, for valkeyClusterOptionsSetEventCallback() */ #define VALKEYCLUSTER_EVENT_SLOTMAP_UPDATED 1 #define VALKEYCLUSTER_EVENT_READY 2 #define VALKEYCLUSTER_EVENT_FREE_CONTEXT 3 @@ -144,16 +144,67 @@ typedef struct valkeyClusterNodeIterator { char opaque_data[VALKEY_NODE_ITERATOR_SIZE]; } valkeyClusterNodeIterator; -/* - * Synchronous API - */ +/* --- Configuration options --- */ + +/* Enable slotmap updates using the command CLUSTER SLOTS. + * Default is the CLUSTER NODES command. */ +#define VALKEY_OPT_USE_CLUSTER_SLOTS 0x1000 +/* Enable parsing of replica nodes. Currently not used, but the + * information is added to its primary node structure. */ +#define VALKEY_OPT_USE_REPLICAS 0x2000 + +typedef struct { + const char *initial_nodes; /* Initial cluster node address(es). */ + int options; /* Bit field of VALKEY_OPT_xxx */ + const struct timeval *connect_timeout; /* Timeout value for connect, no timeout if NULL. */ + const struct timeval *command_timeout; /* Timeout value for commands, no timeout if NULL. */ + const char *username; /* Authentication username. */ + const char *password; /* Authentication password. */ + int max_retry; /* Allowed retry attempts. */ + + /* Common callbacks. */ + void (*event_callback)(const struct valkeyClusterContext *cc, int event, void *privdata); + void *event_privdata; + + /* Synchronous API callbacks. */ + void (*connect_callback)(const valkeyContext *c, int status); + + /* Asynchronous API callbacks. */ + int (*attach_fn)(valkeyAsyncContext *ac, void *attach_data); + void *attach_data; + valkeyConnectCallback *onConnect; + valkeyDisconnectCallback *onDisconnect; + + /* TLS context, enabled using valkeyClusterOptionsEnableTLS. */ + void *tls; + int (*tls_init_fn)(struct valkeyContext *, struct valkeyTLSContext *); +} valkeyClusterOptions; + +/* Helper functions to set options. */ +int valkeyClusterOptionsSetEventCallback(valkeyClusterOptions *options, + void(fn)(const valkeyClusterContext *cc, + int event, void *privdata), + void *privdata); +/* A hook for connect and reconnect attempts, e.g. for applying additional + * socket options. This is called just after connect, before TLS handshake and + * Valkey authentication. + * + * On successful connection, `status` is set to `VALKEY_OK` and the file + * descriptor can be accessed as `c->fd` to apply socket options. + * + * On failed connection attempt, this callback is called with `status` set to + * `VALKEY_ERR`. The `err` field in the `valkeyContext` can be used to find out + * the cause of the error. */ +int valkeyClusterOptionsSetConnectCallback(valkeyClusterOptions *options, + void(fn)(const valkeyContext *c, + int status)); +/* --- Synchronous API --- */ + +valkeyClusterContext *valkeyClusterConnectWithOptions(const valkeyClusterOptions *options); valkeyClusterContext *valkeyClusterConnect(const char *addrs); -valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, - const struct timeval tv); +valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, const struct timeval tv); int valkeyClusterConnect2(valkeyClusterContext *cc); - -valkeyClusterContext *valkeyClusterContextInit(void); void valkeyClusterFree(valkeyClusterContext *cc); /* Configuration options */ @@ -166,29 +217,11 @@ int valkeyClusterSetOptionParseSlaves(valkeyClusterContext *cc); int valkeyClusterSetOptionRouteUseSlots(valkeyClusterContext *cc); int valkeyClusterSetOptionConnectTimeout(valkeyClusterContext *cc, const struct timeval tv); -int valkeyClusterSetOptionTimeout(valkeyClusterContext *cc, - const struct timeval tv); int valkeyClusterSetOptionMaxRetry(valkeyClusterContext *cc, int max_retry_count); -/* A hook for connect and reconnect attempts, e.g. for applying additional - * socket options. This is called just after connect, before TLS handshake and - * Valkey authentication. - * - * On successful connection, `status` is set to `VALKEY_OK` and the file - * descriptor can be accessed as `c->fd` to apply socket options. - * - * On failed connection attempt, this callback is called with `status` set to - * `VALKEY_ERR`. The `err` field in the `valkeyContext` can be used to find out - * the cause of the error. */ -int valkeyClusterSetConnectCallback(valkeyClusterContext *cc, - void(fn)(const valkeyContext *c, - int status)); -/* A hook for events. */ -int valkeyClusterSetEventCallback(valkeyClusterContext *cc, - void(fn)(const valkeyClusterContext *cc, - int event, void *privdata), - void *privdata); +/* Options configurable in runtime. */ +int valkeyClusterSetOptionTimeout(valkeyClusterContext *cc, const struct timeval tv); /* Blocking * The following functions will block for a reply, or return NULL if there was @@ -262,6 +295,10 @@ int valkeyClusterAsyncSetConnectCallback(valkeyClusterAsyncContext *acc, valkeyConnectCallback *fn); int valkeyClusterAsyncSetDisconnectCallback(valkeyClusterAsyncContext *acc, valkeyDisconnectCallback *fn); +int valkeyClusterAsyncSetEventCallback(valkeyClusterAsyncContext *acc, + void(fn)(const valkeyClusterContext *cc, + int event, void *privdata), + void *privdata); /* Connect and update slotmap, will block until complete. */ valkeyClusterAsyncContext *valkeyClusterAsyncConnect(const char *addrs); diff --git a/include/valkey/cluster_tls.h b/include/valkey/cluster_tls.h index a081f98..e825c26 100644 --- a/include/valkey/cluster_tls.h +++ b/include/valkey/cluster_tls.h @@ -40,8 +40,12 @@ extern "C" { /** * Configuration option to enable TLS negotiation on a context. */ -int valkeyClusterSetOptionEnableTLS(valkeyClusterContext *cc, - valkeyTLSContext *tls); +int valkeyClusterOptionsEnableTLS(valkeyClusterOptions *options, + valkeyTLSContext *tls); + +/* A temporary function until the async context accepts options. */ +int valkeyClusterSetTLSContext(valkeyClusterContext *cc, + valkeyTLSContext *tls); #ifdef __cplusplus } diff --git a/include/valkey/valkey.h b/include/valkey/valkey.h index d6e4fc9..e9e0e00 100644 --- a/include/valkey/valkey.h +++ b/include/valkey/valkey.h @@ -169,6 +169,7 @@ enum valkeyConnectionType { #define VALKEY_OPT_PREFER_IPV4 0x20 /* Prefer IPv4 in DNS lookups. */ #define VALKEY_OPT_PREFER_IPV6 0x40 /* Prefer IPv6 in DNS lookups. */ #define VALKEY_OPT_PREFER_IP_UNSPEC (VALKEY_OPT_PREFER_IPV4 | VALKEY_OPT_PREFER_IPV6) +#define VALKEY_OPT_LAST_SA_OPTION 0x40 /* Last defined standalone option. */ /* In Unix systems a file descriptor is a regular signed int, with -1 * representing an invalid descriptor. In Windows it is a SOCKET diff --git a/src/cluster.c b/src/cluster.c index b04d86a..9bf945c 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -49,6 +49,10 @@ #include #include +/* Make sure standalone and cluster options don't overlap. */ +vk_static_assert(VALKEY_OPT_USE_CLUSTER_SLOTS > VALKEY_OPT_LAST_SA_OPTION); + +/* Internal option flags. */ #define VALKEY_FLAG_USE_CLUSTER_SLOTS 0x1 #define VALKEY_FLAG_PARSE_REPLICAS 0x2 #define VALKEY_FLAG_DISCONNECTING 0x4 @@ -1261,7 +1265,7 @@ int valkeyClusterUpdateSlotmap(valkeyClusterContext *cc) { return VALKEY_ERR; } -valkeyClusterContext *valkeyClusterContextInit(void) { +static valkeyClusterContext *valkeyClusterContextInit(const valkeyClusterOptions *options) { valkeyClusterContext *cc; cc = vk_calloc(1, sizeof(valkeyClusterContext)); @@ -1280,7 +1284,49 @@ valkeyClusterContext *valkeyClusterContextInit(void) { } cc->requests->free = listCommandFree; - cc->max_retry_count = CLUSTER_DEFAULT_MAX_RETRY_COUNT; + if (options->options & VALKEY_OPT_USE_CLUSTER_SLOTS) { + cc->flags |= VALKEY_FLAG_USE_CLUSTER_SLOTS; + } + if (options->options & VALKEY_OPT_USE_REPLICAS) { + cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; + } + if (options->max_retry > 0) { + cc->max_retry_count = options->max_retry; + } else { + cc->max_retry_count = CLUSTER_DEFAULT_MAX_RETRY_COUNT; + } + if (options->initial_nodes != NULL && + valkeyClusterSetOptionAddNodes(cc, options->initial_nodes) != VALKEY_OK) { + return cc; /* err and errstr already set. */ + } + if (options->connect_timeout != NULL && + valkeyClusterSetOptionConnectTimeout(cc, *options->connect_timeout) != VALKEY_OK) { + return cc; /* err and errstr already set. */ + } + if (options->command_timeout != NULL && + valkeyClusterSetOptionTimeout(cc, *options->command_timeout) != VALKEY_OK) { + return cc; /* err and errstr already set. */ + } + if (options->username != NULL && + valkeyClusterSetOptionUsername(cc, options->username) != VALKEY_OK) { + return cc; /* err and errstr already set. */ + } + if (options->password != NULL && + valkeyClusterSetOptionPassword(cc, options->password) != VALKEY_OK) { + return cc; /* err and errstr already set. */ + } + if (options->connect_callback) { + cc->on_connect = options->connect_callback; + } + if (options->event_callback) { + cc->event_callback = options->event_callback; + cc->event_privdata = options->event_privdata; + } + if (options->tls) { + cc->tls = options->tls; + cc->tls_init_fn = options->tls_init_fn; + } + return cc; } @@ -1305,47 +1351,32 @@ void valkeyClusterFree(valkeyClusterContext *cc) { vk_free(cc); } -static valkeyClusterContext * -valkeyClusterConnectInternal(valkeyClusterContext *cc, const char *addrs) { - if (valkeyClusterSetOptionAddNodes(cc, addrs) != VALKEY_OK) { - return cc; +valkeyClusterContext *valkeyClusterConnectWithOptions(const valkeyClusterOptions *options) { + valkeyClusterContext *cc = valkeyClusterContextInit(options); + if (cc == NULL) { + return NULL; + } + /* Only connect if options are ok. */ + if (cc->err == 0) { + valkeyClusterUpdateSlotmap(cc); } - valkeyClusterUpdateSlotmap(cc); return cc; } valkeyClusterContext *valkeyClusterConnect(const char *addrs) { - valkeyClusterContext *cc; - - cc = valkeyClusterContextInit(); - - if (cc == NULL) { - return NULL; - } + valkeyClusterOptions options = {0}; + options.initial_nodes = addrs; - return valkeyClusterConnectInternal(cc, addrs); + return valkeyClusterConnectWithOptions(&options); } valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, const struct timeval tv) { - valkeyClusterContext *cc; - - cc = valkeyClusterContextInit(); - - if (cc == NULL) { - return NULL; - } - - if (cc->connect_timeout == NULL) { - cc->connect_timeout = vk_malloc(sizeof(struct timeval)); - if (cc->connect_timeout == NULL) { - return NULL; - } - } + valkeyClusterOptions options = {0}; + options.initial_nodes = addrs; + options.connect_timeout = &tv; - memcpy(cc->connect_timeout, &tv, sizeof(struct timeval)); - - return valkeyClusterConnectInternal(cc, addrs); + return valkeyClusterConnectWithOptions(&options); } static int valkeyClusterSetOptionAddNode(valkeyClusterContext *cc, const char *addr) { @@ -2136,23 +2167,23 @@ static int prepareCommand(valkeyClusterContext *cc, struct cmd *command) { return VALKEY_OK; } -int valkeyClusterSetConnectCallback(valkeyClusterContext *cc, - void(fn)(const valkeyContext *c, - int status)) { - if (cc->on_connect == NULL) { - cc->on_connect = fn; +int valkeyClusterOptionsSetConnectCallback(valkeyClusterOptions *options, + void(fn)(const valkeyContext *c, + int status)) { + if (options->connect_callback == NULL) { + options->connect_callback = fn; return VALKEY_OK; } return VALKEY_ERR; } -int valkeyClusterSetEventCallback(valkeyClusterContext *cc, - void(fn)(const valkeyClusterContext *cc, - int event, void *privdata), - void *privdata) { - if (cc->event_callback == NULL) { - cc->event_callback = fn; - cc->event_privdata = privdata; +int valkeyClusterOptionsSetEventCallback(valkeyClusterOptions *options, + void(fn)(const valkeyClusterContext *cc, + int event, void *privdata), + void *privdata) { + if (options->event_callback == NULL) { + options->event_callback = fn; + options->event_privdata = privdata; return VALKEY_OK; } return VALKEY_ERR; @@ -2826,7 +2857,8 @@ valkeyClusterAsyncContext *valkeyClusterAsyncContextInit(void) { valkeyClusterContext *cc; valkeyClusterAsyncContext *acc; - cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + cc = valkeyClusterContextInit(&options); if (cc == NULL) { return NULL; } @@ -2884,6 +2916,18 @@ int valkeyClusterAsyncSetDisconnectCallback(valkeyClusterAsyncContext *acc, return VALKEY_ERR; } +int valkeyClusterAsyncSetEventCallback(valkeyClusterAsyncContext *acc, + void(fn)(const valkeyClusterContext *cc, + int event, void *privdata), + void *privdata) { + if (acc->cc->event_callback == NULL) { + acc->cc->event_callback = fn; + acc->cc->event_privdata = privdata; + return VALKEY_OK; + } + return VALKEY_ERR; +} + /* Reply callback function for CLUSTER SLOTS */ void clusterSlotsReplyCallback(valkeyAsyncContext *ac, void *r, void *privdata) { diff --git a/src/cluster_tls.c b/src/cluster_tls.c index 7a0df24..ac92ffa 100644 --- a/src/cluster_tls.c +++ b/src/cluster_tls.c @@ -28,8 +28,18 @@ */ #include "cluster_tls.h" -int valkeyClusterSetOptionEnableTLS(valkeyClusterContext *cc, - valkeyTLSContext *tls) { +int valkeyClusterOptionsEnableTLS(valkeyClusterOptions *options, + valkeyTLSContext *tls) { + if (options == NULL || tls == NULL) { + return VALKEY_ERR; + } + options->tls = tls; + options->tls_init_fn = &valkeyInitiateTLSWithContext; + return VALKEY_OK; +} + +int valkeyClusterSetTLSContext(valkeyClusterContext *cc, + valkeyTLSContext *tls) { if (cc == NULL || tls == NULL) { return VALKEY_ERR; } diff --git a/tests/clusterclient.c b/tests/clusterclient.c index 29b9bbf..a01fd23 100644 --- a/tests/clusterclient.c +++ b/tests/clusterclient.c @@ -88,18 +88,19 @@ int main(int argc, char **argv) { struct timeval timeout = {1, 500000}; // 1.5s - valkeyClusterContext *cc = valkeyClusterContextInit(); - valkeyClusterSetOptionAddNodes(cc, initnode); - valkeyClusterSetOptionConnectTimeout(cc, timeout); + valkeyClusterOptions options = {0}; + options.initial_nodes = initnode; + options.connect_timeout = &timeout; if (use_cluster_slots) { - valkeyClusterSetOptionRouteUseSlots(cc); + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; } if (show_events) { - valkeyClusterSetEventCallback(cc, eventCallback, NULL); + valkeyClusterOptionsSetEventCallback(&options, eventCallback, NULL); } - if (valkeyClusterConnect2(cc) != VALKEY_OK) { - printf("Connect error: %s\n", cc->errstr); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + if (cc == NULL || cc->err) { + printf("Connect error: %s\n", cc ? cc->errstr : "OOM"); exit(2); } diff --git a/tests/clusterclient_async.c b/tests/clusterclient_async.c index d19f68d..de2e4e1 100644 --- a/tests/clusterclient_async.c +++ b/tests/clusterclient_async.c @@ -258,7 +258,7 @@ int main(int argc, char **argv) { valkeyClusterSetOptionRouteUseSlots(acc->cc); } if (show_events) { - valkeyClusterSetEventCallback(acc->cc, eventCallback, NULL); + valkeyClusterAsyncSetEventCallback(acc, eventCallback, NULL); } if (show_connection_events) { valkeyClusterAsyncSetConnectCallback(acc, connectCallback); diff --git a/tests/ct_async.c b/tests/ct_async.c index 6148fdb..aba821d 100644 --- a/tests/ct_async.c +++ b/tests/ct_async.c @@ -72,7 +72,7 @@ int main(void) { status = valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); assert(status == VALKEY_OK); - status = valkeyClusterSetEventCallback(acc->cc, eventCallback, acc); + status = valkeyClusterAsyncSetEventCallback(acc, eventCallback, acc); assert(status == VALKEY_OK); status = valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); assert(status == VALKEY_OK); diff --git a/tests/ct_commands.c b/tests/ct_commands.c index 2d462bf..cf3e5e3 100644 --- a/tests/ct_commands.c +++ b/tests/ct_commands.c @@ -462,15 +462,12 @@ void test_multi(valkeyClusterContext *cc) { int main(void) { struct timeval timeout = {0, 500000}; - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.connect_timeout = &timeout; - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE); - valkeyClusterSetOptionConnectTimeout(cc, timeout); - - int status; - status = valkeyClusterConnect2(cc); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + ASSERT_MSG(cc && cc->err == 0, cc ? cc->errstr : "OOM"); load_valkey_version(cc); test_bitfield(cc); diff --git a/tests/ct_connection.c b/tests/ct_connection.c index 7dff73e..01d62d0 100644 --- a/tests/ct_connection.c +++ b/tests/ct_connection.c @@ -29,15 +29,13 @@ void reset_counters(void) { // Connecting to a password protected cluster and // providing a correct password. void test_password_ok(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE_WITH_PASSWORD); - valkeyClusterSetOptionPassword(cc, CLUSTER_PASSWORD); - valkeyClusterSetConnectCallback(cc, connect_callback); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.password = CLUSTER_PASSWORD; + options.connect_callback = connect_callback; - int status; - status = valkeyClusterConnect2(cc); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + ASSERT_MSG(cc && cc->err == 0, cc ? cc->errstr : "OOM"); assert(connect_success_counter == 1); // for CLUSTER NODES load_valkey_version(cc); assert(connect_success_counter == 2); // for checking valkey version @@ -58,14 +56,12 @@ void test_password_ok(void) { // Connecting to a password protected cluster and // providing wrong password. void test_password_wrong(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE_WITH_PASSWORD); - valkeyClusterSetOptionPassword(cc, "faultypass"); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.password = "faultypass"; - int status; - status = valkeyClusterConnect2(cc); - assert(status == VALKEY_ERR); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + assert(cc); assert(cc->err == VALKEY_ERR_OTHER); if (valkey_version_less_than(6, 0)) @@ -79,14 +75,12 @@ void test_password_wrong(void) { // Connecting to a password protected cluster and // not providing any password. void test_password_missing(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE_WITH_PASSWORD); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + // No password set. - // A password is not configured.. - int status; - status = valkeyClusterConnect2(cc); - assert(status == VALKEY_ERR); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + assert(cc); assert(cc->err == VALKEY_ERR_OTHER); assert(strncmp(cc->errstr, "NOAUTH", 6) == 0); @@ -101,14 +95,13 @@ void test_username_ok(void) { return; // Connect to the cluster using username and password - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE_WITH_PASSWORD); - valkeyClusterSetOptionUsername(cc, CLUSTER_USERNAME); - valkeyClusterSetOptionPassword(cc, CLUSTER_PASSWORD); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.username = CLUSTER_USERNAME; + options.password = CLUSTER_PASSWORD; - int ret = valkeyClusterConnect2(cc); - ASSERT_MSG(ret == VALKEY_OK, cc->errstr); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + ASSERT_MSG(cc && cc->err == 0, cc ? cc->errstr : "OOM"); // Test connection valkeyReply *reply = valkeyClusterCommand(cc, "SET key1 Hello"); @@ -123,21 +116,20 @@ void test_username_disabled(void) { if (valkey_version_less_than(6, 0)) return; - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE_WITH_PASSWORD); - valkeyClusterSetOptionUsername(cc, "missing-user"); - valkeyClusterSetOptionPassword(cc, CLUSTER_PASSWORD); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.username = "missing-user"; + options.password = CLUSTER_PASSWORD; // Connect using 'AUTH ' should fail - int ret = valkeyClusterConnect2(cc); - assert(ret == VALKEY_ERR); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + assert(cc); assert(cc->err == VALKEY_ERR_OTHER); assert(strncmp(cc->errstr, "WRONGPASS invalid username-password pair", 40) == 0); // Disable use of username (2 alternatives) - ret = valkeyClusterSetOptionUsername(cc, NULL); + int ret = valkeyClusterSetOptionUsername(cc, NULL); ASSERT_MSG(ret == VALKEY_OK, cc->errstr); ret = valkeyClusterSetOptionUsername(cc, ""); ASSERT_MSG(ret == VALKEY_OK, cc->errstr); @@ -156,23 +148,20 @@ void test_username_disabled(void) { // Connect and handle two clusters simultaneously void test_multicluster(void) { - int ret; valkeyReply *reply; // Connect to first cluster - valkeyClusterContext *cc1 = valkeyClusterContextInit(); + valkeyClusterContext *cc1 = valkeyClusterConnect(CLUSTER_NODE); assert(cc1); - valkeyClusterSetOptionAddNodes(cc1, CLUSTER_NODE); - ret = valkeyClusterConnect2(cc1); - ASSERT_MSG(ret == VALKEY_OK, cc1->errstr); + ASSERT_MSG(cc1->err == 0, cc1->errstr); // Connect to second cluster - valkeyClusterContext *cc2 = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.password = CLUSTER_PASSWORD; + valkeyClusterContext *cc2 = valkeyClusterConnectWithOptions(&options); assert(cc2); - valkeyClusterSetOptionAddNodes(cc2, CLUSTER_NODE_WITH_PASSWORD); - valkeyClusterSetOptionPassword(cc2, CLUSTER_PASSWORD); - ret = valkeyClusterConnect2(cc2); - ASSERT_MSG(ret == VALKEY_OK, cc2->errstr); + ASSERT_MSG(cc2->err == 0, cc2->errstr); // Set keys differently in clusters reply = valkeyClusterCommand(cc1, "SET key Hello1"); @@ -207,16 +196,14 @@ void test_multicluster(void) { void test_connect_timeout(void) { struct timeval timeout = {0, 200000}; - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - /* Configure a non-routable IP address and a timeout */ - valkeyClusterSetOptionAddNodes(cc, "192.168.0.0:7000"); - valkeyClusterSetOptionConnectTimeout(cc, timeout); - valkeyClusterSetConnectCallback(cc, connect_callback); + valkeyClusterOptions options = {0}; + options.initial_nodes = "192.168.0.0:7000"; + options.connect_timeout = &timeout; + options.connect_callback = connect_callback; - int status = valkeyClusterConnect2(cc); - assert(status == VALKEY_ERR); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + assert(cc); assert(cc->err == VALKEY_ERR_IO); assert(strcmp(cc->errstr, "Connection timed out") == 0); assert(connect_success_counter == 0); @@ -230,13 +217,12 @@ void test_connect_timeout(void) { void test_command_timeout(void) { struct timeval timeout = {0, 10000}; - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE); - valkeyClusterSetOptionTimeout(cc, timeout); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.command_timeout = &timeout; - int status = valkeyClusterConnect2(cc); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + ASSERT_MSG(cc && cc->err == 0, cc ? cc->errstr : "OOM"); valkeyClusterNodeIterator ni; valkeyClusterInitNodeIterator(&ni, cc); @@ -263,14 +249,8 @@ void test_command_timeout(void) { /* Connect and configure a command timeout while connected. */ void test_command_timeout_set_while_connected(void) { - struct timeval timeout = {0, 10000}; - - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE); - - int status = valkeyClusterConnect2(cc); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); + valkeyClusterContext *cc = valkeyClusterConnect(CLUSTER_NODE); + ASSERT_MSG(cc && cc->err == 0, cc ? cc->errstr : "OOM"); valkeyClusterNodeIterator ni; valkeyClusterInitNodeIterator(&ni, cc); @@ -283,6 +263,7 @@ void test_command_timeout_set_while_connected(void) { freeReplyObject(reply); /* Set command timeout while connected */ + struct timeval timeout = {0, 10000}; valkeyClusterSetOptionTimeout(cc, timeout); reply = valkeyClusterCommandToNode(cc, node, "DEBUG SLEEP 0.2"); diff --git a/tests/ct_connection_ipv6.c b/tests/ct_connection_ipv6.c index aeee635..39711f3 100644 --- a/tests/ct_connection_ipv6.c +++ b/tests/ct_connection_ipv6.c @@ -10,23 +10,15 @@ // Successful connection an IPv6 cluster void test_successful_ipv6_connection(void) { - - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - - int status; struct timeval timeout = {0, 500000}; // 0.5s - status = valkeyClusterSetOptionConnectTimeout(cc, timeout); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); - - status = valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE_IPV6); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); - status = valkeyClusterSetOptionRouteUseSlots(cc); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_IPV6; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.connect_timeout = &timeout; - status = valkeyClusterConnect2(cc); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + ASSERT_MSG(cc && cc->err == 0, cc ? cc->errstr : "OOM"); valkeyReply *reply; reply = (valkeyReply *)valkeyClusterCommand(cc, "SET key_ipv6 value"); diff --git a/tests/ct_out_of_memory_handling.c b/tests/ct_out_of_memory_handling.c index e65a4cf..e1af517 100644 --- a/tests/ct_out_of_memory_handling.c +++ b/tests/ct_out_of_memory_handling.c @@ -114,75 +114,34 @@ void test_alloc_failure_handling(void) { // Override allocators valkeySetAllocators(&ha); - // Context init + struct timeval timeout = {0, 500000}; + + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_USE_REPLICAS; + options.connect_timeout = &timeout; + options.command_timeout = &timeout; + + // Connect valkeyClusterContext *cc; { for (int i = 0; i < 3; ++i) { successfulAllocations = i; - cc = valkeyClusterContextInit(); + cc = valkeyClusterConnectWithOptions(&options); assert(cc == NULL); } - successfulAllocations = 3; - cc = valkeyClusterContextInit(); - assert(cc); - } - valkeyClusterSetOptionParseSlaves(cc); - - // Add nodes - { - for (int i = 0; i < 9; ++i) { - prepare_allocation_test(cc, i); - result = valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE); - assert(result == VALKEY_ERR); - ASSERT_STR_EQ(cc->errstr, "Out of memory"); - } - - prepare_allocation_test(cc, 9); - result = valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE); - assert(result == VALKEY_OK); - } - - // Set connect timeout - { - struct timeval timeout = {0, 500000}; - - prepare_allocation_test(cc, 0); - result = valkeyClusterSetOptionConnectTimeout(cc, timeout); - assert(result == VALKEY_ERR); - ASSERT_STR_EQ(cc->errstr, "Out of memory"); - - prepare_allocation_test(cc, 1); - result = valkeyClusterSetOptionConnectTimeout(cc, timeout); - assert(result == VALKEY_OK); - } - - // Set request timeout - { - struct timeval timeout = {0, 500000}; - - prepare_allocation_test(cc, 0); - result = valkeyClusterSetOptionTimeout(cc, timeout); - assert(result == VALKEY_ERR); - ASSERT_STR_EQ(cc->errstr, "Out of memory"); - - prepare_allocation_test(cc, 1); - result = valkeyClusterSetOptionTimeout(cc, timeout); - assert(result == VALKEY_OK); - } - - // Connect - { - for (int i = 0; i < 96; ++i) { - prepare_allocation_test(cc, i); - result = valkeyClusterConnect2(cc); - assert(result == VALKEY_ERR); + for (int i = 3; i < 110; ++i) { + successfulAllocations = i; + cc = valkeyClusterConnectWithOptions(&options); + assert(cc); ASSERT_STR_EQ(cc->errstr, "Out of memory"); + valkeyClusterFree(cc); } - prepare_allocation_test(cc, 96); - result = valkeyClusterConnect2(cc); - assert(result == VALKEY_OK); + successfulAllocations = 110; + cc = valkeyClusterConnectWithOptions(&options); + assert(cc && cc->err == 0); } // Command diff --git a/tests/ct_pipeline.c b/tests/ct_pipeline.c index 5fe8cc5..c723308 100644 --- a/tests/ct_pipeline.c +++ b/tests/ct_pipeline.c @@ -12,16 +12,13 @@ // Test of two pipelines using sync API void test_pipeline(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; - int status; - status = valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); - - status = valkeyClusterConnect2(cc); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + ASSERT_MSG(cc && cc->err == 0, cc ? cc->errstr : "OOM"); + int status; status = valkeyClusterAppendCommand(cc, "SET foo one"); ASSERT_MSG(status == VALKEY_OK, cc->errstr); status = valkeyClusterAppendCommand(cc, "SET bar two"); diff --git a/tests/ct_specific_nodes.c b/tests/ct_specific_nodes.c index 8793cb4..b09f260 100644 --- a/tests/ct_specific_nodes.c +++ b/tests/ct_specific_nodes.c @@ -509,15 +509,13 @@ void test_async_transaction(void) { } int main(void) { - int status; + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.max_retry = 1; - valkeyClusterContext *cc = valkeyClusterContextInit(); - assert(cc); - valkeyClusterSetOptionAddNodes(cc, CLUSTER_NODE); - valkeyClusterSetOptionRouteUseSlots(cc); - valkeyClusterSetOptionMaxRetry(cc, 1); - status = valkeyClusterConnect2(cc); - ASSERT_MSG(status == VALKEY_OK, cc->errstr); + valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); + ASSERT_MSG(cc && cc->err == 0, cc ? cc->errstr : "OOM"); load_valkey_version(cc); // Synchronous API diff --git a/tests/ut_slotmap_update.c b/tests/ut_slotmap_update.c index 0477337..96c4198 100644 --- a/tests/ut_slotmap_update.c +++ b/tests/ut_slotmap_update.c @@ -123,15 +123,16 @@ char *resp_encode_array(char *p, sds *resp) { /* Parse a cluster nodes reply from a basic deployment. */ void test_parse_cluster_nodes(bool parse_replicas) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + if (parse_replicas) + options.options |= VALKEY_OPT_USE_REPLICAS; + + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyContext *c = valkeyContextInit(); valkeyClusterNode *node; cluster_slot *slot; dictIterator di; - if (parse_replicas) - cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; - valkeyReply *reply = create_cluster_nodes_reply( "07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1:30004@31004,hostname4 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238317239 4 connected\n" "67ed2db8d677e59ec4a4cefb06858cf2a1a89fa1 127.0.0.1:30002@31002,hostname2 master - 0 1426238316232 2 connected 5461-10922\n" @@ -209,7 +210,8 @@ void test_parse_cluster_nodes(bool parse_replicas) { } void test_parse_cluster_nodes_during_failover(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyContext *c = valkeyContextInit(); valkeyClusterNode *node; cluster_slot *slot; @@ -274,7 +276,8 @@ void test_parse_cluster_nodes_during_failover(void) { /* Skip nodes with the `noaddr` flag. */ void test_parse_cluster_nodes_with_noaddr(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyContext *c = valkeyContextInit(); valkeyClusterNode *node; dictIterator di; @@ -302,7 +305,8 @@ void test_parse_cluster_nodes_with_noaddr(void) { } void test_parse_cluster_nodes_with_empty_ip(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyClusterNode *node; dictIterator di; @@ -337,7 +341,8 @@ void test_parse_cluster_nodes_with_empty_ip(void) { /* Parse replies with additional importing and migrating information. */ void test_parse_cluster_nodes_with_special_slot_entries(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyContext *c = valkeyContextInit(); valkeyClusterNode *node; cluster_slot *slot; @@ -377,15 +382,16 @@ void test_parse_cluster_nodes_with_special_slot_entries(void) { /* Parse a cluster nodes reply containing a primary with multiple replicas. */ void test_parse_cluster_nodes_with_multiple_replicas(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + options.options |= VALKEY_OPT_USE_REPLICAS; + + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyContext *c = valkeyContextInit(); valkeyClusterNode *node; cluster_slot *slot; dictIterator di; listIter li; - cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; - valkeyReply *reply = create_cluster_nodes_reply( "07c37dfeb235213a872192d90877d0cd55635b91 127.0.0.1:30004@31004,hostname4 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238317239 4 connected\n" "6ec23923021cf3ffec47632106199cb7f496ce01 127.0.0.1:30005@31005,hostname5 slave e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 0 1426238316232 5 connected\n" @@ -442,7 +448,8 @@ void test_parse_cluster_nodes_with_multiple_replicas(void) { /* Give error when parsing erroneous data. */ void test_parse_cluster_nodes_with_parse_error(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyContext *c = valkeyContextInit(); valkeyReply *reply; dict *nodes; @@ -490,7 +497,8 @@ void test_parse_cluster_nodes_with_parse_error(void) { /* Redis pre-v4.0 returned node addresses without the clusterbus port, * i.e. `ip:port` instead of `ip:port@cport` */ void test_parse_cluster_nodes_with_legacy_format(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyContext *c = valkeyContextInit(); valkeyClusterNode *node; dictIterator di; @@ -514,14 +522,15 @@ void test_parse_cluster_nodes_with_legacy_format(void) { /* Parse a cluster slots reply from a basic deployment. */ void test_parse_cluster_slots(bool parse_replicas) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + if (parse_replicas) + options.options |= VALKEY_OPT_USE_REPLICAS; + + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyClusterNode *node; cluster_slot *slot; dictIterator di; - if (parse_replicas) - cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; - valkeyReply *reply = create_cluster_slots_reply( "[[0, 5460, ['127.0.0.1', 30001, 'e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca', ['hostname', 'localhost']]," " ['127.0.0.1', 30004, '07c37dfeb235213a872192d90877d0cd55635b91', ['hostname', 'localhost']]]," @@ -596,7 +605,8 @@ void test_parse_cluster_slots(bool parse_replicas) { } void test_parse_cluster_slots_with_empty_ip(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyReply *reply = create_cluster_slots_reply( "[[0, 5460, ['', 6379, 'e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca']]," @@ -613,7 +623,8 @@ void test_parse_cluster_slots_with_empty_ip(void) { } void test_parse_cluster_slots_with_null_ip(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyReply *reply = create_cluster_slots_reply( "[[0, 5460, [null, 6379, 'e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca']]," @@ -631,14 +642,15 @@ void test_parse_cluster_slots_with_null_ip(void) { /* Parse a cluster slots reply containing a primary with multiple replicas. */ void test_parse_cluster_slots_with_multiple_replicas(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + options.options |= VALKEY_OPT_USE_REPLICAS; + + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyClusterNode *node; cluster_slot *slot; dictIterator di; listIter li; - cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; - valkeyReply *reply = create_cluster_slots_reply( "[[0, 16383, ['127.0.0.1', 30001, 'e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca']," " ['127.0.0.1', 30004, '07c37dfeb235213a872192d90877d0cd55635b91']," @@ -688,14 +700,15 @@ void test_parse_cluster_slots_with_multiple_replicas(void) { } void test_parse_cluster_slots_with_noncontiguous_slots(void) { - valkeyClusterContext *cc = valkeyClusterContextInit(); + valkeyClusterOptions options = {0}; + options.options |= VALKEY_OPT_USE_REPLICAS; + + valkeyClusterContext *cc = valkeyClusterContextInit(&options); valkeyClusterNode *node; cluster_slot *slot; dictIterator di; listIter li; - cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; - valkeyReply *reply = create_cluster_slots_reply( "[[0, 0, ['127.0.0.1', 30001, 'e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca']," " ['127.0.0.1', 30004, '07c37dfeb235213a872192d90877d0cd55635b91']]," From 0403b393a148f16519af460507ea13b9b9937bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 16 Oct 2024 15:02:14 +0200 Subject: [PATCH 03/25] Add async cluster connect with options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- examples/cluster-async-tls.c | 27 ++- examples/cluster-async.c | 39 +++-- examples/cluster-clientside-caching-async.c | 22 ++- include/valkey/adapters/ae.h | 11 +- include/valkey/adapters/glib.h | 10 +- include/valkey/adapters/ivykis.h | 8 +- include/valkey/adapters/libev.h | 10 +- include/valkey/adapters/libevent.h | 11 +- include/valkey/adapters/libhv.h | 10 +- include/valkey/adapters/libsdevent.h | 10 +- include/valkey/adapters/libuv.h | 11 +- include/valkey/adapters/macosx.h | 10 +- include/valkey/adapters/poll.h | 6 +- include/valkey/cluster.h | 25 ++- include/valkey/cluster_tls.h | 4 - src/cluster.c | 59 +++---- src/cluster_tls.c | 12 -- tests/clusterclient_async.c | 30 ++-- tests/clusterclient_reconnect_async.c | 14 +- tests/ct_async.c | 32 ++-- tests/ct_async_glib.c | 15 +- tests/ct_async_libev.c | 14 +- tests/ct_async_libuv.c | 17 +- tests/ct_connection.c | 184 +++++++++----------- tests/ct_out_of_memory_handling.c | 72 +++----- tests/ct_pipeline.c | 20 +-- tests/ct_specific_nodes.c | 120 ++++++------- 27 files changed, 356 insertions(+), 447 deletions(-) diff --git a/examples/cluster-async-tls.c b/examples/cluster-async-tls.c index 15c1744..c61c1bf 100644 --- a/examples/cluster-async-tls.c +++ b/examples/cluster-async-tls.c @@ -66,23 +66,22 @@ int main(int argc, char **argv) { exit(1); } - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE_TLS); - valkeyClusterSetOptionRouteUseSlots(acc->cc); - valkeyClusterSetOptionParseSlaves(acc->cc); - valkeyClusterSetTLSContext(acc->cc, tls); - - if (valkeyClusterConnect2(acc->cc) != VALKEY_OK) { - printf("Error: %s\n", acc->cc->errstr); + struct event_base *base = event_base_new(); + + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_TLS; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsEnableTLS(&options, tls); + valkeyClusterOptionsUseLibevent(&options, base); + + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + if (acc == NULL || acc->err != 0) { + printf("Error: %s\n", acc ? acc->errstr : "OOM"); exit(-1); } - struct event_base *base = event_base_new(); - valkeyClusterLibeventAttach(acc, base); - int status; status = valkeyClusterAsyncCommand(acc, setCallback, (char *)"THE_ID", "SET %s %s", "key", "value"); diff --git a/examples/cluster-async.c b/examples/cluster-async.c index 847828b..ef179b3 100644 --- a/examples/cluster-async.c +++ b/examples/cluster-async.c @@ -49,52 +49,55 @@ void disconnectCallback(const valkeyAsyncContext *ac, int status) { int main(int argc, char **argv) { (void)argc; (void)argv; + struct event_base *base = event_base_new(); + + valkeyClusterOptions options = {0}; + options.initial_nodes = "127.0.0.1:7000"; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); + printf("Connecting...\n"); - valkeyClusterAsyncContext *cc = valkeyClusterAsyncConnect("127.0.0.1:7000"); - if (!cc) { + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + if (!acc) { printf("Error: Allocation failure\n"); exit(-1); - } else if (cc->err) { - printf("Error: %s\n", cc->errstr); + } else if (acc->err) { + printf("Error: %s\n", acc->errstr); // handle error exit(-1); } - struct event_base *base = event_base_new(); - valkeyClusterLibeventAttach(cc, base); - valkeyClusterAsyncSetConnectCallback(cc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(cc, disconnectCallback); - int status; - status = valkeyClusterAsyncCommand(cc, setCallback, (char *)"THE_ID", + status = valkeyClusterAsyncCommand(acc, setCallback, (char *)"THE_ID", "SET %s %s", "key", "value"); if (status != VALKEY_OK) { - printf("error: err=%d errstr=%s\n", cc->err, cc->errstr); + printf("error: err=%d errstr=%s\n", acc->err, acc->errstr); } - status = valkeyClusterAsyncCommand(cc, getCallback, (char *)"THE_ID", + status = valkeyClusterAsyncCommand(acc, getCallback, (char *)"THE_ID", "GET %s", "key"); if (status != VALKEY_OK) { - printf("error: err=%d errstr=%s\n", cc->err, cc->errstr); + printf("error: err=%d errstr=%s\n", acc->err, acc->errstr); } - status = valkeyClusterAsyncCommand(cc, setCallback, (char *)"THE_ID", + status = valkeyClusterAsyncCommand(acc, setCallback, (char *)"THE_ID", "SET %s %s", "key2", "value2"); if (status != VALKEY_OK) { - printf("error: err=%d errstr=%s\n", cc->err, cc->errstr); + printf("error: err=%d errstr=%s\n", acc->err, acc->errstr); } - status = valkeyClusterAsyncCommand(cc, getCallback, (char *)"THE_ID", + status = valkeyClusterAsyncCommand(acc, getCallback, (char *)"THE_ID", "GET %s", "key2"); if (status != VALKEY_OK) { - printf("error: err=%d errstr=%s\n", cc->err, cc->errstr); + printf("error: err=%d errstr=%s\n", acc->err, acc->errstr); } printf("Dispatch..\n"); event_base_dispatch(base); printf("Done..\n"); - valkeyClusterAsyncFree(cc); + valkeyClusterAsyncFree(acc); event_base_free(base); return 0; } diff --git a/examples/cluster-clientside-caching-async.c b/examples/cluster-clientside-caching-async.c index 8fdb03d..ab4f11f 100644 --- a/examples/cluster-clientside-caching-async.c +++ b/examples/cluster-clientside-caching-async.c @@ -139,24 +139,22 @@ void modifyKey(const char *key, const char *value) { int main(int argc, char **argv) { (void)argc; (void)argv; - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); + struct event_base *base = event_base_new(); + + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); + + valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(&options); assert(acc); int status; - status = valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - assert(status == VALKEY_OK); - status = valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - assert(status == VALKEY_OK); status = valkeyClusterAsyncSetEventCallback(acc, eventCallback, acc); assert(status == VALKEY_OK); - status = valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - assert(status == VALKEY_OK); - - struct event_base *base = event_base_new(); - status = valkeyClusterLibeventAttach(acc, base); - assert(status == VALKEY_OK); - status = valkeyClusterAsyncConnect2(acc); + status = valkeyClusterAsyncConnect(acc); assert(status == VALKEY_OK); event_base_dispatch(base); diff --git a/include/valkey/adapters/ae.h b/include/valkey/adapters/ae.h index 5d8849d..7c3df00 100644 --- a/include/valkey/adapters/ae.h +++ b/include/valkey/adapters/ae.h @@ -140,14 +140,15 @@ static int valkeyAeAttachAdapter(valkeyAsyncContext *ac, void *loop) { } VALKEY_UNUSED -static int valkeyClusterAeAttach(valkeyClusterAsyncContext *acc, - aeEventLoop *loop) { - if (acc == NULL || loop == NULL) { +static int valkeyClusterOptionsUseAe(valkeyClusterOptions *options, + aeEventLoop *loop) { + if (options == NULL || loop == NULL) { return VALKEY_ERR; } - acc->attach_fn = valkeyAeAttachAdapter; - acc->attach_data = loop; + options->attach_fn = valkeyAeAttachAdapter; + options->attach_data = loop; return VALKEY_OK; } + #endif /* VALKEY_ADAPTERS_AE_H */ diff --git a/include/valkey/adapters/glib.h b/include/valkey/adapters/glib.h index acd0412..b471b65 100644 --- a/include/valkey/adapters/glib.h +++ b/include/valkey/adapters/glib.h @@ -153,14 +153,14 @@ static int valkeyGlibAttachAdapter(valkeyAsyncContext *ac, void *context) { } VALKEY_UNUSED -static int valkeyClusterGlibAttach(valkeyClusterAsyncContext *acc, - GMainContext *context) { - if (acc == NULL) { // A NULL context is accepted. +static int valkeyClusterOptionsUseGlib(valkeyClusterOptions *options, + GMainContext *context) { + if (options == NULL) { // A NULL context is accepted. return VALKEY_ERR; } - acc->attach_fn = valkeyGlibAttachAdapter; - acc->attach_data = context; + options->attach_fn = valkeyGlibAttachAdapter; + options->attach_data = context; return VALKEY_OK; } diff --git a/include/valkey/adapters/ivykis.h b/include/valkey/adapters/ivykis.h index cccdb22..a0bf0b8 100644 --- a/include/valkey/adapters/ivykis.h +++ b/include/valkey/adapters/ivykis.h @@ -85,17 +85,17 @@ static int valkeyIvykisAttach(valkeyAsyncContext *ac) { } /* Internal adapter function with correct function signature. */ -static int valkeyClusterIvykisAttachAdapter(valkeyAsyncContext *ac, VALKEY_UNUSED void *) { +static int valkeyIvykisAttachAdapter(valkeyAsyncContext *ac, VALKEY_UNUSED void *) { return valkeyIvykisAttach(ac); } VALKEY_UNUSED -static int valkeyClusterIvykisAttach(valkeyClusterAsyncContext *acc) { - if (acc == NULL) { +static int valkeyClusterOptionsUseIvykis(valkeyClusterOptions *options) { + if (options == NULL) { return VALKEY_ERR; } - acc->attach_fn = valkeyClusterIvykisAttachAdapter; + options->attach_fn = valkeyIvykisAttachAdapter; return VALKEY_OK; } diff --git a/include/valkey/adapters/libev.h b/include/valkey/adapters/libev.h index 1a6ee28..cc96d8b 100644 --- a/include/valkey/adapters/libev.h +++ b/include/valkey/adapters/libev.h @@ -193,14 +193,14 @@ static int valkeyLibevAttachAdapter(valkeyAsyncContext *ac, void *loop) { } VALKEY_UNUSED -static int valkeyClusterLibevAttach(valkeyClusterAsyncContext *acc, - struct ev_loop *loop) { - if (acc == NULL || loop == NULL) { +static int valkeyClusterOptionsUseLibev(valkeyClusterOptions *options, + struct ev_loop *loop) { + if (options == NULL || loop == NULL) { return VALKEY_ERR; } - acc->attach_fn = valkeyLibevAttachAdapter; - acc->attach_data = loop; + options->attach_fn = valkeyLibevAttachAdapter; + options->attach_data = loop; return VALKEY_OK; } diff --git a/include/valkey/adapters/libevent.h b/include/valkey/adapters/libevent.h index d2ab4fd..c063486 100644 --- a/include/valkey/adapters/libevent.h +++ b/include/valkey/adapters/libevent.h @@ -182,14 +182,15 @@ static int valkeyLibeventAttachAdapter(valkeyAsyncContext *ac, void *base) { } VALKEY_UNUSED -static int valkeyClusterLibeventAttach(valkeyClusterAsyncContext *acc, - struct event_base *base) { - if (acc == NULL || base == NULL) { +static int valkeyClusterOptionsUseLibevent(valkeyClusterOptions *options, + struct event_base *base) { + if (options == NULL || base == NULL) { return VALKEY_ERR; } - acc->attach_fn = valkeyLibeventAttachAdapter; - acc->attach_data = base; + options->attach_fn = valkeyLibeventAttachAdapter; + options->attach_data = base; return VALKEY_OK; } + #endif /* VALKEY_ADAPTERS_LIBEVENT_H */ diff --git a/include/valkey/adapters/libhv.h b/include/valkey/adapters/libhv.h index 5a13aab..d73ec1a 100644 --- a/include/valkey/adapters/libhv.h +++ b/include/valkey/adapters/libhv.h @@ -129,14 +129,14 @@ static int valkeyLibhvAttachAdapter(valkeyAsyncContext *ac, void *loop) { } VALKEY_UNUSED -static int valkeyClusterLibhvAttach(valkeyClusterAsyncContext *acc, - hloop_t *loop) { - if (acc == NULL || loop == NULL) { +static int valkeyClusterOptionsUseLibhv(valkeyClusterOptions *options, + hloop_t *loop) { + if (options == NULL || loop == NULL) { return VALKEY_ERR; } - acc->attach_fn = valkeyLibhvAttachAdapter; - acc->attach_data = loop; + options->attach_fn = valkeyLibhvAttachAdapter; + options->attach_data = loop; return VALKEY_OK; } diff --git a/include/valkey/adapters/libsdevent.h b/include/valkey/adapters/libsdevent.h index 25fb29d..5115186 100644 --- a/include/valkey/adapters/libsdevent.h +++ b/include/valkey/adapters/libsdevent.h @@ -184,14 +184,14 @@ static int valkeyLibsdeventAttachAdapter(valkeyAsyncContext *ac, void *event) { } VALKEY_UNUSED -static int valkeyClusterLibsdeventAttach(valkeyClusterAsyncContext *acc, - struct sd_event *event) { - if (acc == NULL || event == NULL) { +static int valkeyClusterOptionsUseLibsdevent(valkeyClusterOptions *options, + struct sd_event *event) { + if (options == NULL || event == NULL) { return VALKEY_ERR; } - acc->attach_fn = valkeyLibsdeventAttachAdapter; - acc->attach_data = event; + options->attach_fn = valkeyLibsdeventAttachAdapter; + options->attach_data = event; return VALKEY_OK; } diff --git a/include/valkey/adapters/libuv.h b/include/valkey/adapters/libuv.h index d443e19..716e7e6 100644 --- a/include/valkey/adapters/libuv.h +++ b/include/valkey/adapters/libuv.h @@ -203,14 +203,15 @@ static int valkeyLibuvAttachAdapter(valkeyAsyncContext *ac, void *loop) { } VALKEY_UNUSED -static int valkeyClusterLibuvAttach(valkeyClusterAsyncContext *acc, - uv_loop_t *loop) { - if (acc == NULL || loop == NULL) { +static int valkeyClusterOptionsUseLibuv(valkeyClusterOptions *options, + uv_loop_t *loop) { + if (options == NULL || loop == NULL) { return VALKEY_ERR; } - acc->attach_fn = valkeyLibuvAttachAdapter; - acc->attach_data = loop; + options->attach_fn = valkeyLibuvAttachAdapter; + options->attach_data = loop; return VALKEY_OK; } + #endif /* VALKEY_ADAPTERS_LIBUV_H */ diff --git a/include/valkey/adapters/macosx.h b/include/valkey/adapters/macosx.h index 962e2b7..7816441 100644 --- a/include/valkey/adapters/macosx.h +++ b/include/valkey/adapters/macosx.h @@ -149,14 +149,14 @@ static int valkeyMacOSAttachAdapter(valkeyAsyncContext *ac, void *loop) { } VALKEY_UNUSED -static int valkeyClusterMacOSAttach(valkeyClusterAsyncContext *acc, - CFRunLoopRef loop) { - if (acc == NULL || loop == NULL) { +static int valkeyClusterOptionsUseMacOS(valkeyClusterOptions *options, + CFRunLoopRef loop) { + if (options == NULL || loop == NULL) { return VALKEY_ERR; } - acc->attach_fn = valkeyMacOSAttachAdapter; - acc->attach_data = loop; + options->attach_fn = valkeyMacOSAttachAdapter; + options->attach_data = loop; return VALKEY_OK; } diff --git a/include/valkey/adapters/poll.h b/include/valkey/adapters/poll.h index 544a253..5035e07 100644 --- a/include/valkey/adapters/poll.h +++ b/include/valkey/adapters/poll.h @@ -202,12 +202,12 @@ static int valkeyPollAttachAdapter(valkeyAsyncContext *ac, VALKEY_UNUSED void *u } VALKEY_UNUSED -static int valkeyClusterPollAttach(valkeyClusterAsyncContext *acc) { - if (acc == NULL) { +static int valkeyClusterOptionsUsePoll(valkeyClusterOptions *options) { + if (options == NULL) { return VALKEY_ERR; } - acc->attach_fn = valkeyPollAttachAdapter; + options->attach_fn = valkeyPollAttachAdapter; return VALKEY_OK; } diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index 4ec204a..16c80b2 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -170,10 +170,10 @@ typedef struct { void (*connect_callback)(const valkeyContext *c, int status); /* Asynchronous API callbacks. */ - int (*attach_fn)(valkeyAsyncContext *ac, void *attach_data); + valkeyConnectCallback *async_connect_cb; + valkeyDisconnectCallback *async_disconnect_cb; + int (*attach_fn)(valkeyAsyncContext *ac, void *attach_data); /* Event engine attach func. */ void *attach_data; - valkeyConnectCallback *onConnect; - valkeyDisconnectCallback *onDisconnect; /* TLS context, enabled using valkeyClusterOptionsEnableTLS. */ void *tls; @@ -284,28 +284,27 @@ int valkeyClusterUpdateSlotmap(valkeyClusterContext *cc); valkeyContext *valkeyClusterGetValkeyContext(valkeyClusterContext *cc, valkeyClusterNode *node); -/* - * Asynchronous API - */ +/* --- Asynchronous API --- */ -valkeyClusterAsyncContext *valkeyClusterAsyncContextInit(void); +valkeyClusterAsyncContext *valkeyClusterAsyncConnectWithOptions(const valkeyClusterOptions *options); +void valkeyClusterAsyncDisconnect(valkeyClusterAsyncContext *acc); void valkeyClusterAsyncFree(valkeyClusterAsyncContext *acc); +/* Initiate and connect as separate steps. */ +valkeyClusterAsyncContext *valkeyClusterAsyncContextInit(const valkeyClusterOptions *options); +int valkeyClusterAsyncConnect(valkeyClusterAsyncContext *acc); + int valkeyClusterAsyncSetConnectCallback(valkeyClusterAsyncContext *acc, valkeyConnectCallback *fn); int valkeyClusterAsyncSetDisconnectCallback(valkeyClusterAsyncContext *acc, valkeyDisconnectCallback *fn); +/* Callback option configurable after a context initiation, enabling that the + * valkeyClusterAsyncContext pointer can be given as privdata in the callback. */ int valkeyClusterAsyncSetEventCallback(valkeyClusterAsyncContext *acc, void(fn)(const valkeyClusterContext *cc, int event, void *privdata), void *privdata); -/* Connect and update slotmap, will block until complete. */ -valkeyClusterAsyncContext *valkeyClusterAsyncConnect(const char *addrs); -/* Connect and update slotmap asynchronously using configured event engine. */ -int valkeyClusterAsyncConnect2(valkeyClusterAsyncContext *acc); -void valkeyClusterAsyncDisconnect(valkeyClusterAsyncContext *acc); - /* Commands */ int valkeyClusterAsyncCommand(valkeyClusterAsyncContext *acc, valkeyClusterCallbackFn *fn, void *privdata, diff --git a/include/valkey/cluster_tls.h b/include/valkey/cluster_tls.h index e825c26..f0a20ca 100644 --- a/include/valkey/cluster_tls.h +++ b/include/valkey/cluster_tls.h @@ -43,10 +43,6 @@ extern "C" { int valkeyClusterOptionsEnableTLS(valkeyClusterOptions *options, valkeyTLSContext *tls); -/* A temporary function until the async context accepts options. */ -int valkeyClusterSetTLSContext(valkeyClusterContext *cc, - valkeyTLSContext *tls); - #ifdef __cplusplus } #endif diff --git a/src/cluster.c b/src/cluster.c index 9bf945c..2f527cd 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -2710,24 +2710,6 @@ static inline void valkeyClusterAsyncClearError(valkeyClusterAsyncContext *acc) acc->errstr[0] = '\0'; } -static valkeyClusterAsyncContext * -valkeyClusterAsyncInitialize(valkeyClusterContext *cc) { - valkeyClusterAsyncContext *acc; - - if (cc == NULL) { - return NULL; - } - - acc = vk_calloc(1, sizeof(valkeyClusterAsyncContext)); - if (acc == NULL) - return NULL; - - acc->cc = cc; - valkeyClusterAsyncSetError(acc, cc->err, cc->errstr); - - return acc; -} - static cluster_async_data *cluster_async_data_create(void) { /* use calloc to guarantee all fields are zeroed */ return vk_calloc(1, sizeof(cluster_async_data)); @@ -2853,49 +2835,56 @@ valkeyClusterGetValkeyAsyncContext(valkeyClusterAsyncContext *acc, return ac; } -valkeyClusterAsyncContext *valkeyClusterAsyncContextInit(void) { +valkeyClusterAsyncContext *valkeyClusterAsyncContextInit(const valkeyClusterOptions *options) { valkeyClusterContext *cc; valkeyClusterAsyncContext *acc; - valkeyClusterOptions options = {0}; - cc = valkeyClusterContextInit(&options); + cc = valkeyClusterContextInit(options); if (cc == NULL) { return NULL; } - acc = valkeyClusterAsyncInitialize(cc); + acc = vk_calloc(1, sizeof(valkeyClusterAsyncContext)); if (acc == NULL) { valkeyClusterFree(cc); return NULL; } + acc->cc = cc; + valkeyClusterAsyncSetError(acc, cc->err, cc->errstr); + + if (options->async_connect_cb != NULL) { + acc->onConnect = options->async_connect_cb; + } + if (options->async_disconnect_cb != NULL) { + acc->onDisconnect = options->async_disconnect_cb; + } + if (options->attach_fn != NULL) { + acc->attach_fn = options->attach_fn; + acc->attach_data = options->attach_data; + } return acc; } -valkeyClusterAsyncContext *valkeyClusterAsyncConnect(const char *addrs) { - - valkeyClusterContext *cc; - valkeyClusterAsyncContext *acc; - - cc = valkeyClusterConnect(addrs); - if (cc == NULL) { - return NULL; - } - - acc = valkeyClusterAsyncInitialize(cc); +valkeyClusterAsyncContext *valkeyClusterAsyncConnectWithOptions(const valkeyClusterOptions *options) { + valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(options); if (acc == NULL) { - valkeyClusterFree(cc); return NULL; } + //TODO: valkeyClusterAsyncConnect(acc); + if (valkeyClusterUpdateSlotmap(acc->cc) != VALKEY_OK) { + valkeyClusterAsyncSetError(acc, acc->cc->err, acc->cc->errstr); + } return acc; } -int valkeyClusterAsyncConnect2(valkeyClusterAsyncContext *acc) { +int valkeyClusterAsyncConnect(valkeyClusterAsyncContext *acc) { /* An attach function for an async event library is required. */ if (acc->attach_fn == NULL) { return VALKEY_ERR; } + /* TODO: add options to use: valkeyClusterUpdateSlotmap(acc->cc); */ return updateSlotMapAsync(acc, NULL /*any node*/); } diff --git a/src/cluster_tls.c b/src/cluster_tls.c index ac92ffa..0c07222 100644 --- a/src/cluster_tls.c +++ b/src/cluster_tls.c @@ -37,15 +37,3 @@ int valkeyClusterOptionsEnableTLS(valkeyClusterOptions *options, options->tls_init_fn = &valkeyInitiateTLSWithContext; return VALKEY_OK; } - -int valkeyClusterSetTLSContext(valkeyClusterContext *cc, - valkeyTLSContext *tls) { - if (cc == NULL || tls == NULL) { - return VALKEY_ERR; - } - - cc->tls = tls; - cc->tls_init_fn = &valkeyInitiateTLSWithContext; - - return VALKEY_OK; -} diff --git a/tests/clusterclient_async.c b/tests/clusterclient_async.c index de2e4e1..80f620f 100644 --- a/tests/clusterclient_async.c +++ b/tests/clusterclient_async.c @@ -247,33 +247,31 @@ int main(int argc, char **argv) { } const char *initnode = argv[optind]; struct timeval timeout = {0, 500000}; + struct event_base *base = event_base_new(); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterSetOptionAddNodes(acc->cc, initnode); - valkeyClusterSetOptionTimeout(acc->cc, timeout); - valkeyClusterSetOptionConnectTimeout(acc->cc, timeout); - valkeyClusterSetOptionMaxRetry(acc->cc, 1); + valkeyClusterOptions options = {0}; + options.initial_nodes = initnode; + options.connect_timeout = &timeout; + options.command_timeout = &timeout; + options.max_retry = 1; if (use_cluster_slots) { - valkeyClusterSetOptionRouteUseSlots(acc->cc); + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; } if (show_events) { - valkeyClusterAsyncSetEventCallback(acc, eventCallback, NULL); + options.event_callback = eventCallback; } if (show_connection_events) { - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; } + valkeyClusterOptionsUseLibevent(&options, base); - if (valkeyClusterConnect2(acc->cc) != VALKEY_OK) { - printf("Connect error: %s\n", acc->cc->errstr); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + if (acc == NULL || acc->err != 0) { + printf("Connect error: %s\n", acc ? acc->errstr : "OOM"); exit(2); } - struct event_base *base = event_base_new(); - int status = valkeyClusterLibeventAttach(acc, base); - assert(status == VALKEY_OK); - /* Schedule a read from stdin and send next command */ event_base_once(base, -1, EV_TIMEOUT, sendNextCommand, acc, NULL); diff --git a/tests/clusterclient_reconnect_async.c b/tests/clusterclient_reconnect_async.c index 05e973b..ddde2bc 100644 --- a/tests/clusterclient_reconnect_async.c +++ b/tests/clusterclient_reconnect_async.c @@ -95,15 +95,15 @@ int main(int argc, char **argv) { exit(1); } const char *initnode = argv[1]; + struct event_base *base = event_base_new(); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterSetOptionAddNodes(acc->cc, initnode); - valkeyClusterSetOptionRouteUseSlots(acc->cc); + valkeyClusterOptions options = {0}; + options.initial_nodes = initnode; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + valkeyClusterOptionsUseLibevent(&options, base); - struct event_base *base = event_base_new(); - int status = valkeyClusterLibeventAttach(acc, base); - assert(status == VALKEY_OK); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(&options); + assert(acc); connectToValkey(acc); // schedule reading from stdin and sending next command diff --git a/tests/ct_async.c b/tests/ct_async.c index aba821d..1eb75c0 100644 --- a/tests/ct_async.c +++ b/tests/ct_async.c @@ -60,32 +60,22 @@ void eventCallback(const valkeyClusterContext *cc, int event, void *privdata) { } int main(void) { + struct event_base *base = event_base_new(); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - - int status; - status = valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - assert(status == VALKEY_OK); - status = valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - assert(status == VALKEY_ERR); /* Re-registration not accepted */ - - status = valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - assert(status == VALKEY_OK); - status = valkeyClusterAsyncSetEventCallback(acc, eventCallback, acc); - assert(status == VALKEY_OK); - status = valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - assert(status == VALKEY_OK); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); - /* Expect error when connecting without an attached event library. */ - status = valkeyClusterAsyncConnect2(acc); - assert(status == VALKEY_ERR); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(&options); + assert(acc); - struct event_base *base = event_base_new(); - status = valkeyClusterLibeventAttach(acc, base); + /* Set an event callback that uses acc as privdata */ + int status = valkeyClusterAsyncSetEventCallback(acc, eventCallback, acc); assert(status == VALKEY_OK); - status = valkeyClusterAsyncConnect2(acc); + status = valkeyClusterAsyncConnect(acc); assert(status == VALKEY_OK); event_base_dispatch(base); diff --git a/tests/ct_async_glib.c b/tests/ct_async_glib.c index 58c076f..94cbb76 100644 --- a/tests/ct_async_glib.c +++ b/tests/ct_async_glib.c @@ -41,16 +41,17 @@ int main(int argc, char **argv) { GMainContext *context = NULL; mainloop = g_main_loop_new(context, FALSE); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnect(CLUSTER_NODE); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseGlib(&options, context); + + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); assert(acc); ASSERT_MSG(acc->err == 0, acc->errstr); - int status = valkeyClusterGlibAttach(acc, context); - assert(status == VALKEY_OK); - - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - + int status; status = valkeyClusterAsyncCommand(acc, setCallback, (char *)"id", "SET key value"); ASSERT_MSG(status == VALKEY_OK, acc->errstr); diff --git a/tests/ct_async_libev.c b/tests/ct_async_libev.c index 4f93c32..17fd930 100644 --- a/tests/ct_async_libev.c +++ b/tests/ct_async_libev.c @@ -35,17 +35,17 @@ int main(int argc, char **argv) { UNUSED(argc); UNUSED(argv); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnect(CLUSTER_NODE); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibev(&options, EV_DEFAULT); + + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); assert(acc); ASSERT_MSG(acc->err == 0, acc->errstr); int status; - status = valkeyClusterLibevAttach(acc, EV_DEFAULT); - assert(status == VALKEY_OK); - - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - status = valkeyClusterAsyncCommand(acc, setCallback, (char *)"ID", "SET key value"); ASSERT_MSG(status == VALKEY_OK, acc->errstr); diff --git a/tests/ct_async_libuv.c b/tests/ct_async_libuv.c index 9c2aca0..2fd38b3 100644 --- a/tests/ct_async_libuv.c +++ b/tests/ct_async_libuv.c @@ -36,18 +36,19 @@ int main(int argc, char **argv) { UNUSED(argc); UNUSED(argv); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnect(CLUSTER_NODE); + uv_loop_t *loop = uv_default_loop(); + + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibuv(&options, loop); + + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); assert(acc); ASSERT_MSG(acc->err == 0, acc->errstr); int status; - uv_loop_t *loop = uv_default_loop(); - status = valkeyClusterLibuvAttach(acc, loop); - assert(status == VALKEY_OK); - - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - status = valkeyClusterAsyncCommand(acc, setCallback, (char *)"ID", "SET key value"); ASSERT_MSG(status == VALKEY_OK, acc->errstr); diff --git a/tests/ct_connection.c b/tests/ct_connection.c index 01d62d0..7182e1e 100644 --- a/tests/ct_connection.c +++ b/tests/ct_connection.c @@ -328,26 +328,22 @@ void commandCallback(valkeyClusterAsyncContext *cc, void *r, void *privdata) { // Connecting to a password protected cluster using // the async API, providing correct password. void test_async_password_ok(void) { - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE_WITH_PASSWORD); - valkeyClusterSetOptionPassword(acc->cc, CLUSTER_PASSWORD); - struct event_base *base = event_base_new(); - valkeyClusterLibeventAttach(acc, base); - int ret; - ret = valkeyClusterConnect2(acc->cc); - assert(ret == VALKEY_OK); - assert(acc->err == 0); - assert(acc->cc->err == 0); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.password = CLUSTER_PASSWORD; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); + + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + ASSERT_MSG(acc && acc->err == 0, acc ? acc->errstr : "OOM"); // Test connection ExpectedResult r = { .type = VALKEY_REPLY_STATUS, .str = "OK", .disconnect = true}; - ret = valkeyClusterAsyncCommand(acc, commandCallback, &r, "SET key1 Hello"); + int ret = valkeyClusterAsyncCommand(acc, commandCallback, &r, "SET key1 Hello"); assert(ret == VALKEY_OK); event_base_dispatch(base); @@ -359,16 +355,16 @@ void test_async_password_ok(void) { /* Connect to a password protected cluster using the wrong password. An eventloop is not attached since it is not needed is this case. */ void test_async_password_wrong(void) { - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE_WITH_PASSWORD); - valkeyClusterSetOptionPassword(acc->cc, "faultypass"); + struct event_base *base = event_base_new(); - int ret; - ret = valkeyClusterConnect2(acc->cc); - assert(ret == VALKEY_ERR); - assert(acc->err == VALKEY_OK); // TODO: This must be wrong! - assert(acc->cc->err == VALKEY_ERR_OTHER); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.password = "faultypass"; + valkeyClusterOptionsUseLibevent(&options, base); + + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + assert(acc); + assert(acc->err == VALKEY_ERR_OTHER); if (valkey_version_less_than(6, 0)) assert(strcmp(acc->cc->errstr, "ERR invalid password") == 0); else @@ -376,73 +372,73 @@ void test_async_password_wrong(void) { // No connection ExpectedResult r; - ret = valkeyClusterAsyncCommand(acc, commandCallback, &r, "SET key1 Hello"); + int ret = valkeyClusterAsyncCommand(acc, commandCallback, &r, "SET key1 Hello"); assert(ret == VALKEY_ERR); assert(acc->err == VALKEY_ERR_OTHER); assert(strcmp(acc->errstr, "slotmap not available") == 0); valkeyClusterAsyncFree(acc); + event_base_free(base); } /* Connect to a password protected cluster without providing a password. An eventloop is not attached since it is not needed is this case. */ void test_async_password_missing(void) { - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE_WITH_PASSWORD); + struct event_base *base = event_base_new(); + + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); // Password not configured - int ret; - ret = valkeyClusterConnect2(acc->cc); - assert(ret == VALKEY_ERR); - assert(acc->err == VALKEY_OK); // TODO: This must be wrong! - assert(acc->cc->err == VALKEY_ERR_OTHER); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + assert(acc); + assert(acc->err == VALKEY_ERR_OTHER); assert(strncmp(acc->cc->errstr, "NOAUTH", 6) == 0); // No connection ExpectedResult r; - ret = valkeyClusterAsyncCommand(acc, commandCallback, &r, "SET key1 Hello"); + int ret = valkeyClusterAsyncCommand(acc, commandCallback, &r, "SET key1 Hello"); assert(ret == VALKEY_ERR); assert(acc->err == VALKEY_ERR_OTHER); assert(strcmp(acc->errstr, "slotmap not available") == 0); valkeyClusterAsyncFree(acc); + event_base_free(base); } // Connect to a cluster and authenticate using username and password void test_async_username_ok(void) { if (valkey_version_less_than(6, 0)) return; + struct event_base *base = event_base_new(); // Connect to the cluster using username and password - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE_WITH_PASSWORD); - valkeyClusterSetOptionUsername(acc->cc, "missing-user"); - valkeyClusterSetOptionPassword(acc->cc, CLUSTER_PASSWORD); - - struct event_base *base = event_base_new(); - valkeyClusterLibeventAttach(acc, base); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + options.username = "missing-user"; + options.password = CLUSTER_PASSWORD; + valkeyClusterOptionsUseLibevent(&options, base); // Connect using wrong username should fail - int ret = valkeyClusterConnect2(acc->cc); - assert(ret == VALKEY_ERR); - assert(acc->cc->err == VALKEY_ERR_OTHER); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + assert(acc); + assert(acc->err == VALKEY_ERR_OTHER); assert(strncmp(acc->cc->errstr, "WRONGPASS invalid username-password pair", 40) == 0); // Set correct username - ret = valkeyClusterSetOptionUsername(acc->cc, CLUSTER_USERNAME); + int ret = valkeyClusterSetOptionUsername(acc->cc, CLUSTER_USERNAME); ASSERT_MSG(ret == VALKEY_OK, acc->cc->errstr); // Connect using correct username should pass - ret = valkeyClusterConnect2(acc->cc); + ret = valkeyClusterConnect2(acc->cc); //TODO: handle async so it updates acc->err assert(ret == VALKEY_OK); - assert(acc->err == 0); + // assert(acc->err == 0); assert(acc->cc->err == 0); // Test connection @@ -459,40 +455,32 @@ void test_async_username_ok(void) { // Connect and handle two clusters simultaneously using the async API void test_async_multicluster(void) { - int ret; - - valkeyClusterAsyncContext *acc1 = valkeyClusterAsyncContextInit(); - assert(acc1); - valkeyClusterAsyncSetConnectCallback(acc1, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc1, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc1->cc, CLUSTER_NODE); - - valkeyClusterAsyncContext *acc2 = valkeyClusterAsyncContextInit(); - assert(acc2); - valkeyClusterAsyncSetConnectCallback(acc2, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc2, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc2->cc, CLUSTER_NODE_WITH_PASSWORD); - valkeyClusterSetOptionPassword(acc2->cc, CLUSTER_PASSWORD); - struct event_base *base = event_base_new(); - valkeyClusterLibeventAttach(acc1, base); - valkeyClusterLibeventAttach(acc2, base); + + valkeyClusterOptions options1 = {0}; + options1.initial_nodes = CLUSTER_NODE; + options1.async_connect_cb = connectCallback; + options1.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options1, base); // Connect to first cluster - ret = valkeyClusterConnect2(acc1->cc); - assert(ret == VALKEY_OK); - assert(acc1->err == 0); - assert(acc1->cc->err == 0); + valkeyClusterAsyncContext *acc1 = valkeyClusterAsyncConnectWithOptions(&options1); + ASSERT_MSG(acc1 && acc1->err == 0, acc1 ? acc1->errstr : "OOM"); + + valkeyClusterOptions options2 = {0}; + options2.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options2.password = CLUSTER_PASSWORD; + options2.async_connect_cb = connectCallback; + options2.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options2, base); // Connect to second cluster - ret = valkeyClusterConnect2(acc2->cc); - assert(ret == VALKEY_OK); - assert(acc2->err == 0); - assert(acc2->cc->err == 0); + valkeyClusterAsyncContext *acc2 = valkeyClusterAsyncConnectWithOptions(&options2); + ASSERT_MSG(acc2 && acc2->err == 0, acc2 ? acc2->errstr : "OOM"); // Set keys differently in clusters ExpectedResult r1 = {.type = VALKEY_REPLY_STATUS, .str = "OK"}; - ret = valkeyClusterAsyncCommand(acc1, commandCallback, &r1, "SET key A"); + int ret = valkeyClusterAsyncCommand(acc1, commandCallback, &r1, "SET key A"); assert(ret == VALKEY_OK); ExpectedResult r2 = {.type = VALKEY_REPLY_STATUS, .str = "OK"}; @@ -525,22 +513,19 @@ void test_async_multicluster(void) { /* Connect to a non-routable address which results in a connection timeout. */ void test_async_connect_timeout(void) { + struct event_base *base = event_base_new(); struct timeval timeout = {0, 200000}; - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - + valkeyClusterOptions options = {0}; /* Configure a non-routable IP address and a timeout */ - valkeyClusterSetOptionAddNodes(acc->cc, "192.168.0.0:7000"); - valkeyClusterSetOptionConnectTimeout(acc->cc, timeout); - - struct event_base *base = event_base_new(); - valkeyClusterLibeventAttach(acc, base); + options.initial_nodes = "192.168.0.0:7000"; + options.connect_timeout = &timeout; + valkeyClusterOptionsUseLibevent(&options, base); - int status = valkeyClusterConnect2(acc->cc); - assert(status == VALKEY_ERR); - assert(acc->cc->err == VALKEY_ERR_IO); - assert(strcmp(acc->cc->errstr, "Connection timed out") == 0); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + assert(acc); + assert(acc->err == VALKEY_ERR_IO); + assert(strcmp(acc->errstr, "Connection timed out") == 0); event_base_dispatch(base); @@ -550,19 +535,16 @@ void test_async_connect_timeout(void) { /* Connect using a pre-configured command timeout */ void test_async_command_timeout(void) { + struct event_base *base = event_base_new(); struct timeval timeout = {0, 10000}; - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - valkeyClusterSetOptionTimeout(acc->cc, timeout); - - struct event_base *base = event_base_new(); - valkeyClusterLibeventAttach(acc, base); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.command_timeout = &timeout; + valkeyClusterOptionsUseLibevent(&options, base); - int status = valkeyClusterConnect2(acc->cc); - assert(status == VALKEY_OK); - assert(acc->cc->err == 0); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + ASSERT_MSG(acc && acc->err == 0, acc ? acc->errstr : "OOM"); valkeyClusterNodeIterator ni; valkeyClusterInitNodeIterator(&ni, acc->cc); @@ -572,8 +554,8 @@ void test_async_command_timeout(void) { /* Simulate a command timeout and expect a timeout error */ ExpectedResult r = { .noreply = true, .errstr = "Timeout", .disconnect = true}; - status = valkeyClusterAsyncCommandToNode(acc, node, commandCallback, &r, - "DEBUG SLEEP 0.2"); + int status = valkeyClusterAsyncCommandToNode(acc, node, commandCallback, &r, + "DEBUG SLEEP 0.2"); assert(status == VALKEY_OK); event_base_dispatch(base); diff --git a/tests/ct_out_of_memory_handling.c b/tests/ct_out_of_memory_handling.c index e1af517..ace035d 100644 --- a/tests/ct_out_of_memory_handling.c +++ b/tests/ct_out_of_memory_handling.c @@ -447,64 +447,38 @@ void test_alloc_failure_handling_async(void) { // Override allocators valkeySetAllocators(&ha); - // Context init - valkeyClusterAsyncContext *acc; - { - for (int i = 0; i < 4; ++i) { - successfulAllocations = 0; - acc = valkeyClusterAsyncContextInit(); - assert(acc == NULL); - } - successfulAllocations = 4; - acc = valkeyClusterAsyncContextInit(); - assert(acc); - } - valkeyClusterSetOptionParseSlaves(acc->cc); + struct event_base *base = event_base_new(); + assert(base); - // Set callbacks - { - prepare_allocation_test_async(acc, 0); - result = valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - assert(result == VALKEY_OK); - result = valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - assert(result == VALKEY_OK); - } + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_USE_REPLICAS; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); - // Add nodes + // Connect + valkeyClusterAsyncContext *acc; { - for (int i = 0; i < 9; ++i) { - prepare_allocation_test(acc->cc, i); - result = valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - assert(result == VALKEY_ERR); - ASSERT_STR_EQ(acc->cc->errstr, "Out of memory"); + for (int i = 0; i < 13; ++i) { + successfulAllocations = i; + acc = valkeyClusterAsyncConnectWithOptions(&options); + assert(acc == NULL); } - prepare_allocation_test(acc->cc, 9); - result = valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - assert(result == VALKEY_OK); - } - - // Connect - { - for (int i = 0; i < 94; ++i) { - prepare_allocation_test(acc->cc, i); - result = valkeyClusterConnect2(acc->cc); - assert(result == VALKEY_ERR); - ASSERT_STR_EQ(acc->cc->errstr, "Out of memory"); + for (int i = 13; i < 107; ++i) { + successfulAllocations = i; + acc = valkeyClusterAsyncConnectWithOptions(&options); + ASSERT_STR_EQ(acc->errstr, "Out of memory"); + assert(acc != NULL); + valkeyClusterAsyncFree(acc); } - prepare_allocation_test(acc->cc, 94); - result = valkeyClusterConnect2(acc->cc); - assert(result == VALKEY_OK); + successfulAllocations = 107; + acc = valkeyClusterAsyncConnectWithOptions(&options); + assert(acc && acc->err == 0); } - struct event_base *base = event_base_new(); - assert(base); - - successfulAllocations = 0; - result = valkeyClusterLibeventAttach(acc, base); - assert(result == VALKEY_OK); - // Async command 1 ExpectedResult r1 = {.type = VALKEY_REPLY_STATUS, .str = "OK"}; { diff --git a/tests/ct_pipeline.c b/tests/ct_pipeline.c index c723308..6dd1ed2 100644 --- a/tests/ct_pipeline.c +++ b/tests/ct_pipeline.c @@ -92,20 +92,18 @@ void commandCallback(valkeyClusterAsyncContext *cc, void *r, void *privdata) { // nature of an event loop. Therefore, unlike the synchronous API, there is only // a single way to send commands. void test_async_pipeline(void) { - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); + struct event_base *base = event_base_new(); - int status; - status = valkeyClusterConnect2(acc->cc); - ASSERT_MSG(status == VALKEY_OK, acc->errstr); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); - struct event_base *base = event_base_new(); - status = valkeyClusterLibeventAttach(acc, base); - assert(status == VALKEY_OK); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + ASSERT_MSG(acc && acc->err == 0, acc ? acc->errstr : "OOM"); + int status; ExpectedResult r1 = {.type = VALKEY_REPLY_STATUS, .str = "OK"}; status = valkeyClusterAsyncCommand(acc, commandCallback, &r1, "SET foo six"); diff --git a/tests/ct_specific_nodes.c b/tests/ct_specific_nodes.c index b09f260..eefb09a 100644 --- a/tests/ct_specific_nodes.c +++ b/tests/ct_specific_nodes.c @@ -318,27 +318,25 @@ void commandCallback(valkeyClusterAsyncContext *cc, void *r, void *privdata) { } void test_async_to_single_node(void) { - int status; + struct event_base *base = event_base_new(); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - valkeyClusterSetOptionMaxRetry(acc->cc, 1); - valkeyClusterSetOptionRouteUseSlots(acc->cc); - status = valkeyClusterConnect2(acc->cc); - ASSERT_MSG(status == VALKEY_OK, acc->errstr); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.max_retry = 1; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); - struct event_base *base = event_base_new(); - status = valkeyClusterLibeventAttach(acc, base); - assert(status == VALKEY_OK); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + ASSERT_MSG(acc && acc->err == 0, acc ? acc->errstr : "OOM"); valkeyClusterNodeIterator ni; valkeyClusterInitNodeIterator(&ni, acc->cc); valkeyClusterNode *node = valkeyClusterNodeNext(&ni); assert(node); + int status; ExpectedResult r1 = {.type = VALKEY_REPLY_INTEGER, .disconnect = true}; status = valkeyClusterAsyncCommandToNode(acc, node, commandCallback, &r1, "DBSIZE"); @@ -351,27 +349,25 @@ void test_async_to_single_node(void) { } void test_async_formatted_to_single_node(void) { - int status; + struct event_base *base = event_base_new(); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - valkeyClusterSetOptionMaxRetry(acc->cc, 1); - valkeyClusterSetOptionRouteUseSlots(acc->cc); - status = valkeyClusterConnect2(acc->cc); - ASSERT_MSG(status == VALKEY_OK, acc->errstr); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.max_retry = 1; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); - struct event_base *base = event_base_new(); - status = valkeyClusterLibeventAttach(acc, base); - assert(status == VALKEY_OK); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + ASSERT_MSG(acc && acc->err == 0, acc ? acc->errstr : "OOM"); valkeyClusterNodeIterator ni; valkeyClusterInitNodeIterator(&ni, acc->cc); valkeyClusterNode *node = valkeyClusterNodeNext(&ni); assert(node); + int status; ExpectedResult r1 = {.type = VALKEY_REPLY_INTEGER, .disconnect = true}; char command[] = "*1\r\n$6\r\nDBSIZE\r\n"; status = valkeyClusterAsyncFormattedCommandToNode( @@ -385,27 +381,25 @@ void test_async_formatted_to_single_node(void) { } void test_async_command_argv_to_single_node(void) { - int status; + struct event_base *base = event_base_new(); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - valkeyClusterSetOptionMaxRetry(acc->cc, 1); - valkeyClusterSetOptionRouteUseSlots(acc->cc); - status = valkeyClusterConnect2(acc->cc); - ASSERT_MSG(status == VALKEY_OK, acc->errstr); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.max_retry = 1; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); - struct event_base *base = event_base_new(); - status = valkeyClusterLibeventAttach(acc, base); - assert(status == VALKEY_OK); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + ASSERT_MSG(acc && acc->err == 0, acc ? acc->errstr : "OOM"); valkeyClusterNodeIterator ni; valkeyClusterInitNodeIterator(&ni, acc->cc); valkeyClusterNode *node = valkeyClusterNodeNext(&ni); assert(node); + int status; ExpectedResult r1 = {.type = VALKEY_REPLY_INTEGER, .disconnect = true}; status = valkeyClusterAsyncCommandArgvToNode( acc, node, commandCallback, &r1, 1, (const char *[]){"DBSIZE"}, @@ -419,25 +413,23 @@ void test_async_command_argv_to_single_node(void) { } void test_async_to_all_nodes(void) { - int status; + struct event_base *base = event_base_new(); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - valkeyClusterSetOptionMaxRetry(acc->cc, 1); - valkeyClusterSetOptionRouteUseSlots(acc->cc); - status = valkeyClusterConnect2(acc->cc); - ASSERT_MSG(status == VALKEY_OK, acc->errstr); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.max_retry = 1; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); - struct event_base *base = event_base_new(); - status = valkeyClusterLibeventAttach(acc, base); - assert(status == VALKEY_OK); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + ASSERT_MSG(acc && acc->err == 0, acc ? acc->errstr : "OOM"); valkeyClusterNodeIterator ni; valkeyClusterInitNodeIterator(&ni, acc->cc); + int status; ExpectedResult r1 = {.type = VALKEY_REPLY_INTEGER}; valkeyClusterNode *node; @@ -461,25 +453,23 @@ void test_async_to_all_nodes(void) { } void test_async_transaction(void) { - int status; + struct event_base *base = event_base_new(); - valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); - assert(acc); - valkeyClusterAsyncSetConnectCallback(acc, connectCallback); - valkeyClusterAsyncSetDisconnectCallback(acc, disconnectCallback); - valkeyClusterSetOptionAddNodes(acc->cc, CLUSTER_NODE); - valkeyClusterSetOptionMaxRetry(acc->cc, 1); - valkeyClusterSetOptionRouteUseSlots(acc->cc); - status = valkeyClusterConnect2(acc->cc); - ASSERT_MSG(status == VALKEY_OK, acc->errstr); + valkeyClusterOptions options = {0}; + options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.max_retry = 1; + options.async_connect_cb = connectCallback; + options.async_disconnect_cb = disconnectCallback; + valkeyClusterOptionsUseLibevent(&options, base); - struct event_base *base = event_base_new(); - status = valkeyClusterLibeventAttach(acc, base); - assert(status == VALKEY_OK); + valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); + ASSERT_MSG(acc && acc->err == 0, acc ? acc->errstr : "OOM"); valkeyClusterNode *node = valkeyClusterGetNodeByKey(acc->cc, (char *)"foo"); assert(node); + int status; ExpectedResult r1 = {.type = VALKEY_REPLY_STATUS, .str = "OK"}; status = valkeyClusterAsyncCommandToNode(acc, node, commandCallback, &r1, "MULTI"); From c582f075ef474e6bf1d37305873c38f7c171cd65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Sun, 20 Oct 2024 09:18:25 +0200 Subject: [PATCH 04/25] Remove config functions that are replaced with options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- include/valkey/cluster.h | 17 ---------- src/cluster.c | 70 +++++++--------------------------------- tests/ct_connection.c | 51 ++++------------------------- 3 files changed, 19 insertions(+), 119 deletions(-) diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index 16c80b2..56e0a2d 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -207,19 +207,6 @@ valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, const s int valkeyClusterConnect2(valkeyClusterContext *cc); void valkeyClusterFree(valkeyClusterContext *cc); -/* Configuration options */ -int valkeyClusterSetOptionAddNodes(valkeyClusterContext *cc, const char *addrs); -int valkeyClusterSetOptionUsername(valkeyClusterContext *cc, - const char *username); -int valkeyClusterSetOptionPassword(valkeyClusterContext *cc, - const char *password); -int valkeyClusterSetOptionParseSlaves(valkeyClusterContext *cc); -int valkeyClusterSetOptionRouteUseSlots(valkeyClusterContext *cc); -int valkeyClusterSetOptionConnectTimeout(valkeyClusterContext *cc, - const struct timeval tv); -int valkeyClusterSetOptionMaxRetry(valkeyClusterContext *cc, - int max_retry_count); - /* Options configurable in runtime. */ int valkeyClusterSetOptionTimeout(valkeyClusterContext *cc, const struct timeval tv); @@ -294,10 +281,6 @@ void valkeyClusterAsyncFree(valkeyClusterAsyncContext *acc); valkeyClusterAsyncContext *valkeyClusterAsyncContextInit(const valkeyClusterOptions *options); int valkeyClusterAsyncConnect(valkeyClusterAsyncContext *acc); -int valkeyClusterAsyncSetConnectCallback(valkeyClusterAsyncContext *acc, - valkeyConnectCallback *fn); -int valkeyClusterAsyncSetDisconnectCallback(valkeyClusterAsyncContext *acc, - valkeyDisconnectCallback *fn); /* Callback option configurable after a context initiation, enabling that the * valkeyClusterAsyncContext pointer can be given as privdata in the callback. */ int valkeyClusterAsyncSetEventCallback(valkeyClusterAsyncContext *acc, diff --git a/src/cluster.c b/src/cluster.c index 2f527cd..6a2eb43 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -109,6 +109,10 @@ static void cluster_slot_destroy(cluster_slot *slot); static int updateNodesAndSlotmap(valkeyClusterContext *cc, dict *nodes); static int updateSlotMapAsync(valkeyClusterAsyncContext *acc, valkeyAsyncContext *ac); +static int valkeyClusterSetOptionAddNodes(valkeyClusterContext *cc, const char *addrs); +static int valkeyClusterSetOptionConnectTimeout(valkeyClusterContext *cc, const struct timeval tv); +static int valkeyClusterSetOptionPassword(valkeyClusterContext *cc, const char *password); +static int valkeyClusterSetOptionUsername(valkeyClusterContext *cc, const char *username); void listClusterNodeDestructor(void *val) { freeValkeyClusterNode(val); } @@ -1472,8 +1476,8 @@ static int valkeyClusterSetOptionAddNode(valkeyClusterContext *cc, const char *a return VALKEY_ERR; } -int valkeyClusterSetOptionAddNodes(valkeyClusterContext *cc, - const char *addrs) { +static int valkeyClusterSetOptionAddNodes(valkeyClusterContext *cc, + const char *addrs) { int ret; sds *address = NULL; int address_count = 0; @@ -1517,8 +1521,8 @@ int valkeyClusterSetOptionAddNodes(valkeyClusterContext *cc, * Disabled by default. Can be disabled again by providing an * empty string or a null pointer. */ -int valkeyClusterSetOptionUsername(valkeyClusterContext *cc, - const char *username) { +static int valkeyClusterSetOptionUsername(valkeyClusterContext *cc, + const char *username) { if (cc == NULL) { return VALKEY_ERR; } @@ -1543,8 +1547,8 @@ int valkeyClusterSetOptionUsername(valkeyClusterContext *cc, * Configure a password used when connecting to password-protected * Valkey instances. (See Valkey AUTH command) */ -int valkeyClusterSetOptionPassword(valkeyClusterContext *cc, - const char *password) { +static int valkeyClusterSetOptionPassword(valkeyClusterContext *cc, + const char *password) { if (cc == NULL) { return VALKEY_ERR; @@ -1566,30 +1570,8 @@ int valkeyClusterSetOptionPassword(valkeyClusterContext *cc, return VALKEY_OK; } -int valkeyClusterSetOptionParseSlaves(valkeyClusterContext *cc) { - - if (cc == NULL) { - return VALKEY_ERR; - } - - cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; - - return VALKEY_OK; -} - -int valkeyClusterSetOptionRouteUseSlots(valkeyClusterContext *cc) { - - if (cc == NULL) { - return VALKEY_ERR; - } - - cc->flags |= VALKEY_FLAG_USE_CLUSTER_SLOTS; - - return VALKEY_OK; -} - -int valkeyClusterSetOptionConnectTimeout(valkeyClusterContext *cc, - const struct timeval tv) { +static int valkeyClusterSetOptionConnectTimeout(valkeyClusterContext *cc, + const struct timeval tv) { if (cc == NULL) { return VALKEY_ERR; @@ -1669,17 +1651,6 @@ int valkeyClusterSetOptionTimeout(valkeyClusterContext *cc, return VALKEY_OK; } -int valkeyClusterSetOptionMaxRetry(valkeyClusterContext *cc, - int max_retry_count) { - if (cc == NULL || max_retry_count <= 0) { - return VALKEY_ERR; - } - - cc->max_retry_count = max_retry_count; - - return VALKEY_OK; -} - int valkeyClusterConnect2(valkeyClusterContext *cc) { if (cc == NULL) { @@ -2888,23 +2859,6 @@ int valkeyClusterAsyncConnect(valkeyClusterAsyncContext *acc) { return updateSlotMapAsync(acc, NULL /*any node*/); } -int valkeyClusterAsyncSetConnectCallback(valkeyClusterAsyncContext *acc, - valkeyConnectCallback *fn) { - if (acc->onConnect != NULL) - return VALKEY_ERR; - acc->onConnect = fn; - return VALKEY_OK; -} - -int valkeyClusterAsyncSetDisconnectCallback(valkeyClusterAsyncContext *acc, - valkeyDisconnectCallback *fn) { - if (acc->onDisconnect == NULL) { - acc->onDisconnect = fn; - return VALKEY_OK; - } - return VALKEY_ERR; -} - int valkeyClusterAsyncSetEventCallback(valkeyClusterAsyncContext *acc, void(fn)(const valkeyClusterContext *cc, int event, void *privdata), diff --git a/tests/ct_connection.c b/tests/ct_connection.c index 7182e1e..6ca2d78 100644 --- a/tests/ct_connection.c +++ b/tests/ct_connection.c @@ -111,41 +111,6 @@ void test_username_ok(void) { valkeyClusterFree(cc); } -// Test of disabling the use of username after it was enabled. -void test_username_disabled(void) { - if (valkey_version_less_than(6, 0)) - return; - - valkeyClusterOptions options = {0}; - options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; - options.username = "missing-user"; - options.password = CLUSTER_PASSWORD; - - // Connect using 'AUTH ' should fail - valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); - assert(cc); - assert(cc->err == VALKEY_ERR_OTHER); - assert(strncmp(cc->errstr, "WRONGPASS invalid username-password pair", - 40) == 0); - - // Disable use of username (2 alternatives) - int ret = valkeyClusterSetOptionUsername(cc, NULL); - ASSERT_MSG(ret == VALKEY_OK, cc->errstr); - ret = valkeyClusterSetOptionUsername(cc, ""); - ASSERT_MSG(ret == VALKEY_OK, cc->errstr); - - // Connect using 'AUTH ' should pass - ret = valkeyClusterConnect2(cc); - ASSERT_MSG(ret == VALKEY_OK, cc->errstr); - - // Test connection - valkeyReply *reply = valkeyClusterCommand(cc, "SET key1 Hello"); - CHECK_REPLY_OK(cc, reply); - freeReplyObject(reply); - - valkeyClusterFree(cc); -} - // Connect and handle two clusters simultaneously void test_multicluster(void) { valkeyReply *reply; @@ -428,23 +393,22 @@ void test_async_username_ok(void) { valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); assert(acc); assert(acc->err == VALKEY_ERR_OTHER); - assert(strncmp(acc->cc->errstr, "WRONGPASS invalid username-password pair", + assert(strncmp(acc->errstr, "WRONGPASS invalid username-password pair", 40) == 0); + valkeyClusterAsyncFree(acc); // Set correct username - int ret = valkeyClusterSetOptionUsername(acc->cc, CLUSTER_USERNAME); - ASSERT_MSG(ret == VALKEY_OK, acc->cc->errstr); + options.username = CLUSTER_USERNAME; // Connect using correct username should pass - ret = valkeyClusterConnect2(acc->cc); //TODO: handle async so it updates acc->err - assert(ret == VALKEY_OK); - // assert(acc->err == 0); - assert(acc->cc->err == 0); + acc = valkeyClusterAsyncConnectWithOptions(&options); + assert(acc); + assert(acc->err == 0); // Test connection ExpectedResult r = { .type = VALKEY_REPLY_STATUS, .str = "OK", .disconnect = true}; - ret = valkeyClusterAsyncCommand(acc, commandCallback, &r, "SET key1 Hello"); + int ret = valkeyClusterAsyncCommand(acc, commandCallback, &r, "SET key1 Hello"); assert(ret == VALKEY_OK); event_base_dispatch(base); @@ -570,7 +534,6 @@ int main(void) { test_password_wrong(); test_password_missing(); test_username_ok(); - test_username_disabled(); test_multicluster(); test_connect_timeout(); test_command_timeout(); From b5ad0a955485ef3071963d15df2099a3fb0de113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 4 Dec 2024 21:08:39 +0100 Subject: [PATCH 05/25] New option: Blocking slotmap updates after initial async connect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- include/valkey/cluster.h | 3 +- src/cluster.c | 42 ++++++++++++--------------- tests/clusterclient_async.c | 3 +- tests/clusterclient_reconnect_async.c | 16 +++++----- tests/ct_async_glib.c | 1 + tests/ct_async_libev.c | 1 + tests/ct_async_libuv.c | 1 + tests/ct_connection.c | 8 +++++ tests/ct_out_of_memory_handling.c | 3 +- tests/ct_pipeline.c | 1 + tests/ct_specific_nodes.c | 15 ++++++---- 11 files changed, 55 insertions(+), 39 deletions(-) diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index 56e0a2d..ad1807c 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -152,6 +152,8 @@ typedef struct valkeyClusterNodeIterator { /* Enable parsing of replica nodes. Currently not used, but the * information is added to its primary node structure. */ #define VALKEY_OPT_USE_REPLICAS 0x2000 +/* Use a blocking slotmap update after an initial async connect. */ +#define VALKEY_OPT_BLOCKING_INITIAL_UPDATE 0x4000 typedef struct { const char *initial_nodes; /* Initial cluster node address(es). */ @@ -204,7 +206,6 @@ int valkeyClusterOptionsSetConnectCallback(valkeyClusterOptions *options, valkeyClusterContext *valkeyClusterConnectWithOptions(const valkeyClusterOptions *options); valkeyClusterContext *valkeyClusterConnect(const char *addrs); valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, const struct timeval tv); -int valkeyClusterConnect2(valkeyClusterContext *cc); void valkeyClusterFree(valkeyClusterContext *cc); /* Options configurable in runtime. */ diff --git a/src/cluster.c b/src/cluster.c index 6a2eb43..c93f060 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -56,6 +56,7 @@ vk_static_assert(VALKEY_OPT_USE_CLUSTER_SLOTS > VALKEY_OPT_LAST_SA_OPTION); #define VALKEY_FLAG_USE_CLUSTER_SLOTS 0x1 #define VALKEY_FLAG_PARSE_REPLICAS 0x2 #define VALKEY_FLAG_DISCONNECTING 0x4 +#define VALKEY_FLAG_BLOCKING_INITIAL_UPDATE 0x8 // Cluster errors are offset by 100 to be sufficiently out of range of // standard Valkey errors @@ -1294,6 +1295,9 @@ static valkeyClusterContext *valkeyClusterContextInit(const valkeyClusterOptions if (options->options & VALKEY_OPT_USE_REPLICAS) { cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; } + if (options->options & VALKEY_OPT_BLOCKING_INITIAL_UPDATE) { + cc->flags |= VALKEY_FLAG_BLOCKING_INITIAL_UPDATE; + } if (options->max_retry > 0) { cc->max_retry_count = options->max_retry; } else { @@ -1651,24 +1655,6 @@ int valkeyClusterSetOptionTimeout(valkeyClusterContext *cc, return VALKEY_OK; } -int valkeyClusterConnect2(valkeyClusterContext *cc) { - - if (cc == NULL) { - return VALKEY_ERR; - } - - if (dictSize(cc->nodes) == 0) { - valkeyClusterSetError(cc, VALKEY_ERR_OTHER, - "server address not configured"); - return VALKEY_ERR; - } - /* Clear a previously set shutdown flag since we allow a - * reconnection of an async context using this API (legacy). */ - cc->flags &= ~VALKEY_FLAG_DISCONNECTING; - - return valkeyClusterUpdateSlotmap(cc); -} - valkeyContext *valkeyClusterGetValkeyContext(valkeyClusterContext *cc, valkeyClusterNode *node) { valkeyContext *c = NULL; @@ -2843,10 +2829,7 @@ valkeyClusterAsyncContext *valkeyClusterAsyncConnectWithOptions(const valkeyClus return NULL; } - //TODO: valkeyClusterAsyncConnect(acc); - if (valkeyClusterUpdateSlotmap(acc->cc) != VALKEY_OK) { - valkeyClusterAsyncSetError(acc, acc->cc->err, acc->cc->errstr); - } + valkeyClusterAsyncConnect(acc); return acc; } @@ -2855,7 +2838,20 @@ int valkeyClusterAsyncConnect(valkeyClusterAsyncContext *acc) { if (acc->attach_fn == NULL) { return VALKEY_ERR; } - /* TODO: add options to use: valkeyClusterUpdateSlotmap(acc->cc); */ + + /* Clear a previously set shutdown flag to allow a + * reconnection of an async context using this API. */ + acc->cc->flags &= ~VALKEY_FLAG_DISCONNECTING; + + /* Use blocking initial slotmap update when configured. */ + if (acc->cc->flags & VALKEY_FLAG_BLOCKING_INITIAL_UPDATE) { + if (valkeyClusterUpdateSlotmap(acc->cc) != VALKEY_OK) { + valkeyClusterAsyncSetError(acc, acc->cc->err, acc->cc->errstr); + return VALKEY_ERR; + } + return VALKEY_OK; + } + /* Use non-blocking initial slotmap update. */ return updateSlotMapAsync(acc, NULL /*any node*/); } diff --git a/tests/clusterclient_async.c b/tests/clusterclient_async.c index 80f620f..fd1cb3f 100644 --- a/tests/clusterclient_async.c +++ b/tests/clusterclient_async.c @@ -251,11 +251,12 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = initnode; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.connect_timeout = &timeout; options.command_timeout = &timeout; options.max_retry = 1; if (use_cluster_slots) { - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.options |= VALKEY_OPT_USE_CLUSTER_SLOTS; } if (show_events) { options.event_callback = eventCallback; diff --git a/tests/clusterclient_reconnect_async.c b/tests/clusterclient_reconnect_async.c index ddde2bc..ebb0755 100644 --- a/tests/clusterclient_reconnect_async.c +++ b/tests/clusterclient_reconnect_async.c @@ -27,16 +27,15 @@ void connectToValkey(valkeyClusterAsyncContext *acc) { /* reset context in case of reconnect */ valkeyClusterAsyncDisconnect(acc); - int status = valkeyClusterConnect2(acc->cc); - if (status == VALKEY_OK) { + if (valkeyClusterAsyncConnect(acc) == VALKEY_OK) { // cluster mode - } else if (acc->cc->err && - strcmp(acc->cc->errstr, VALKEY_ENOCLUSTER) == 0) { + } else if (acc->err && + strcmp(acc->errstr, VALKEY_ENOCLUSTER) == 0) { printf("[no cluster]\n"); - acc->cc->err = 0; - memset(acc->cc->errstr, '\0', strlen(acc->cc->errstr)); + acc->err = 0; + memset(acc->errstr, '\0', strlen(acc->errstr)); } else { - printf("Connect error: %s\n", acc->cc->errstr); + printf("Connect error: %s\n", acc->errstr); exit(-1); } } @@ -99,7 +98,8 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = initnode; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | + VALKEY_OPT_BLOCKING_INITIAL_UPDATE; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(&options); diff --git a/tests/ct_async_glib.c b/tests/ct_async_glib.c index 94cbb76..750b469 100644 --- a/tests/ct_async_glib.c +++ b/tests/ct_async_glib.c @@ -43,6 +43,7 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; valkeyClusterOptionsUseGlib(&options, context); diff --git a/tests/ct_async_libev.c b/tests/ct_async_libev.c index 17fd930..22f517a 100644 --- a/tests/ct_async_libev.c +++ b/tests/ct_async_libev.c @@ -37,6 +37,7 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; valkeyClusterOptionsUseLibev(&options, EV_DEFAULT); diff --git a/tests/ct_async_libuv.c b/tests/ct_async_libuv.c index 2fd38b3..481f235 100644 --- a/tests/ct_async_libuv.c +++ b/tests/ct_async_libuv.c @@ -40,6 +40,7 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; valkeyClusterOptionsUseLibuv(&options, loop); diff --git a/tests/ct_connection.c b/tests/ct_connection.c index 6ca2d78..720be54 100644 --- a/tests/ct_connection.c +++ b/tests/ct_connection.c @@ -297,6 +297,7 @@ void test_async_password_ok(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.password = CLUSTER_PASSWORD; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -324,6 +325,7 @@ void test_async_password_wrong(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.password = "faultypass"; valkeyClusterOptionsUseLibevent(&options, base); @@ -353,6 +355,7 @@ void test_async_password_missing(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); @@ -383,6 +386,7 @@ void test_async_username_ok(void) { // Connect to the cluster using username and password valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; options.username = "missing-user"; @@ -423,6 +427,7 @@ void test_async_multicluster(void) { valkeyClusterOptions options1 = {0}; options1.initial_nodes = CLUSTER_NODE; + options1.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options1.async_connect_cb = connectCallback; options1.async_disconnect_cb = disconnectCallback; valkeyClusterOptionsUseLibevent(&options1, base); @@ -433,6 +438,7 @@ void test_async_multicluster(void) { valkeyClusterOptions options2 = {0}; options2.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; + options2.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options2.password = CLUSTER_PASSWORD; options2.async_connect_cb = connectCallback; options2.async_disconnect_cb = disconnectCallback; @@ -483,6 +489,7 @@ void test_async_connect_timeout(void) { valkeyClusterOptions options = {0}; /* Configure a non-routable IP address and a timeout */ options.initial_nodes = "192.168.0.0:7000"; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.connect_timeout = &timeout; valkeyClusterOptionsUseLibevent(&options, base); @@ -504,6 +511,7 @@ void test_async_command_timeout(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.command_timeout = &timeout; valkeyClusterOptionsUseLibevent(&options, base); diff --git a/tests/ct_out_of_memory_handling.c b/tests/ct_out_of_memory_handling.c index ace035d..9c9ee1f 100644 --- a/tests/ct_out_of_memory_handling.c +++ b/tests/ct_out_of_memory_handling.c @@ -452,7 +452,8 @@ void test_alloc_failure_handling_async(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_REPLICAS; + options.options = VALKEY_OPT_USE_REPLICAS | + VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); diff --git a/tests/ct_pipeline.c b/tests/ct_pipeline.c index 6dd1ed2..5d14b6b 100644 --- a/tests/ct_pipeline.c +++ b/tests/ct_pipeline.c @@ -96,6 +96,7 @@ void test_async_pipeline(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); diff --git a/tests/ct_specific_nodes.c b/tests/ct_specific_nodes.c index eefb09a..8008f69 100644 --- a/tests/ct_specific_nodes.c +++ b/tests/ct_specific_nodes.c @@ -322,7 +322,8 @@ void test_async_to_single_node(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | + VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -353,7 +354,8 @@ void test_async_formatted_to_single_node(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | + VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -385,7 +387,8 @@ void test_async_command_argv_to_single_node(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | + VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -417,7 +420,8 @@ void test_async_to_all_nodes(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | + VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -457,7 +461,8 @@ void test_async_transaction(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | + VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; From b61a8131b8a3bbc6c3484414a39671106c938975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Fri, 3 Jan 2025 13:23:10 +0100 Subject: [PATCH 06/25] Update documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 128 +++++++++++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index 320476c..c5f4f3b 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -7,6 +7,7 @@ It is not intended as a complete reference. For that it's always best to refer t - [Synchronous API](#synchronous-api) - [Connecting](#connecting) + - [Connection options](#connection-options) - [Executing commands](#executing-commands) - [Executing commands on a specific node](#executing-commands-on-a-specific-node) - [Disconnecting/cleanup](#disconnecting-cleanup) @@ -14,6 +15,7 @@ It is not intended as a complete reference. For that it's always best to refer t - [Events](#events) - [Asynchronous API](#asynchronous-api) - [Connecting](#connecting-1) + - [Connection options](#connection-options-1) - [Executing commands](#executing-commands-1) - [Executing commands on a specific node](#executing-commands-on-a-specific-node-1) - [Disconnecting/cleanup](#disconnecting-cleanup-1) @@ -27,37 +29,57 @@ It is not intended as a complete reference. For that it's always best to refer t ### Connecting -There are a variety of ways to setup and connect to a cluster. -The most basic alternative lacks many options, but can be enough for some use cases. +There are a few alternative ways to setup and connect to a cluster. +The basic alternatives lacks most options, but can be enough for some use cases. ```c -valkeyClusterContext *cc = valkeyClusterConnect("127.0.0.1:6379", VALKEYCLUSTER_FLAG_NULL); +valkeyClusterContext *valkeyClusterConnect(const char *addrs); +valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, + const struct timeval tv); + +// There is also a convenience struct to specify various options. +valkeyClusterContext *valkeyClusterConnectWithOptions(const valkeyClusterOptions *options); +``` + +When connecting to a cluster, libvalkey will return `NULL` in the event that we can't allocate the context, and set the `err` member if we can allocate the context but there are issues. +So when connecting it's simple to handle error states. + +```c +valkeyClusterContext *cc = valkeyClusterConnect("127.0.0.1:6379,127.0.0.1:6380"); if (cc == NULL || cc->err) { fprintf(stderr, "Error: %s\n", cc ? cc->errstr : "OOM"); } ``` -When connecting to a cluster, libvalkey will return `NULL` in the event that we can't allocate the context, and set the `err` member if we can connect but there are issues. -So when connecting it's simple to handle error states. - -To be able to set options before any connection attempt is performed a `valkeyClusterContext` can be created using `valkeyClusterContextInit`. -The context is where the known cluster state and the options are kept, which can be configured using functions like -`valkeyClusterSetOptionAddNodes` to add one or many cluster node addresses, -or `valkeyClusterSetOptionUsername` together with `valkeyClusterSetOptionPassword` to configure authentication and so on. -See [`include/valkey/cluster.h`](../include/valkey/cluster.h) for more details. +### Connection options -The function `valkeyClusterConnect2` is provided to connect using the prepared `valkeyClusterContext`. +There are a variety of options you can specify when connecting to a cluster, which are delivered via the `valkeyClusterOptions` helper struct. +This includes information how to connect to the cluster and defining optional callbacks as well as other flags. +See [include/valkey/cluster.h](../include/valkey/cluster.h) for more details. ```c -valkeyClusterContext *cc = valkeyClusterContextInit(); -valkeyClusterSetOptionAddNodes(cc, "127.0.0.1:6379,127.0.0.1:6380"); -int status = valkeyClusterConnect2(cc); -if (status == VALKEY_ERR) { - printf("Error: %s\n", cc->errstr); - // handle error +valkeyClusterOptions opt = {0}; +opt.initial_nodes = "127.0.0.1:6379,127.0.0.1:6380"; // Addresses to initially connect to. +opt.options = VALKEY_OPT_USE_CLUSTER_SLOTS; // See available flags below. +opt.password = "password" // Authentication; libvalkey sends the `AUTH` command. + +// There are helper functions to set some options. +valkeyClusterOptionsEnableTLS(&opt, tlsCtx); // Use TLS and a prepared `valkeyTLSContext` when connecting. + +valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); +if (cc == NULL || cc->err) { + fprintf(stderr, "Error: %s\n", cc ? cc->errstr : "OOM"); } ``` +There are also several flags you can specify when using the `valkeyClusterOptions` helper struct. + +| Flag | Description | +| --- | --- | +| VALKEY\_OPT\_USE\_CLUSTER\_SLOTS | Tells libvalkey to use the command `CLUSTER SLOTS` when updating its slot map (cluster topology).
Libvalkey uses `CLUSTER NODES` by default. | +| VALKEY\_OPT\_USE\_REPLICAS| Tells libvalkey to keep parsed information of replica nodes. | +| VALKEY\_OPT\_BLOCKING\_INITIAL\_UPDATE | **ASYNC**: Tells libvalkey to perform the initial slot map update in a blocking fashion. The function call will wait for a slot map update before returning so that the returned context is immediately ready to accept commands. | + ### Executing commands The primary command interface is a `printf`-like function that takes a format string along with a variable number of arguments. @@ -121,6 +143,7 @@ if (valkeyClusterAppendCommand(cc, "SET foo bar") != VALKEY_OK) { fprintf(stderr, "Error appending command: %s\n", cc->errstr); exit(1); } + if (valkeyClusterAppendCommand(cc, "GET foo") != VALKEY_OK) { fprintf(stderr, "Error appending command: %s\n", cc->errstr); exit(1); @@ -128,15 +151,15 @@ if (valkeyClusterAppendCommand(cc, "GET foo") != VALKEY_OK) { valkeyReply *reply; if (valkeyClusterGetReply(cc,&reply) != VALKEY_OK) { - fprintf(stderr, "Error reading reply %zu: %s\n", i, c->errstr); - exit(1); + fprintf(stderr, "Error reading reply %zu: %s\n", i, c->errstr); + exit(1); } // Handle the reply for SET here. freeReplyObject(reply); if (valkeyClusterGetReply(cc,&reply) != VALKEY_OK) { - fprintf(stderr, "Error reading reply %zu: %s\n", i, c->errstr); - exit(1); + fprintf(stderr, "Error reading reply %zu: %s\n", i, c->errstr); + exit(1); } // Handle the reply for GET here. freeReplyObject(reply); @@ -149,10 +172,10 @@ freeReplyObject(reply); There is a hook to get notified when certain events occur. ```c -int valkeyClusterSetEventCallback(valkeyClusterContext *cc, - void(fn)(const valkeyClusterContext *cc, int event, - void *privdata), - void *privdata); +int valkeyClusterOptionsSetEventCallback(valkeyClusterOptions *options, + void(fn)(const valkeyClusterContext *cc, int event, + void *privdata), + void *privdata); ``` The callback is called with `event` set to one of the following values: @@ -172,8 +195,8 @@ This is useful for applying socket options or access endpoint information for a The callback is registered using the following function: ```c -int valkeyClusterSetConnectCallback(valkeyClusterContext *cc, - void(fn)(const valkeyContext *c, int status)); +int valkeyClusterOptionsSetConnectCallback(valkeyClusterOptions *options, + void(fn)(const valkeyContext *c, int status)); ``` The callback is called just after connect, before TLS handshake and authentication. @@ -194,18 +217,7 @@ The first alternative is to use the function `valkeyClusterAsyncConnect`, which Any command sent by the user thereafter will create a new non-blocking connection, unless a non-blocking connection already exists to the destination. The function returns a pointer to a newly created `valkeyClusterAsyncContext` struct and its `err` field should be checked to make sure the initial slot map update was successful. -```c -// Insufficient error handling for brevity. -valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnect("127.0.0.1:6379", VALKEYCLUSTER_FLAG_NULL); -if (acc->err) { - printf("error: %s\n", acc->errstr); - exit(1); -} -// Attach an event engine. In this example we use libevent. -struct event_base *base = event_base_new(); -valkeyClusterLibeventAttach(acc, base); -``` The second alternative is to use `valkeyClusterAsyncContextInit` and `valkeyClusterAsyncConnect2` which avoids the initial blocking connect. This connection alternative requires an attached event engine when `valkeyClusterAsyncConnect2` is called, but the connect and the initial slot map update is done in a non-blocking fashion. @@ -214,22 +226,12 @@ This means that commands sent directly after `valkeyClusterAsyncConnect2` may fa You may use the [`eventCallback`](#events-per-cluster-context-1) to be notified when the slot map is updated and the client is ready to accept commands. A crude example of using the `eventCallback` can be found in [this test case](../tests/ct_async.c). -```c -// Insufficient error handling for brevity. -valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(); -// Add a cluster node address for the initial connect. -valkeyClusterSetOptionAddNodes(acc->cc, "127.0.0.1:6379"); -// Attach an event engine. In this example we use libevent. -struct event_base *base = event_base_new(); -valkeyClusterLibeventAttach(acc, base); -if (valkeyClusterAsyncConnect2(acc) != VALKEY_OK) { - printf("error: %s\n", acc->errstr); - exit(1); -} -``` + +### Connection options + ### Executing commands @@ -284,18 +286,19 @@ After this, the disconnection callback is executed with the `VALKEY_OK` status a #### Events per cluster context -Use [`valkeyClusterSetEventCallback`](#events-per-cluster-context) with `acc->cc` as the context to get notified when certain events occur. +Use [`valkeyClusterOptionsSetEventCallback`](#events-per-cluster-context) to get notified when certain events occur. #### Events per connection Because the connections that will be created are non-blocking, the kernel is not able to instantly return if the specified host and port is able to accept a connection. Instead, use a connect callback to be notified when a connection is established or failed. Similarly, a disconnect callback can be used to be notified about a disconnected connection (either because of an error or per user request). -The callbacks are installed using the following functions: +The callbacks can be enabled using the following options when calling `valkeyClusterAsyncConnectWithOptions`: ```c -status = valkeyClusterAsyncSetConnectCallback(acc, callbackFn); -status = valkeyClusterAsyncSetDisconnectCallback(acc, callbackFn); +valkeyClusterOptions opt = {0}; +opt.async_connect_cb = callbackFn; +opt.async_disconnect_cb = callbackFn; ``` The connect callback function should have the following prototype, aliased to `valkeyConnectCallback`: @@ -305,14 +308,15 @@ void(valkeyAsyncContext *ac, int status); On a connection attempt, the `status` argument is set to `VALKEY_OK` when the connection was successful. The file description of the connection socket can be retrieved from a `valkeyAsyncContext` as `ac->c->fd`. -On a disconnect, the `status` argument is set to `VALKEY_OK` when disconnection was initiated by the user, or `VALKEY_ERR` when the disconnection was caused by an error. -When it is `VALKEY_ERR`, the `err` field in the context can be accessed to find out the cause of the error. -You don't need to reconnect in the disconnect callback. -libvalkey will reconnect by itself when the next command is handled. +The disconnect callback function should have the following prototype, aliased to `valkeyDisconnectCallback`: +```c +void(const valkeyAsyncContext *ac, int status); +``` -Setting the connect and disconnect callbacks can only be done once per context. -For subsequent calls it will return `VALKEY_ERR`. +On a disconnection the `status` argument is set to `VALKEY_OK` if it was initiated by the user, or to `VALKEY_ERR` when it was caused by an error. +When caused by an error the `err` field in the context can be accessed to get the error cause. +You don't need to reconnect in the disconnect callback since libvalkey will reconnect by itself when the next command is handled. ## Miscellaneous From 978e2e149613f40472440e62de850d55c2dfe294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 22 Jan 2025 01:07:14 +0100 Subject: [PATCH 07/25] fixup: correcting ct_out_of_memory_handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- tests/ct_out_of_memory_handling.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ct_out_of_memory_handling.c b/tests/ct_out_of_memory_handling.c index 9c9ee1f..33143d2 100644 --- a/tests/ct_out_of_memory_handling.c +++ b/tests/ct_out_of_memory_handling.c @@ -131,7 +131,7 @@ void test_alloc_failure_handling(void) { assert(cc == NULL); } - for (int i = 3; i < 110; ++i) { + for (int i = 3; i < 102; ++i) { successfulAllocations = i; cc = valkeyClusterConnectWithOptions(&options); assert(cc); @@ -139,7 +139,7 @@ void test_alloc_failure_handling(void) { valkeyClusterFree(cc); } - successfulAllocations = 110; + successfulAllocations = 102; cc = valkeyClusterConnectWithOptions(&options); assert(cc && cc->err == 0); } @@ -467,7 +467,7 @@ void test_alloc_failure_handling_async(void) { assert(acc == NULL); } - for (int i = 13; i < 107; ++i) { + for (int i = 13; i < 99; ++i) { successfulAllocations = i; acc = valkeyClusterAsyncConnectWithOptions(&options); ASSERT_STR_EQ(acc->errstr, "Out of memory"); @@ -475,7 +475,7 @@ void test_alloc_failure_handling_async(void) { valkeyClusterAsyncFree(acc); } - successfulAllocations = 107; + successfulAllocations = 99; acc = valkeyClusterAsyncConnectWithOptions(&options); assert(acc && acc->err == 0); } From a1f704c02f56d36e2176a47a21324be7b5166fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 29 Jan 2025 12:16:45 +0100 Subject: [PATCH 08/25] Use CLUSTER SLOTS during slotmap updates by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The option VALKEY_OPT_USE_CLUSTER_NODES can be used to change to `cluster nodes`. Signed-off-by: Björn Svensson --- docs/cluster.md | 4 ++-- docs/migration-guide.md | 3 +++ examples/cluster-async-tls.c | 1 - examples/cluster-simple.c | 1 - examples/cluster-tls.c | 1 - include/valkey/cluster.h | 6 +++--- src/cluster.c | 28 +++++++++++++-------------- tests/clusterclient.c | 8 ++++---- tests/clusterclient_async.c | 8 ++++---- tests/clusterclient_reconnect_async.c | 3 +-- tests/ct_connection.c | 2 +- tests/ct_connection_ipv6.c | 1 - tests/ct_out_of_memory_handling.c | 8 ++++---- tests/ct_specific_nodes.c | 16 +++++---------- 14 files changed, 41 insertions(+), 49 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index c5f4f3b..dd22b4f 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -60,7 +60,7 @@ See [include/valkey/cluster.h](../include/valkey/cluster.h) for more details. ```c valkeyClusterOptions opt = {0}; opt.initial_nodes = "127.0.0.1:6379,127.0.0.1:6380"; // Addresses to initially connect to. -opt.options = VALKEY_OPT_USE_CLUSTER_SLOTS; // See available flags below. +opt.options = VALKEY_OPT_USE_CLUSTER_NODES; // See available flags below. opt.password = "password" // Authentication; libvalkey sends the `AUTH` command. // There are helper functions to set some options. @@ -76,7 +76,7 @@ There are also several flags you can specify when using the `valkeyClusterOption | Flag | Description | | --- | --- | -| VALKEY\_OPT\_USE\_CLUSTER\_SLOTS | Tells libvalkey to use the command `CLUSTER SLOTS` when updating its slot map (cluster topology).
Libvalkey uses `CLUSTER NODES` by default. | +| VALKEY\_OPT\_USE\_CLUSTER\_NODES | Tells libvalkey to use the command `CLUSTER NODES` when updating its slot map (cluster topology).
Libvalkey uses `CLUSTER SLOTS` by default. | | VALKEY\_OPT\_USE\_REPLICAS| Tells libvalkey to keep parsed information of replica nodes. | | VALKEY\_OPT\_BLOCKING\_INITIAL\_UPDATE | **ASYNC**: Tells libvalkey to perform the initial slot map update in a blocking fashion. The function call will wait for a slot map update before returning so that the returned context is immediately ready to accept commands. | diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 9ec1ced..ed869a0 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -27,6 +27,9 @@ The type `sds` is removed from the public API. ## Migrating from `hiredis-cluster` 0.14.0 +The command used to update the internal slot map is changed to `CLUSTER SLOTS`. +`CLUSTER NODES` can be re-enabled through options, see `VALKEY_OPT_USE_CLUSTER_NODES`. + ### Renamed API functions * `ctx_get_by_node` is renamed to `valkeyClusterGetValkeyContext`. diff --git a/examples/cluster-async-tls.c b/examples/cluster-async-tls.c index c61c1bf..a855338 100644 --- a/examples/cluster-async-tls.c +++ b/examples/cluster-async-tls.c @@ -70,7 +70,6 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_TLS; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; valkeyClusterOptionsEnableTLS(&options, tls); diff --git a/examples/cluster-simple.c b/examples/cluster-simple.c index 4b8e269..431ca13 100644 --- a/examples/cluster-simple.c +++ b/examples/cluster-simple.c @@ -10,7 +10,6 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = "127.0.0.1:7000"; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; options.connect_timeout = &timeout; valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); diff --git a/examples/cluster-tls.c b/examples/cluster-tls.c index 9ebab72..8fb5a1e 100644 --- a/examples/cluster-tls.c +++ b/examples/cluster-tls.c @@ -27,7 +27,6 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_TLS; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; options.connect_timeout = &timeout; valkeyClusterOptionsEnableTLS(&options, tls); diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index ad1807c..235d7a0 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -146,9 +146,9 @@ typedef struct valkeyClusterNodeIterator { /* --- Configuration options --- */ -/* Enable slotmap updates using the command CLUSTER SLOTS. - * Default is the CLUSTER NODES command. */ -#define VALKEY_OPT_USE_CLUSTER_SLOTS 0x1000 +/* Enable slotmap updates using the command CLUSTER NODES. + * Default is the CLUSTER SLOTS command. */ +#define VALKEY_OPT_USE_CLUSTER_NODES 0x1000 /* Enable parsing of replica nodes. Currently not used, but the * information is added to its primary node structure. */ #define VALKEY_OPT_USE_REPLICAS 0x2000 diff --git a/src/cluster.c b/src/cluster.c index 4b38464..4c472a9 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -50,10 +50,10 @@ #include /* Make sure standalone and cluster options don't overlap. */ -vk_static_assert(VALKEY_OPT_USE_CLUSTER_SLOTS > VALKEY_OPT_LAST_SA_OPTION); +vk_static_assert(VALKEY_OPT_USE_CLUSTER_NODES > VALKEY_OPT_LAST_SA_OPTION); /* Internal option flags. */ -#define VALKEY_FLAG_USE_CLUSTER_SLOTS 0x1 +#define VALKEY_FLAG_USE_CLUSTER_NODES 0x1 #define VALKEY_FLAG_PARSE_REPLICAS 0x2 #define VALKEY_FLAG_DISCONNECTING 0x4 #define VALKEY_FLAG_BLOCKING_INITIAL_UPDATE 0x8 @@ -1027,9 +1027,9 @@ static dict *parse_cluster_nodes(valkeyClusterContext *cc, valkeyContext *c, val /* Sends CLUSTER SLOTS or CLUSTER NODES to the node with context c. */ static int clusterUpdateRouteSendCommand(valkeyClusterContext *cc, valkeyContext *c) { - const char *cmd = (cc->flags & VALKEY_FLAG_USE_CLUSTER_SLOTS ? - VALKEY_COMMAND_CLUSTER_SLOTS : - VALKEY_COMMAND_CLUSTER_NODES); + const char *cmd = (cc->flags & VALKEY_FLAG_USE_CLUSTER_NODES ? + VALKEY_COMMAND_CLUSTER_NODES : + VALKEY_COMMAND_CLUSTER_SLOTS); if (valkeyAppendCommand(c, cmd) != VALKEY_OK) { valkeyClusterSetError(cc, c->err, c->errstr); return VALKEY_ERR; @@ -1059,10 +1059,10 @@ static int clusterUpdateRouteHandleReply(valkeyClusterContext *cc, } dict *nodes; - if (cc->flags & VALKEY_FLAG_USE_CLUSTER_SLOTS) { - nodes = parse_cluster_slots(cc, c, reply); - } else { + if (cc->flags & VALKEY_FLAG_USE_CLUSTER_NODES) { nodes = parse_cluster_nodes(cc, c, reply); + } else { + nodes = parse_cluster_slots(cc, c, reply); } freeReplyObject(reply); return updateNodesAndSlotmap(cc, nodes); @@ -1275,8 +1275,8 @@ static valkeyClusterContext *valkeyClusterContextInit(const valkeyClusterOptions } cc->requests->free = listCommandFree; - if (options->options & VALKEY_OPT_USE_CLUSTER_SLOTS) { - cc->flags |= VALKEY_FLAG_USE_CLUSTER_SLOTS; + if (options->options & VALKEY_OPT_USE_CLUSTER_NODES) { + cc->flags |= VALKEY_FLAG_USE_CLUSTER_NODES; } if (options->options & VALKEY_OPT_USE_REPLICAS) { cc->flags |= VALKEY_FLAG_PARSE_REPLICAS; @@ -2967,12 +2967,12 @@ static int updateSlotMapAsync(valkeyClusterAsyncContext *acc, /* Send a command depending of config */ int status; - if (acc->cc->flags & VALKEY_FLAG_USE_CLUSTER_SLOTS) { - status = valkeyAsyncCommand(ac, clusterSlotsReplyCallback, acc, - VALKEY_COMMAND_CLUSTER_SLOTS); - } else { + if (acc->cc->flags & VALKEY_FLAG_USE_CLUSTER_NODES) { status = valkeyAsyncCommand(ac, clusterNodesReplyCallback, acc, VALKEY_COMMAND_CLUSTER_NODES); + } else { + status = valkeyAsyncCommand(ac, clusterSlotsReplyCallback, acc, + VALKEY_COMMAND_CLUSTER_SLOTS); } if (status == VALKEY_OK) { diff --git a/tests/clusterclient.c b/tests/clusterclient.c index a01fd23..cde34e1 100644 --- a/tests/clusterclient.c +++ b/tests/clusterclient.c @@ -63,7 +63,7 @@ void eventCallback(const valkeyClusterContext *cc, int event, void *privdata) { int main(int argc, char **argv) { int show_events = 0; - int use_cluster_slots = 1; + int use_cluster_nodes = 0; int send_to_all = 0; int argindex; @@ -72,7 +72,7 @@ int main(int argc, char **argv) { if (strcmp(argv[argindex], "--events") == 0) { show_events = 1; } else if (strcmp(argv[argindex], "--use-cluster-nodes") == 0) { - use_cluster_slots = 0; + use_cluster_nodes = 1; } else { fprintf(stderr, "Unknown argument: '%s'\n", argv[argindex]); exit(1); @@ -91,8 +91,8 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = initnode; options.connect_timeout = &timeout; - if (use_cluster_slots) { - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; + if (use_cluster_nodes) { + options.options = VALKEY_OPT_USE_CLUSTER_NODES; } if (show_events) { valkeyClusterOptionsSetEventCallback(&options, eventCallback, NULL); diff --git a/tests/clusterclient_async.c b/tests/clusterclient_async.c index ec64382..defc29c 100644 --- a/tests/clusterclient_async.c +++ b/tests/clusterclient_async.c @@ -237,13 +237,13 @@ void disconnectCallback(const valkeyAsyncContext *ac, int status) { } int main(int argc, char **argv) { - int use_cluster_slots = 1; // Get topology via CLUSTER SLOTS + int use_cluster_nodes = 0; int show_connection_events = 0; int optind; for (optind = 1; optind < argc && argv[optind][0] == '-'; optind++) { if (strcmp(argv[optind], "--use-cluster-nodes") == 0) { - use_cluster_slots = 0; // Use the default CLUSTER NODES instead + use_cluster_nodes = 1; } else if (strcmp(argv[optind], "--events") == 0) { show_events = 1; } else if (strcmp(argv[optind], "--connection-events") == 0) { @@ -272,8 +272,8 @@ int main(int argc, char **argv) { if (!async_initial_update) { options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; } - if (use_cluster_slots) { - options.options |= VALKEY_OPT_USE_CLUSTER_SLOTS; + if (use_cluster_nodes) { + options.options |= VALKEY_OPT_USE_CLUSTER_NODES; } if (show_connection_events) { options.async_connect_cb = connectCallback; diff --git a/tests/clusterclient_reconnect_async.c b/tests/clusterclient_reconnect_async.c index ebb0755..6af5451 100644 --- a/tests/clusterclient_reconnect_async.c +++ b/tests/clusterclient_reconnect_async.c @@ -98,8 +98,7 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = initnode; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | - VALKEY_OPT_BLOCKING_INITIAL_UPDATE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(&options); diff --git a/tests/ct_connection.c b/tests/ct_connection.c index 720be54..5388388 100644 --- a/tests/ct_connection.c +++ b/tests/ct_connection.c @@ -36,7 +36,7 @@ void test_password_ok(void) { valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); ASSERT_MSG(cc && cc->err == 0, cc ? cc->errstr : "OOM"); - assert(connect_success_counter == 1); // for CLUSTER NODES + assert(connect_success_counter == 1); // for CLUSTER SLOTS load_valkey_version(cc); assert(connect_success_counter == 2); // for checking valkey version diff --git a/tests/ct_connection_ipv6.c b/tests/ct_connection_ipv6.c index 39711f3..02723c1 100644 --- a/tests/ct_connection_ipv6.c +++ b/tests/ct_connection_ipv6.c @@ -14,7 +14,6 @@ void test_successful_ipv6_connection(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_IPV6; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; options.connect_timeout = &timeout; valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); diff --git a/tests/ct_out_of_memory_handling.c b/tests/ct_out_of_memory_handling.c index 33143d2..ca0cf28 100644 --- a/tests/ct_out_of_memory_handling.c +++ b/tests/ct_out_of_memory_handling.c @@ -131,7 +131,7 @@ void test_alloc_failure_handling(void) { assert(cc == NULL); } - for (int i = 3; i < 102; ++i) { + for (int i = 3; i < 160; ++i) { successfulAllocations = i; cc = valkeyClusterConnectWithOptions(&options); assert(cc); @@ -139,7 +139,7 @@ void test_alloc_failure_handling(void) { valkeyClusterFree(cc); } - successfulAllocations = 102; + successfulAllocations = 160; cc = valkeyClusterConnectWithOptions(&options); assert(cc && cc->err == 0); } @@ -467,7 +467,7 @@ void test_alloc_failure_handling_async(void) { assert(acc == NULL); } - for (int i = 13; i < 99; ++i) { + for (int i = 13; i < 157; ++i) { successfulAllocations = i; acc = valkeyClusterAsyncConnectWithOptions(&options); ASSERT_STR_EQ(acc->errstr, "Out of memory"); @@ -475,7 +475,7 @@ void test_alloc_failure_handling_async(void) { valkeyClusterAsyncFree(acc); } - successfulAllocations = 99; + successfulAllocations = 157; acc = valkeyClusterAsyncConnectWithOptions(&options); assert(acc && acc->err == 0); } diff --git a/tests/ct_specific_nodes.c b/tests/ct_specific_nodes.c index 8008f69..a1acbd0 100644 --- a/tests/ct_specific_nodes.c +++ b/tests/ct_specific_nodes.c @@ -322,8 +322,7 @@ void test_async_to_single_node(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | - VALKEY_OPT_BLOCKING_INITIAL_UPDATE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -354,8 +353,7 @@ void test_async_formatted_to_single_node(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | - VALKEY_OPT_BLOCKING_INITIAL_UPDATE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -387,8 +385,7 @@ void test_async_command_argv_to_single_node(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | - VALKEY_OPT_BLOCKING_INITIAL_UPDATE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -420,8 +417,7 @@ void test_async_to_all_nodes(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | - VALKEY_OPT_BLOCKING_INITIAL_UPDATE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -461,8 +457,7 @@ void test_async_transaction(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS | - VALKEY_OPT_BLOCKING_INITIAL_UPDATE; + options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; @@ -506,7 +501,6 @@ void test_async_transaction(void) { int main(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.options = VALKEY_OPT_USE_CLUSTER_SLOTS; options.max_retry = 1; valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); From 3681d8817680c6d524dd2a6cff73a26e42e52b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 29 Jan 2025 14:08:19 +0100 Subject: [PATCH 09/25] fixup: remove valkeyClusterOptionsSetEventCallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 21 +++++++++++++++------ include/valkey/cluster.h | 13 +++++++------ src/cluster.c | 12 ------------ tests/clusterclient.c | 2 +- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index dd22b4f..7346e9b 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -172,10 +172,18 @@ freeReplyObject(reply); There is a hook to get notified when certain events occur. ```c -int valkeyClusterOptionsSetEventCallback(valkeyClusterOptions *options, - void(fn)(const valkeyClusterContext *cc, int event, - void *privdata), - void *privdata); +/* Function to be called when events occur. */ +void callbackFn(const valkeyClusterContext *cc, int event, void *privdata) { + switch (event) { + // Handle event + } +} + +valkeyClusterOptions opt = {0}; +opt.event_callback = callbackFn; +opt.event_privdata = my_privdata; // User defined data can be provided to the callback. +// Set additional options... +valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); ``` The callback is called with `event` set to one of the following values: @@ -183,7 +191,7 @@ The callback is called with `event` set to one of the following values: * `VALKEYCLUSTER_EVENT_SLOTMAP_UPDATED` when the slot mapping has been updated; * `VALKEYCLUSTER_EVENT_READY` when the slot mapping has been fetched for the first time and the client is ready to accept commands, useful when initiating the - client with `valkeyClusterAsyncConnect2()` where a client is not immediately + client with `valkeyClusterAsyncConnect()` where a client is not immediately ready after a successful call; * `VALKEYCLUSTER_EVENT_FREE_CONTEXT` when the cluster context is being freed, so that the user can free the event `privdata`. @@ -286,7 +294,8 @@ After this, the disconnection callback is executed with the `VALKEY_OK` status a #### Events per cluster context -Use [`valkeyClusterOptionsSetEventCallback`](#events-per-cluster-context) to get notified when certain events occur. +Use [`event_callback` in `valkeyClusterOptions`](#events-per-cluster-context) to get notified when certain events occur. +Alternatively `valkeyClusterAsyncSetEventCallback` can be used when the `valkeyClusterAsyncContext` is required to be provided in `privdata`. #### Events per connection diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index 235d7a0..de1ba42 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -44,7 +44,7 @@ #define VALKEY_ROLE_PRIMARY 1 #define VALKEY_ROLE_REPLICA 2 -/* Events, for valkeyClusterOptionsSetEventCallback() */ +/* Events, for event_callback in valkeyClusterOptions. */ #define VALKEYCLUSTER_EVENT_SLOTMAP_UPDATED 1 #define VALKEYCLUSTER_EVENT_READY 2 #define VALKEYCLUSTER_EVENT_FREE_CONTEXT 3 @@ -165,6 +165,12 @@ typedef struct { int max_retry; /* Allowed retry attempts. */ /* Common callbacks. */ + + /* A hook to get notified when certain events occur. The `event` is set to + * VALKEYCLUSTER_EVENT_SLOTMAP_UPDATED when the slot mapping has been updated; + * VALKEYCLUSTER_EVENT_READY when the slot mapping has been fetched for the first + * time and the client is ready to accept commands; + * VALKEYCLUSTER_EVENT_FREE_CONTEXT when the cluster context is being freed. */ void (*event_callback)(const struct valkeyClusterContext *cc, int event, void *privdata); void *event_privdata; @@ -182,11 +188,6 @@ typedef struct { int (*tls_init_fn)(struct valkeyContext *, struct valkeyTLSContext *); } valkeyClusterOptions; -/* Helper functions to set options. */ -int valkeyClusterOptionsSetEventCallback(valkeyClusterOptions *options, - void(fn)(const valkeyClusterContext *cc, - int event, void *privdata), - void *privdata); /* A hook for connect and reconnect attempts, e.g. for applying additional * socket options. This is called just after connect, before TLS handshake and * Valkey authentication. diff --git a/src/cluster.c b/src/cluster.c index 4c472a9..9cc37c8 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -2120,18 +2120,6 @@ int valkeyClusterOptionsSetConnectCallback(valkeyClusterOptions *options, return VALKEY_ERR; } -int valkeyClusterOptionsSetEventCallback(valkeyClusterOptions *options, - void(fn)(const valkeyClusterContext *cc, - int event, void *privdata), - void *privdata) { - if (options->event_callback == NULL) { - options->event_callback = fn; - options->event_privdata = privdata; - return VALKEY_OK; - } - return VALKEY_ERR; -} - void *valkeyClusterFormattedCommand(valkeyClusterContext *cc, char *cmd, int len) { valkeyReply *reply = NULL; diff --git a/tests/clusterclient.c b/tests/clusterclient.c index cde34e1..97517db 100644 --- a/tests/clusterclient.c +++ b/tests/clusterclient.c @@ -95,7 +95,7 @@ int main(int argc, char **argv) { options.options = VALKEY_OPT_USE_CLUSTER_NODES; } if (show_events) { - valkeyClusterOptionsSetEventCallback(&options, eventCallback, NULL); + options.event_callback = eventCallback; } valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); From 84738012b7ce4e64dc659b81d725fde0fbce2781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 29 Jan 2025 14:32:53 +0100 Subject: [PATCH 10/25] fixup: remote valkeyClusterOptionsSetConnectCallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 12 +++++++++--- include/valkey/cluster.h | 25 +++++++++++-------------- src/cluster.c | 10 ---------- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index 7346e9b..b94271e 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -200,11 +200,17 @@ The callback is called with `event` set to one of the following values: There is a hook to get notified about connect and reconnect attempts. This is useful for applying socket options or access endpoint information for a connection to a particular node. -The callback is registered using the following function: +The callback is registered using an option: ```c -int valkeyClusterOptionsSetConnectCallback(valkeyClusterOptions *options, - void(fn)(const valkeyContext *c, int status)); +void connect_callback(const valkeyContext *c, int status) { + // Perform desired action +} + +valkeyClusterOptions opt = {0}; +opt.connect_callback = connect_callback; +// Set additional options... +valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); ``` The callback is called just after connect, before TLS handshake and authentication. diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index de1ba42..6a45ce9 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -175,6 +175,17 @@ typedef struct { void *event_privdata; /* Synchronous API callbacks. */ + + /* A hook for connect and reconnect attempts, e.g. for applying additional + * socket options. This is called just after connect, before TLS handshake and + * Valkey authentication. + * + * On successful connection, `status` is set to `VALKEY_OK` and the file + * descriptor can be accessed as `c->fd` to apply socket options. + * + * On failed connection attempt, this callback is called with `status` set to + * `VALKEY_ERR`. The `err` field in the `valkeyContext` can be used to find out + * the cause of the error. */ void (*connect_callback)(const valkeyContext *c, int status); /* Asynchronous API callbacks. */ @@ -188,20 +199,6 @@ typedef struct { int (*tls_init_fn)(struct valkeyContext *, struct valkeyTLSContext *); } valkeyClusterOptions; -/* A hook for connect and reconnect attempts, e.g. for applying additional - * socket options. This is called just after connect, before TLS handshake and - * Valkey authentication. - * - * On successful connection, `status` is set to `VALKEY_OK` and the file - * descriptor can be accessed as `c->fd` to apply socket options. - * - * On failed connection attempt, this callback is called with `status` set to - * `VALKEY_ERR`. The `err` field in the `valkeyContext` can be used to find out - * the cause of the error. */ -int valkeyClusterOptionsSetConnectCallback(valkeyClusterOptions *options, - void(fn)(const valkeyContext *c, - int status)); - /* --- Synchronous API --- */ valkeyClusterContext *valkeyClusterConnectWithOptions(const valkeyClusterOptions *options); diff --git a/src/cluster.c b/src/cluster.c index 9cc37c8..053027d 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -2110,16 +2110,6 @@ static int prepareCommand(valkeyClusterContext *cc, struct cmd *command) { return VALKEY_OK; } -int valkeyClusterOptionsSetConnectCallback(valkeyClusterOptions *options, - void(fn)(const valkeyContext *c, - int status)) { - if (options->connect_callback == NULL) { - options->connect_callback = fn; - return VALKEY_OK; - } - return VALKEY_ERR; -} - void *valkeyClusterFormattedCommand(valkeyClusterContext *cc, char *cmd, int len) { valkeyReply *reply = NULL; From a5ecc02fa39f294e23bde5b6751e4040873b8aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 29 Jan 2025 15:15:36 +0100 Subject: [PATCH 11/25] fixup: fix cluster.md review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index b94271e..45a83ef 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -36,12 +36,15 @@ The basic alternatives lacks most options, but can be enough for some use cases. valkeyClusterContext *valkeyClusterConnect(const char *addrs); valkeyClusterContext *valkeyClusterConnectWithTimeout(const char *addrs, const struct timeval tv); +``` + +There is also a convenience struct to specify various options. -// There is also a convenience struct to specify various options. +```c valkeyClusterContext *valkeyClusterConnectWithOptions(const valkeyClusterOptions *options); ``` -When connecting to a cluster, libvalkey will return `NULL` in the event that we can't allocate the context, and set the `err` member if we can allocate the context but there are issues. +When connecting to a cluster, `NULL` is returned when the context can't be allocated, or `err` and `errstr` are set in the returned allocated context when there are issues. So when connecting it's simple to handle error states. ```c @@ -53,8 +56,8 @@ if (cc == NULL || cc->err) { ### Connection options -There are a variety of options you can specify when connecting to a cluster, which are delivered via the `valkeyClusterOptions` helper struct. -This includes information how to connect to the cluster and defining optional callbacks as well as other flags. +There are a variety of options you can specify using the `valkeyClusterOptions` struct when connecting to a cluster. +This includes information about how to connect to the cluster and defining optional callbacks and other options. See [include/valkey/cluster.h](../include/valkey/cluster.h) for more details. ```c @@ -72,7 +75,7 @@ if (cc == NULL || cc->err) { } ``` -There are also several flags you can specify when using the `valkeyClusterOptions` helper struct. +There are also several flags you can specify in `valkeyClusterOptions.flags`. It's a bitwise OR of the following flags: | Flag | Description | | --- | --- | From bf83e65dd588206bfde41cbe0c10efab9592a59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 29 Jan 2025 15:40:01 +0100 Subject: [PATCH 12/25] fixup: remove helper valkeyClusterOptionsEnableTLS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change makes cluster_tls.[c.h] files superfluous. Signed-off-by: Björn Svensson --- CMakeLists.txt | 1 - Makefile | 7 +++-- docs/cluster.md | 5 ++-- examples/cluster-async-tls.c | 5 ++-- examples/cluster-tls.c | 5 ++-- include/valkey/cluster.h | 2 +- include/valkey/cluster_tls.h | 50 ------------------------------------ src/cluster_tls.c | 39 ---------------------------- 8 files changed, 12 insertions(+), 102 deletions(-) delete mode 100644 include/valkey/cluster_tls.h delete mode 100644 src/cluster_tls.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c07f2e..4501a9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,7 +161,6 @@ IF(ENABLE_TLS) ENDIF() FIND_PACKAGE(OpenSSL REQUIRED) SET(valkey_tls_sources - src/cluster_tls.c src/tls.c) ADD_LIBRARY(valkey_tls ${valkey_tls_sources}) ADD_LIBRARY(valkey::valkey_tls ALIAS valkey_tls) diff --git a/Makefile b/Makefile index 6884607..59391d2 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,8 @@ INCLUDE_DIR = include/valkey TEST_SRCS = $(TEST_DIR)/client_test.c $(TEST_DIR)/ut_parse_cmd.c $(TEST_DIR)/ut_slotmap_update.c TEST_BINS = $(patsubst $(TEST_DIR)/%.c,$(TEST_DIR)/%,$(TEST_SRCS)) -SOURCES = $(filter-out $(wildcard $(SRC_DIR)/*tls.c) $(SRC_DIR)/rdma.c, $(wildcard $(SRC_DIR)/*.c)) -HEADERS = $(filter-out $(wildcard $(INCLUDE_DIR)/*tls.h) $(INCLUDE_DIR)/rdma.h, $(wildcard $(INCLUDE_DIR)/*.h)) +SOURCES = $(filter-out $(SRC_DIR)/tls.c $(SRC_DIR)/rdma.c, $(wildcard $(SRC_DIR)/*.c)) +HEADERS = $(filter-out $(INCLUDE_DIR)/tls.h $(INCLUDE_DIR)/rdma.h, $(wildcard $(INCLUDE_DIR)/*.h)) OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SOURCES)) @@ -95,7 +95,7 @@ TLS_DYLIB_MAKE_CMD=$(CC) $(OPTIMIZATION) $(PLATFORM_FLAGS) -shared -Wl,-soname,$ USE_TLS?=0 ifeq ($(USE_TLS),1) - TLS_SOURCES = $(wildcard $(SRC_DIR)/*tls.c) + TLS_SOURCES = $(SRC_DIR)/tls.c TLS_OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(TLS_SOURCES)) # This is required for test.c only @@ -293,7 +293,6 @@ install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME) $(TLS_INSTALL) install-tls: $(TLS_DYLIBNAME) $(TLS_STLIBNAME) $(TLS_PKGCONFNAME) mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH) $(INSTALL) $(INCLUDE_DIR)/tls.h $(INSTALL_INCLUDE_PATH) - $(INSTALL) $(INCLUDE_DIR)/cluster_tls.h $(INSTALL_INCLUDE_PATH) $(INSTALL) $(TLS_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(TLS_DYLIB_MINOR_NAME) ln -sf $(TLS_DYLIB_MINOR_NAME) $(INSTALL_LIBRARY_PATH)/$(TLS_ROOT_DYLIB_NAME) ln -sf $(TLS_DYLIB_MINOR_NAME) $(INSTALL_LIBRARY_PATH)/$(TLS_DYLIB_MAJOR_NAME) diff --git a/docs/cluster.md b/docs/cluster.md index 45a83ef..36a3301 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -66,8 +66,9 @@ opt.initial_nodes = "127.0.0.1:6379,127.0.0.1:6380"; // Addresses to initially c opt.options = VALKEY_OPT_USE_CLUSTER_NODES; // See available flags below. opt.password = "password" // Authentication; libvalkey sends the `AUTH` command. -// There are helper functions to set some options. -valkeyClusterOptionsEnableTLS(&opt, tlsCtx); // Use TLS and a prepared `valkeyTLSContext` when connecting. +// Enable TLS using a prepared `valkeyTLSContext` when connecting. +options.tls = tlsCtx; +options.tls_init_fn = &valkeyInitiateTLSWithContext; valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); if (cc == NULL || cc->err) { diff --git a/examples/cluster-async-tls.c b/examples/cluster-async-tls.c index a855338..98eb0d5 100644 --- a/examples/cluster-async-tls.c +++ b/examples/cluster-async-tls.c @@ -1,5 +1,5 @@ #include -#include +#include #include @@ -72,7 +72,8 @@ int main(int argc, char **argv) { options.initial_nodes = CLUSTER_NODE_TLS; options.async_connect_cb = connectCallback; options.async_disconnect_cb = disconnectCallback; - valkeyClusterOptionsEnableTLS(&options, tls); + options.tls = tls; + options.tls_init_fn = &valkeyInitiateTLSWithContext; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); diff --git a/examples/cluster-tls.c b/examples/cluster-tls.c index 8fb5a1e..be7ac9f 100644 --- a/examples/cluster-tls.c +++ b/examples/cluster-tls.c @@ -1,7 +1,5 @@ #include -#include #include -#include #include #include @@ -28,7 +26,8 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_TLS; options.connect_timeout = &timeout; - valkeyClusterOptionsEnableTLS(&options, tls); + options.tls = tls; + options.tls_init_fn = &valkeyInitiateTLSWithContext; valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&options); if (!cc) { diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index 6a45ce9..5c289b3 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -194,7 +194,7 @@ typedef struct { int (*attach_fn)(valkeyAsyncContext *ac, void *attach_data); /* Event engine attach func. */ void *attach_data; - /* TLS context, enabled using valkeyClusterOptionsEnableTLS. */ + /* TLS context, initiated using valkeyCreateTLSContext. */ void *tls; int (*tls_init_fn)(struct valkeyContext *, struct valkeyTLSContext *); } valkeyClusterOptions; diff --git a/include/valkey/cluster_tls.h b/include/valkey/cluster_tls.h deleted file mode 100644 index f0a20ca..0000000 --- a/include/valkey/cluster_tls.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2022, Bjorn Svensson - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef VALKEY_CLUSTER_TLS_H -#define VALKEY_CLUSTER_TLS_H - -#include "cluster.h" -#include "tls.h" -#include "valkey.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Configuration option to enable TLS negotiation on a context. - */ -int valkeyClusterOptionsEnableTLS(valkeyClusterOptions *options, - valkeyTLSContext *tls); - -#ifdef __cplusplus -} -#endif - -#endif /* VALKEY_CLUSTER_TLS_H */ diff --git a/src/cluster_tls.c b/src/cluster_tls.c deleted file mode 100644 index 0c07222..0000000 --- a/src/cluster_tls.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, Bjorn Svensson - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#include "cluster_tls.h" - -int valkeyClusterOptionsEnableTLS(valkeyClusterOptions *options, - valkeyTLSContext *tls) { - if (options == NULL || tls == NULL) { - return VALKEY_ERR; - } - options->tls = tls; - options->tls_init_fn = &valkeyInitiateTLSWithContext; - return VALKEY_OK; -} From 6c88335b809ff0130a1c4430c4d6cd7e27b33033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Wed, 29 Jan 2025 15:47:28 +0100 Subject: [PATCH 13/25] fixup: Add 'bitwise' to spellcheck wordlist in CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- .github/wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/wordlist.txt b/.github/wordlist.txt index c62a233..f59b831 100644 --- a/.github/wordlist.txt +++ b/.github/wordlist.txt @@ -17,6 +17,7 @@ Autoloading backend backends behaviour +bitwise boolean bugfix CAS From 97e02b63206d49f07db9f67a4bada9f6192a39cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Thu, 30 Jan 2025 00:15:32 +0100 Subject: [PATCH 14/25] fixup: fix leak warnings during OOM testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- tests/ct_out_of_memory_handling.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/ct_out_of_memory_handling.c b/tests/ct_out_of_memory_handling.c index ca0cf28..030a321 100644 --- a/tests/ct_out_of_memory_handling.c +++ b/tests/ct_out_of_memory_handling.c @@ -131,13 +131,14 @@ void test_alloc_failure_handling(void) { assert(cc == NULL); } - for (int i = 3; i < 160; ++i) { + for (int i = 3; i < 100; ++i) { successfulAllocations = i; cc = valkeyClusterConnectWithOptions(&options); assert(cc); ASSERT_STR_EQ(cc->errstr, "Out of memory"); valkeyClusterFree(cc); } + // Skip iteration 100 to 159 since sdscatfmt give leak warnings during OOM. successfulAllocations = 160; cc = valkeyClusterConnectWithOptions(&options); @@ -467,13 +468,14 @@ void test_alloc_failure_handling_async(void) { assert(acc == NULL); } - for (int i = 13; i < 157; ++i) { + for (int i = 13; i < 100; ++i) { successfulAllocations = i; acc = valkeyClusterAsyncConnectWithOptions(&options); ASSERT_STR_EQ(acc->errstr, "Out of memory"); assert(acc != NULL); valkeyClusterAsyncFree(acc); } + // Skip iteration 100 to 156 since sdscatfmt give leak warnings during OOM. successfulAllocations = 157; acc = valkeyClusterAsyncConnectWithOptions(&options); From 28f790b77edbee3b30773b72e864df886c18dc99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Thu, 30 Jan 2025 11:52:20 +0100 Subject: [PATCH 15/25] Update migration guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/migration-guide.md | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/docs/migration-guide.md b/docs/migration-guide.md index ed869a0..cd90267 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -27,14 +27,17 @@ The type `sds` is removed from the public API. ## Migrating from `hiredis-cluster` 0.14.0 -The command used to update the internal slot map is changed to `CLUSTER SLOTS`. -`CLUSTER NODES` can be re-enabled through options, see `VALKEY_OPT_USE_CLUSTER_NODES`. +* The cluster client initiation procedure is changed and `valkeyClusterOptions` + should be used to specify options when creating a context. + The `examples` directory in this repo contains some common client initiations + that might be helpful. +* The command used to update the internal slot map is changed to `CLUSTER SLOTS`. + `CLUSTER NODES` can be re-enabled through options using `VALKEY_OPT_USE_CLUSTER_NODES`. ### Renamed API functions * `ctx_get_by_node` is renamed to `valkeyClusterGetValkeyContext`. * `actx_get_by_node` is renamed to `valkeyClusterGetValkeyAsyncContext`. -* `redisClusterAsyncSetConnectCallbackNC` is renamed to `valkeyClusterAsyncSetConnectCallback`. ### Renamed API defines @@ -44,14 +47,32 @@ The command used to update the internal slot map is changed to `CLUSTER SLOTS`. ### Removed API functions -* `redisClusterSetMaxRedirect` removed and replaced with `valkeyClusterSetOptionMaxRetry`. -* `redisClusterSetOptionAddNode` removed and replaced with `valkeyClusterSetOptionAddNodes`. - (Note the "s" in the end of the function name.) +* `redisClusterConnect2` removed, use `valkeyClusterConnectWithOptions`. +* `redisClusterContextInit` removed, use `valkeyClusterConnectWithOptions`. +* `redisClusterSetConnectCallback` removed, use `valkeyClusterOptions.connect_callback`. +* `redisClusterSetEventCallback` removed, use `valkeyClusterOptions.event_callback`. +* `redisClusterSetMaxRedirect` removed, use `valkeyClusterOptions.max_retry`. +* `redisClusterSetOptionAddNode` removed, use `valkeyClusterOptions.initial_nodes`. +* `redisClusterSetOptionAddNodes` removed, use `valkeyClusterOptions.initial_nodes`. * `redisClusterSetOptionConnectBlock` removed since it was deprecated. * `redisClusterSetOptionConnectNonBlock` removed since it was deprecated. +* `redisClusterSetOptionConnectTimeout` removed, use `valkeyClusterOptions.connect_timeout`. +* `redisClusterSetOptionMaxRetry` removed, use `valkeyClusterOptions.max_retry`. +* `redisClusterSetOptionParseSlaves` removed, use `valkeyClusterOptions.flags` and `VALKEY_OPT_USE_REPLICAS`. +* `redisClusterSetOptionPassword` removed, use `valkeyClusterOptions.password`. +* `redisClusterSetOptionRouteUseSlots` removed, the use of `CLUSTER SLOTS` is enabled by default. +* `redisClusterSetOptionUsername` removed, use `valkeyClusterOptions.username`. +* `redisClusterAsyncSetConnectCallback` removed, but `valkeyClusterOptions.async_connect_cb` can be used which accepts a non-const callback function prototype. +* `redisClusterAsyncSetConnectCallbackNC` removed, use `valkeyClusterOptions.async_connect_cb`. +* `redisClusterAsyncSetDisconnectCallback` removed, use `valkeyClusterOptions.async_disconnect_cb`. * `parse_cluster_nodes` removed from API, for internal use only. * `parse_cluster_slots` removed from API, for internal use only. -* `redisClusterAsyncSetConnectCallback` is removed, but can be replaced with `valkeyClusterAsyncSetConnectCallback` which accepts the non-const callback function prototype. + +### Removed API defines + +* `HIRCLUSTER_FLAG_NULL` removed. +* `HIRCLUSTER_FLAG_ADD_SLAVE` removed, flag can be replaced with an option, see `VALKEY_OPT_USE_REPLICAS`. +* `HIRCLUSTER_FLAG_ROUTE_USE_SLOTS` removed, the use of `CLUSTER SLOTS` is enabled by default. ### Removed support for splitting multi-key commands per slot From 03b2aaf46babef420f21d94bc200171344dbe732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Thu, 30 Jan 2025 12:16:03 +0100 Subject: [PATCH 16/25] Update docs and option name `async_connect_callback` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use similar naming as option `connect_callback` for the blocking API. Signed-off-by: Björn Svensson --- docs/cluster.md | 4 ++-- docs/migration-guide.md | 6 +++--- examples/cluster-async-tls.c | 4 ++-- examples/cluster-async.c | 4 ++-- examples/cluster-clientside-caching-async.c | 4 ++-- include/valkey/cluster.h | 19 ++++++++++++++++--- src/cluster.c | 8 ++++---- tests/clusterclient_async.c | 4 ++-- tests/ct_async.c | 4 ++-- tests/ct_async_glib.c | 4 ++-- tests/ct_async_libev.c | 4 ++-- tests/ct_async_libuv.c | 4 ++-- tests/ct_connection.c | 20 ++++++++++---------- tests/ct_out_of_memory_handling.c | 4 ++-- tests/ct_pipeline.c | 4 ++-- tests/ct_specific_nodes.c | 20 ++++++++++---------- 16 files changed, 65 insertions(+), 52 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index 36a3301..aa5e11f 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -316,8 +316,8 @@ The callbacks can be enabled using the following options when calling `valkeyClu ```c valkeyClusterOptions opt = {0}; -opt.async_connect_cb = callbackFn; -opt.async_disconnect_cb = callbackFn; +opt.async_connect_callback = callbackFn; +opt.async_disconnect_callback = callbackFn; ``` The connect callback function should have the following prototype, aliased to `valkeyConnectCallback`: diff --git a/docs/migration-guide.md b/docs/migration-guide.md index cd90267..424a7d2 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -62,9 +62,9 @@ The type `sds` is removed from the public API. * `redisClusterSetOptionPassword` removed, use `valkeyClusterOptions.password`. * `redisClusterSetOptionRouteUseSlots` removed, the use of `CLUSTER SLOTS` is enabled by default. * `redisClusterSetOptionUsername` removed, use `valkeyClusterOptions.username`. -* `redisClusterAsyncSetConnectCallback` removed, but `valkeyClusterOptions.async_connect_cb` can be used which accepts a non-const callback function prototype. -* `redisClusterAsyncSetConnectCallbackNC` removed, use `valkeyClusterOptions.async_connect_cb`. -* `redisClusterAsyncSetDisconnectCallback` removed, use `valkeyClusterOptions.async_disconnect_cb`. +* `redisClusterAsyncSetConnectCallback` removed, but `valkeyClusterOptions.async_connect_callback` can be used which accepts a non-const callback function prototype. +* `redisClusterAsyncSetConnectCallbackNC` removed, use `valkeyClusterOptions.async_connect_callback`. +* `redisClusterAsyncSetDisconnectCallback` removed, use `valkeyClusterOptions.async_disconnect_callback`. * `parse_cluster_nodes` removed from API, for internal use only. * `parse_cluster_slots` removed from API, for internal use only. diff --git a/examples/cluster-async-tls.c b/examples/cluster-async-tls.c index 98eb0d5..1425bf4 100644 --- a/examples/cluster-async-tls.c +++ b/examples/cluster-async-tls.c @@ -70,8 +70,8 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_TLS; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; options.tls = tls; options.tls_init_fn = &valkeyInitiateTLSWithContext; valkeyClusterOptionsUseLibevent(&options, base); diff --git a/examples/cluster-async.c b/examples/cluster-async.c index ef179b3..0c3bdcb 100644 --- a/examples/cluster-async.c +++ b/examples/cluster-async.c @@ -53,8 +53,8 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = "127.0.0.1:7000"; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); printf("Connecting...\n"); diff --git a/examples/cluster-clientside-caching-async.c b/examples/cluster-clientside-caching-async.c index ab4f11f..c153ef5 100644 --- a/examples/cluster-clientside-caching-async.c +++ b/examples/cluster-clientside-caching-async.c @@ -143,8 +143,8 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(&options); diff --git a/include/valkey/cluster.h b/include/valkey/cluster.h index 5c289b3..56a5eef 100644 --- a/include/valkey/cluster.h +++ b/include/valkey/cluster.h @@ -189,9 +189,22 @@ typedef struct { void (*connect_callback)(const valkeyContext *c, int status); /* Asynchronous API callbacks. */ - valkeyConnectCallback *async_connect_cb; - valkeyDisconnectCallback *async_disconnect_cb; - int (*attach_fn)(valkeyAsyncContext *ac, void *attach_data); /* Event engine attach func. */ + + /* A hook for asynchronous connect or reconnect attempts. + * + * On successful connection, `status` is set to `VALKEY_OK`. + * On failed connection attempt, this callback is called with `status` set to + * `VALKEY_ERR`. The `err` field in the `valkeyAsyncContext` can be used to + * find out the cause of the error. */ + void (*async_connect_callback)(struct valkeyAsyncContext *, int status); + + /* A hook for asynchronous disconnections. + * Called when either a connection is terminated due to an error or per + * user request. The status is set accordingly (VALKEY_OK, VALKEY_ERR). */ + void (*async_disconnect_callback)(const struct valkeyAsyncContext *, int status); + + /* Event engine attach function, initiated using a engine specific helper. */ + int (*attach_fn)(valkeyAsyncContext *ac, void *attach_data); void *attach_data; /* TLS context, initiated using valkeyCreateTLSContext. */ diff --git a/src/cluster.c b/src/cluster.c index 053027d..6878ec7 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -2773,11 +2773,11 @@ valkeyClusterAsyncContext *valkeyClusterAsyncContextInit(const valkeyClusterOpti acc->cc = cc; valkeyClusterAsyncSetError(acc, cc->err, cc->errstr); - if (options->async_connect_cb != NULL) { - acc->onConnect = options->async_connect_cb; + if (options->async_connect_callback != NULL) { + acc->onConnect = options->async_connect_callback; } - if (options->async_disconnect_cb != NULL) { - acc->onDisconnect = options->async_disconnect_cb; + if (options->async_disconnect_callback != NULL) { + acc->onDisconnect = options->async_disconnect_callback; } if (options->attach_fn != NULL) { acc->attach_fn = options->attach_fn; diff --git a/tests/clusterclient_async.c b/tests/clusterclient_async.c index defc29c..84c4c32 100644 --- a/tests/clusterclient_async.c +++ b/tests/clusterclient_async.c @@ -276,8 +276,8 @@ int main(int argc, char **argv) { options.options |= VALKEY_OPT_USE_CLUSTER_NODES; } if (show_connection_events) { - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; } valkeyClusterOptionsUseLibevent(&options, base); diff --git a/tests/ct_async.c b/tests/ct_async.c index 1eb75c0..4aff38c 100644 --- a/tests/ct_async.c +++ b/tests/ct_async.c @@ -64,8 +64,8 @@ int main(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(&options); diff --git a/tests/ct_async_glib.c b/tests/ct_async_glib.c index 750b469..34c5c89 100644 --- a/tests/ct_async_glib.c +++ b/tests/ct_async_glib.c @@ -44,8 +44,8 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseGlib(&options, context); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); diff --git a/tests/ct_async_libev.c b/tests/ct_async_libev.c index 22f517a..0a89562 100644 --- a/tests/ct_async_libev.c +++ b/tests/ct_async_libev.c @@ -38,8 +38,8 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibev(&options, EV_DEFAULT); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); diff --git a/tests/ct_async_libuv.c b/tests/ct_async_libuv.c index 481f235..8bb22d7 100644 --- a/tests/ct_async_libuv.c +++ b/tests/ct_async_libuv.c @@ -41,8 +41,8 @@ int main(int argc, char **argv) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibuv(&options, loop); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); diff --git a/tests/ct_connection.c b/tests/ct_connection.c index 5388388..b257203 100644 --- a/tests/ct_connection.c +++ b/tests/ct_connection.c @@ -299,8 +299,8 @@ void test_async_password_ok(void) { options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.password = CLUSTER_PASSWORD; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); @@ -356,8 +356,8 @@ void test_async_password_missing(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); // Password not configured @@ -387,8 +387,8 @@ void test_async_username_ok(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; options.username = "missing-user"; options.password = CLUSTER_PASSWORD; valkeyClusterOptionsUseLibevent(&options, base); @@ -428,8 +428,8 @@ void test_async_multicluster(void) { valkeyClusterOptions options1 = {0}; options1.initial_nodes = CLUSTER_NODE; options1.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; - options1.async_connect_cb = connectCallback; - options1.async_disconnect_cb = disconnectCallback; + options1.async_connect_callback = connectCallback; + options1.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options1, base); // Connect to first cluster @@ -440,8 +440,8 @@ void test_async_multicluster(void) { options2.initial_nodes = CLUSTER_NODE_WITH_PASSWORD; options2.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options2.password = CLUSTER_PASSWORD; - options2.async_connect_cb = connectCallback; - options2.async_disconnect_cb = disconnectCallback; + options2.async_connect_callback = connectCallback; + options2.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options2, base); // Connect to second cluster diff --git a/tests/ct_out_of_memory_handling.c b/tests/ct_out_of_memory_handling.c index 030a321..ad5992f 100644 --- a/tests/ct_out_of_memory_handling.c +++ b/tests/ct_out_of_memory_handling.c @@ -455,8 +455,8 @@ void test_alloc_failure_handling_async(void) { options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_USE_REPLICAS | VALKEY_OPT_BLOCKING_INITIAL_UPDATE; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); // Connect diff --git a/tests/ct_pipeline.c b/tests/ct_pipeline.c index 5d14b6b..7d55a7c 100644 --- a/tests/ct_pipeline.c +++ b/tests/ct_pipeline.c @@ -97,8 +97,8 @@ void test_async_pipeline(void) { valkeyClusterOptions options = {0}; options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); diff --git a/tests/ct_specific_nodes.c b/tests/ct_specific_nodes.c index a1acbd0..8e1063c 100644 --- a/tests/ct_specific_nodes.c +++ b/tests/ct_specific_nodes.c @@ -324,8 +324,8 @@ void test_async_to_single_node(void) { options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); @@ -355,8 +355,8 @@ void test_async_formatted_to_single_node(void) { options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); @@ -387,8 +387,8 @@ void test_async_command_argv_to_single_node(void) { options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); @@ -419,8 +419,8 @@ void test_async_to_all_nodes(void) { options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); @@ -459,8 +459,8 @@ void test_async_transaction(void) { options.initial_nodes = CLUSTER_NODE; options.options = VALKEY_OPT_BLOCKING_INITIAL_UPDATE; options.max_retry = 1; - options.async_connect_cb = connectCallback; - options.async_disconnect_cb = disconnectCallback; + options.async_connect_callback = connectCallback; + options.async_disconnect_callback = disconnectCallback; valkeyClusterOptionsUseLibevent(&options, base); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); From fa7c35dcc76cf6658943ecd494e69f862cca3471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Thu, 30 Jan 2025 12:22:48 +0100 Subject: [PATCH 17/25] fixup: spelling correction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/migration-guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 424a7d2..5830c19 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -29,8 +29,8 @@ The type `sds` is removed from the public API. * The cluster client initiation procedure is changed and `valkeyClusterOptions` should be used to specify options when creating a context. - The `examples` directory in this repo contains some common client initiations - that might be helpful. + The `examples` directory in this repository contains some common client + initiation examples that might be helpful. * The command used to update the internal slot map is changed to `CLUSTER SLOTS`. `CLUSTER NODES` can be re-enabled through options using `VALKEY_OPT_USE_CLUSTER_NODES`. From 7a5bc1a8f5c4babcce5f727f89b47868fa4097af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Thu, 30 Jan 2025 22:44:22 +0100 Subject: [PATCH 18/25] fixup: review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 10 +++++----- docs/migration-guide.md | 2 +- docs/standalone.md | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index aa5e11f..6a7848d 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -67,8 +67,8 @@ opt.options = VALKEY_OPT_USE_CLUSTER_NODES; // See available flags belo opt.password = "password" // Authentication; libvalkey sends the `AUTH` command. // Enable TLS using a prepared `valkeyTLSContext` when connecting. -options.tls = tlsCtx; -options.tls_init_fn = &valkeyInitiateTLSWithContext; +opt.tls = tlsCtx; +opt.tls_init_fn = &valkeyInitiateTLSWithContext; valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); if (cc == NULL || cc->err) { @@ -80,9 +80,9 @@ There are also several flags you can specify in `valkeyClusterOptions.flags`. It | Flag | Description | | --- | --- | -| VALKEY\_OPT\_USE\_CLUSTER\_NODES | Tells libvalkey to use the command `CLUSTER NODES` when updating its slot map (cluster topology).
Libvalkey uses `CLUSTER SLOTS` by default. | -| VALKEY\_OPT\_USE\_REPLICAS| Tells libvalkey to keep parsed information of replica nodes. | -| VALKEY\_OPT\_BLOCKING\_INITIAL\_UPDATE | **ASYNC**: Tells libvalkey to perform the initial slot map update in a blocking fashion. The function call will wait for a slot map update before returning so that the returned context is immediately ready to accept commands. | +| `VALKEY_OPT_USE_CLUSTER_NODES` | Tells libvalkey to use the command `CLUSTER NODES` when updating its slot map (cluster topology).
Libvalkey uses `CLUSTER SLOTS` by default. | +| `VALKEY_OPT_USE_REPLICAS` | Tells libvalkey to keep parsed information of replica nodes. | +| `VALKEY_OPT_BLOCKING_INITIAL_UPDATE` | **ASYNC**: Tells libvalkey to perform the initial slot map update in a blocking fashion. The function call will wait for a slot map update before returning so that the returned context is immediately ready to accept commands. | ### Executing commands diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 5830c19..8e73a29 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -31,7 +31,7 @@ The type `sds` is removed from the public API. should be used to specify options when creating a context. The `examples` directory in this repository contains some common client initiation examples that might be helpful. -* The command used to update the internal slot map is changed to `CLUSTER SLOTS`. +* The default command to update the internal slot map is changed to `CLUSTER SLOTS`. `CLUSTER NODES` can be re-enabled through options using `VALKEY_OPT_USE_CLUSTER_NODES`. ### Renamed API functions diff --git a/docs/standalone.md b/docs/standalone.md index c6e87b8..a204036 100644 --- a/docs/standalone.md +++ b/docs/standalone.md @@ -72,12 +72,12 @@ There are also several flags you can specify when using the `valkeyOptions` help | Flag | Description | | --- | --- | -| VALKEY\_OPT\_NONBLOCK | Tells libvalkey to make a non-blocking connection. | -| VALKEY\_OPT\_REUSEADDR | Tells libvalkey to set the [SO_REUSEADDR](https://man7.org/linux/man-pages/man7/socket.7.html) socket option | -| VALKEY\_OPT\_PREFER\_IPV4
VALKEY\_OPT\_PREFER_IPV6
VALKEY\_OPT\_PREFER\_IP\_UNSPEC | Informs libvalkey to either prefer IPv4 or IPv6 when invoking [getaddrinfo](https://man7.org/linux/man-pages/man3/gai_strerror.3.html). `VALKEY_OPT_PREFER_IP_UNSPEC` will cause libvalkey to specify `AF_UNSPEC` in the getaddrinfo call, which means both IPv4 and IPv6 addresses will be searched simultaneously.
Libvalkey prefers IPv4 by default. | -| VALKEY\_OPT\_NO\_PUSH\_AUTOFREE | Tells libvalkey to not install the default RESP3 PUSH handler (which just intercepts and frees the replies). This is useful in situations where you want to process these messages in-band. | -| VALKEY\_OPT\_NOAUTOFREEREPLIES | **ASYNC**: tells libvalkey not to automatically invoke `freeReplyObject` after executing the reply callback. | -| VALKEY\_OPT\_NOAUTOFREE | **ASYNC**: Tells libvalkey not to automatically free the `valkeyAsyncContext` on connection/communication failure, but only if the user makes an explicit call to `valkeyAsyncDisconnect` or `valkeyAsyncFree` | +| `VALKEY_OPT_NONBLOCK` | Tells libvalkey to make a non-blocking connection. | +| `VALKEY_OPT_REUSEADDR` | Tells libvalkey to set the [SO_REUSEADDR](https://man7.org/linux/man-pages/man7/socket.7.html) socket option | +| `VALKEY_OPT_PREFER_IPV4`
`VALKEY_OPT_PREFER_IPV6`
`VALKEY_OPT_PREFER_IP_UNSPEC` | Informs libvalkey to either prefer IPv4 or IPv6 when invoking [getaddrinfo](https://man7.org/linux/man-pages/man3/gai_strerror.3.html). `VALKEY_OPT_PREFER_IP_UNSPEC` will cause libvalkey to specify `AF_UNSPEC` in the getaddrinfo call, which means both IPv4 and IPv6 addresses will be searched simultaneously.
Libvalkey prefers IPv4 by default. | +| `VALKEY_OPT_NO_PUSH_AUTOFREE` | Tells libvalkey to not install the default RESP3 PUSH handler (which just intercepts and frees the replies). This is useful in situations where you want to process these messages in-band. | +| `VALKEY_OPT_NOAUTOFREEREPLIES` | **ASYNC**: tells libvalkey not to automatically invoke `freeReplyObject` after executing the reply callback. | +| `VALKEY_OPT_NOAUTOFREE` | **ASYNC**: Tells libvalkey not to automatically free the `valkeyAsyncContext` on connection/communication failure, but only if the user makes an explicit call to `valkeyAsyncDisconnect` or `valkeyAsyncFree` | ### Executing commands @@ -298,7 +298,7 @@ Libvalkey also has an asynchronous API which supports a great many different eve Libvalkey provides an `valkeyAsyncContext` to manage asynchronous connections which works similarly to the synchronous context. ```c -valkeyAsyncContext *ac = valkeyAsyncConnect("loalhost", 6379); +valkeyAsyncContext *ac = valkeyAsyncConnect("localhost", 6379); if (ac == NULL) { fprintf(stderr, "Error: Out of memory trying to allocate valkeyAsyncContext\n"); exit(1); From e1454375b76601cd75aaf052d3b215b25de6811b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Thu, 30 Jan 2025 23:52:55 +0100 Subject: [PATCH 19/25] Add a TLS section in cluster.md as in standalone.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index 6a7848d..efd6c6e 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -21,6 +21,7 @@ It is not intended as a complete reference. For that it's always best to refer t - [Disconnecting/cleanup](#disconnecting-cleanup-1) - [Events](#events-1) - [Miscellaneous](#miscellaneous) + - [TLS support](#tls-support) - [Cluster node iterator](#cluster-node-iterator) - [Extend the list of supported commands](#extend-the-list-of-supported-commands) - [Random number generator](#random-number-generator) @@ -66,10 +67,6 @@ opt.initial_nodes = "127.0.0.1:6379,127.0.0.1:6380"; // Addresses to initially c opt.options = VALKEY_OPT_USE_CLUSTER_NODES; // See available flags below. opt.password = "password" // Authentication; libvalkey sends the `AUTH` command. -// Enable TLS using a prepared `valkeyTLSContext` when connecting. -opt.tls = tlsCtx; -opt.tls_init_fn = &valkeyInitiateTLSWithContext; - valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); if (cc == NULL || cc->err) { fprintf(stderr, "Error: %s\n", cc ? cc->errstr : "OOM"); @@ -339,6 +336,31 @@ You don't need to reconnect in the disconnect callback since libvalkey will reco ## Miscellaneous +### TLS support + +TLS support is not enabled by default and requires an explicit build flag as described in [`README.md`](../README.md#building). + +When support is enabled, TLS can be enabled on a cluster context using a prepared `valkeyTLSContext` and the options `valkeyClusterOptions.tls` and `valkeyClusterOptions.tls_init_fn`. + +```c +// Initialize the OpenSSL library. +valkeyInitOpenSSL(); + +// Initialize a valkeyTLSContext, which holds an OpenSSL context. +valkeyTLSContext *tls = valkeyCreateTLSContext("ca.crt", NULL, "client.crt", + "client.key", NULL, NULL); +// Set options to enable TLS on context. +valkeyClusterOptions opt = { + .tls = tls; + .tls_init_fn = &valkeyInitiateTLSWithContext; +}; + +valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); +if (cc == NULL || cc->err) { + fprintf(stderr, "Error: %s\n", cc ? cc->errstr : "OOM"); +} +``` + ### Cluster node iterator A `valkeyClusterNodeIterator` can be used to iterate on all known master nodes in a cluster context. From f421006944b425f841d7d1858e71d0367877bfa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Fri, 31 Jan 2025 00:03:00 +0100 Subject: [PATCH 20/25] fixup: correcting options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index efd6c6e..02552a1 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -62,10 +62,11 @@ This includes information about how to connect to the cluster and defining optio See [include/valkey/cluster.h](../include/valkey/cluster.h) for more details. ```c -valkeyClusterOptions opt = {0}; -opt.initial_nodes = "127.0.0.1:6379,127.0.0.1:6380"; // Addresses to initially connect to. -opt.options = VALKEY_OPT_USE_CLUSTER_NODES; // See available flags below. -opt.password = "password" // Authentication; libvalkey sends the `AUTH` command. +valkeyClusterOptions opt = { + .initial_nodes = "127.0.0.1:6379,127.0.0.1:6380"; // Addresses to initially connect to. + .options = VALKEY_OPT_USE_CLUSTER_NODES; // See available option flags below. + .password = "password"; // Authenticate connections using the `AUTH` command. +}; valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); if (cc == NULL || cc->err) { @@ -73,7 +74,7 @@ if (cc == NULL || cc->err) { } ``` -There are also several flags you can specify in `valkeyClusterOptions.flags`. It's a bitwise OR of the following flags: +There are also several flags you can specify in `valkeyClusterOptions.options`. It's a bitwise OR of the following flags: | Flag | Description | | --- | --- | @@ -115,7 +116,7 @@ When there is a need to send commands to a specific node, the following low-leve valkeyReply *reply = valkeyClusterCommandToNode(cc, node, "DBSIZE"); ``` -This function handles `printf`-like arguments similar to `valkeyClusterCommand()`, but will only attempt to send the command to the given node and will not perform redirects or retries. +This function handles `printf`-like arguments similar to `valkeyClusterCommand`, but will only attempt to send the command to the given node and will not perform redirects or retries. If the command times out or the connection to the node fails, a slot map update is scheduled to be performed when the next command is sent. `valkeyClusterCommandToNode` also performs a slot map update if it has previously been scheduled. @@ -174,16 +175,16 @@ There is a hook to get notified when certain events occur. ```c /* Function to be called when events occur. */ -void callbackFn(const valkeyClusterContext *cc, int event, void *privdata) { +void event_cb(const valkeyClusterContext *cc, int event, void *privdata) { switch (event) { // Handle event } } -valkeyClusterOptions opt = {0}; -opt.event_callback = callbackFn; -opt.event_privdata = my_privdata; // User defined data can be provided to the callback. -// Set additional options... +valkeyClusterOptions opt = { + .event_callback = event_cb; + .event_privdata = my_privdata; // User defined data can be provided to the callback. +}; valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); ``` @@ -192,7 +193,7 @@ The callback is called with `event` set to one of the following values: * `VALKEYCLUSTER_EVENT_SLOTMAP_UPDATED` when the slot mapping has been updated; * `VALKEYCLUSTER_EVENT_READY` when the slot mapping has been fetched for the first time and the client is ready to accept commands, useful when initiating the - client with `valkeyClusterAsyncConnect()` where a client is not immediately + client with `valkeyClusterAsyncConnect` where a client is not immediately ready after a successful call; * `VALKEYCLUSTER_EVENT_FREE_CONTEXT` when the cluster context is being freed, so that the user can free the event `privdata`. @@ -201,16 +202,16 @@ The callback is called with `event` set to one of the following values: There is a hook to get notified about connect and reconnect attempts. This is useful for applying socket options or access endpoint information for a connection to a particular node. -The callback is registered using an option: +The callback is registered using an option. ```c -void connect_callback(const valkeyContext *c, int status) { +void connect_cb(const valkeyContext *c, int status) { // Perform desired action } -valkeyClusterOptions opt = {0}; -opt.connect_callback = connect_callback; -// Set additional options... +valkeyClusterOptions opt = { + .connect_callback = connect_cb; +}; valkeyClusterContext *cc = valkeyClusterConnectWithOptions(&opt); ``` From 3cf42b1339925b330a90e231c5be24d668904c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Mon, 3 Feb 2025 02:08:04 +0100 Subject: [PATCH 21/25] Update async API readme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 63 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index 02552a1..6b783ee 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -225,29 +225,64 @@ The `err` field in the `valkeyContext` can be used to find out the cause of the ## Asynchronous API +The asynchronous API supports a wide range of event libraries and uses [adapters](../include/valkey/adapters/) to attach to a specific event library. +Each adapter provide a convenience function that configures which event loop instance the created context will be attached to. + ### Connecting -There are two alternative ways to initiate a cluster client, which also determines how the client behaves during the initial connect. - -The first alternative is to use the function `valkeyClusterAsyncConnect`, which initially connects to the cluster in a blocking fashion and waits for the slot map before returning. -Any command sent by the user thereafter will create a new non-blocking connection, unless a non-blocking connection already exists to the destination. -The function returns a pointer to a newly created `valkeyClusterAsyncContext` struct and its `err` field should be checked to make sure the initial slot map update was successful. +To asynchronously connect to a cluster a `valkeyClusterOptions` should first be initiated with initial nodes and more, +but it's also important to configure which event library to use before calling `valkeyClusterAsyncConnectWithOptions`. +```c +valkeyClusterOptions options = { + .initial_nodes = "127.0.0.1:7000"; +}; +// Use convenience function to set which event library to use. +valkeyClusterOptionsUseLibev(&options, EV_DEFAULT); -The second alternative is to use `valkeyClusterAsyncContextInit` and `valkeyClusterAsyncConnect2` which avoids the initial blocking connect. -This connection alternative requires an attached event engine when `valkeyClusterAsyncConnect2` is called, but the connect and the initial slot map update is done in a non-blocking fashion. +// Initiate the context and start connecting to the initial nodes. +valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); +``` -This means that commands sent directly after `valkeyClusterAsyncConnect2` may fail because the initial slot map has not yet been retrieved and the client doesn't know which cluster node to send the command to. +Since an initial slotmap update is performed asynchronously any command sent directly after `valkeyClusterAsyncConnectWithOptions` may fail +because the initial slot map has not yet been retrieved and the client doesn't know which cluster node to send the command to. You may use the [`eventCallback`](#events-per-cluster-context-1) to be notified when the slot map is updated and the client is ready to accept commands. A crude example of using the `eventCallback` can be found in [this test case](../tests/ct_async.c). +Another option is to enable blocking initial slotmap updates using the option `VALKEY_OPT_BLOCKING_INITIAL_UPDATE`. +When enabled `valkeyClusterAsyncConnectWithOptions` will initially connect to the cluster in a blocking fashion and wait for the slot map before returning. +Any command sent by the user thereafter will create a new non-blocking connection, unless a non-blocking connection already exists to the destination. +The function returns a pointer to a newly created `valkeyClusterAsyncContext` struct and its `err` field should be checked to make sure the initial slot map update was successful. + +There is also a separate API to perform the context initiation and initial connect in separate steps. +This is useful when there is a need to provide an event callback with the current `valkeyClusterAsyncContext`. +The context is first initiated using `valkeyClusterAsyncContextInit` and then `valkeyClusterAsyncConnect` will initiate connection attempts. +```c +valkeyClusterOptions options = { + .initial_nodes = "127.0.0.1:7000"; +}; +valkeyClusterOptionsUseLibev(&options, EV_DEFAULT); +// Initiate the context. +valkeyClusterAsyncContext *acc = valkeyClusterAsyncContextInit(&options); +// Set the event callback using the context as privdata. +valkeyClusterAsyncSetEventCallback(acc, eventCallback, acc); + +// Start connecting to the initial nodes. +valkeyClusterAsyncConnect(acc) +``` ### Connection options +There are a variety of options you can specify using the `valkeyClusterOptions` struct when connecting to a cluster. + +One async API specific option is `VALKEY_OPT_BLOCKING_INITIAL_UPDATE` which enables the initial slot map update to be performed in a blocking fashion. +The connect function will wait for a slot map update before returning so that the returned context is immediately ready to accept commands. + +See previous [Connection options](#connection-options) section for common options. ### Executing commands @@ -303,7 +338,10 @@ After this, the disconnection callback is executed with the `VALKEY_OK` status a #### Events per cluster context Use [`event_callback` in `valkeyClusterOptions`](#events-per-cluster-context) to get notified when certain events occur. -Alternatively `valkeyClusterAsyncSetEventCallback` can be used when the `valkeyClusterAsyncContext` is required to be provided in `privdata`. + +When the callback function requires the current `valkeyClusterAsyncContext` it can be provided in the `privdata`. +In this case initiate the context using `valkeyClusterAsyncContextInit`, set the callback and `privdata` using `valkeyClusterAsyncSetEventCallback`, +and initate connection attempts using `valkeyClusterAsyncConnect` as described under the [Connecting](#connecting-1) section. #### Events per connection @@ -313,9 +351,10 @@ Similarly, a disconnect callback can be used to be notified about a disconnected The callbacks can be enabled using the following options when calling `valkeyClusterAsyncConnectWithOptions`: ```c -valkeyClusterOptions opt = {0}; -opt.async_connect_callback = callbackFn; -opt.async_disconnect_callback = callbackFn; +valkeyClusterOptions opt = { + .async_connect_callback = connect_cb; + .async_disconnect_callback = disconnect_cb; +} ``` The connect callback function should have the following prototype, aliased to `valkeyConnectCallback`: From d57b28ad46b242a867f806d8b6dfc332b4d8cfcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Mon, 3 Feb 2025 02:11:12 +0100 Subject: [PATCH 22/25] fixup: spelling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/cluster.md b/docs/cluster.md index 6b783ee..7fe3fd2 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -245,12 +245,12 @@ valkeyClusterOptionsUseLibev(&options, EV_DEFAULT); valkeyClusterAsyncContext *acc = valkeyClusterAsyncConnectWithOptions(&options); ``` -Since an initial slotmap update is performed asynchronously any command sent directly after `valkeyClusterAsyncConnectWithOptions` may fail +Since an initial slot map update is performed asynchronously any command sent directly after `valkeyClusterAsyncConnectWithOptions` may fail because the initial slot map has not yet been retrieved and the client doesn't know which cluster node to send the command to. You may use the [`eventCallback`](#events-per-cluster-context-1) to be notified when the slot map is updated and the client is ready to accept commands. A crude example of using the `eventCallback` can be found in [this test case](../tests/ct_async.c). -Another option is to enable blocking initial slotmap updates using the option `VALKEY_OPT_BLOCKING_INITIAL_UPDATE`. +Another option is to enable blocking initial slot map updates using the option `VALKEY_OPT_BLOCKING_INITIAL_UPDATE`. When enabled `valkeyClusterAsyncConnectWithOptions` will initially connect to the cluster in a blocking fashion and wait for the slot map before returning. Any command sent by the user thereafter will create a new non-blocking connection, unless a non-blocking connection already exists to the destination. The function returns a pointer to a newly created `valkeyClusterAsyncContext` struct and its `err` field should be checked to make sure the initial slot map update was successful. @@ -279,7 +279,7 @@ valkeyClusterAsyncConnect(acc) There are a variety of options you can specify using the `valkeyClusterOptions` struct when connecting to a cluster. -One async API specific option is `VALKEY_OPT_BLOCKING_INITIAL_UPDATE` which enables the initial slot map update to be performed in a blocking fashion. +One asynchronous API specific option is `VALKEY_OPT_BLOCKING_INITIAL_UPDATE` which enables the initial slot map update to be performed in a blocking fashion. The connect function will wait for a slot map update before returning so that the returned context is immediately ready to accept commands. See previous [Connection options](#connection-options) section for common options. @@ -341,7 +341,7 @@ Use [`event_callback` in `valkeyClusterOptions`](#events-per-cluster-context) to When the callback function requires the current `valkeyClusterAsyncContext` it can be provided in the `privdata`. In this case initiate the context using `valkeyClusterAsyncContextInit`, set the callback and `privdata` using `valkeyClusterAsyncSetEventCallback`, -and initate connection attempts using `valkeyClusterAsyncConnect` as described under the [Connecting](#connecting-1) section. +and initiate connection attempts using `valkeyClusterAsyncConnect` as described under the [Connecting](#connecting-1) section. #### Events per connection From 5270d934e0ecaeb7e27e5a83bee443030f2ad043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Mon, 3 Feb 2025 11:42:00 +0100 Subject: [PATCH 23/25] fixup: correcting grammar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/cluster.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cluster.md b/docs/cluster.md index 7fe3fd2..a08ef36 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -277,7 +277,7 @@ valkeyClusterAsyncConnect(acc) ### Connection options -There are a variety of options you can specify using the `valkeyClusterOptions` struct when connecting to a cluster. +There is a variety of options you can specify using the `valkeyClusterOptions` struct when connecting to a cluster. One asynchronous API specific option is `VALKEY_OPT_BLOCKING_INITIAL_UPDATE` which enables the initial slot map update to be performed in a blocking fashion. The connect function will wait for a slot map update before returning so that the returned context is immediately ready to accept commands. From c29e0db09a441cfccb2fa44cd397f4bd58132535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Mon, 3 Feb 2025 12:01:23 +0100 Subject: [PATCH 24/25] fixup: add markdown links in migration guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/migration-guide.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 8e73a29..422f222 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -29,7 +29,10 @@ The type `sds` is removed from the public API. * The cluster client initiation procedure is changed and `valkeyClusterOptions` should be used to specify options when creating a context. - The `examples` directory in this repository contains some common client + See documentation for configuration examples when using the + [Synchronous API](cluster.md#connection-options) or the + [Asynchronous API](cluster.md#connecting-1). + The [examples](../examples/) directory also contains some common client initiation examples that might be helpful. * The default command to update the internal slot map is changed to `CLUSTER SLOTS`. `CLUSTER NODES` can be re-enabled through options using `VALKEY_OPT_USE_CLUSTER_NODES`. From 80022643ba44d4d492eabbfbc600dfca64594f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= Date: Mon, 3 Feb 2025 12:16:21 +0100 Subject: [PATCH 25/25] fixup: link to heading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Björn Svensson --- docs/migration-guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/migration-guide.md b/docs/migration-guide.md index 422f222..afa1c34 100644 --- a/docs/migration-guide.md +++ b/docs/migration-guide.md @@ -30,8 +30,8 @@ The type `sds` is removed from the public API. * The cluster client initiation procedure is changed and `valkeyClusterOptions` should be used to specify options when creating a context. See documentation for configuration examples when using the - [Synchronous API](cluster.md#connection-options) or the - [Asynchronous API](cluster.md#connecting-1). + [Synchronous API](cluster.md#synchronous-api) or the + [Asynchronous API](cluster.md#asynchronous-api). The [examples](../examples/) directory also contains some common client initiation examples that might be helpful. * The default command to update the internal slot map is changed to `CLUSTER SLOTS`.