diff --git a/README.md b/README.md index 03c21f6b7..3e7910059 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ are also included. ### Red Hat Enterprise Linux, CentOS and Amazon Linux - $ sudo yum install openssl-devel glibc-devel autoconf automake libtool + $ sudo yum install openssl-devel glibc-devel autoconf automake libtool libz-devel [Optional:] $ sudo yum install gcc-c++ graphviz rpm-build ### Fedora - $ sudo yum install openssl-devel glibc-devel autoconf automake libtool + $ sudo yum install openssl-devel glibc-devel autoconf automake libtool libz-devel [Optional:] $ sudo yum install gcc-c++ graphviz rpm-build diff --git a/src/include/aerospike/as_event.h b/src/include/aerospike/as_event.h index 261564152..9e0849140 100644 --- a/src/include/aerospike/as_event.h +++ b/src/include/aerospike/as_event.h @@ -164,10 +164,12 @@ as_policy_event_init(as_policy_event* policy) } /** - * Create new event loops with default event policy. + * Create new aerospike internal event loops with default event policy. These event loops are used + * exclusively for aerospike database commands and are not shared with the application for other + * tasks. If shared event loops are desired, use as_event_set_external_loop_capacity() and + * as_event_set_external_loop() instead. * - * This method should only be called when async client commands will be used and the calling program - * itself is not async. If this method is used, it must be called before aerospike_connect(). + * This function must be called before aerospike_connect(). * * @param capacity Number of event loops to create. * @return Event loop array. @@ -178,10 +180,12 @@ AS_EXTERN as_event_loop* as_event_create_loops(uint32_t capacity); /** - * Create new event loops with specified event policy. - * - * This method should only be called when async client commands will be used and the calling program - * itself is not async. If this method is used, it must be called before aerospike_connect(). + * Create new aerospike internal event loops with specified event policy. These event loops are used + * exclusively for aerospike database commands and are not shared with the application for other + * tasks. If shared event loops are desired, use as_event_set_external_loop_capacity() and + * as_set_external_event_loop() instead. + * + * This function must be called before aerospike_connect(). * * @param err The as_error to be populated if an error occurs. * @param policy Event loop configuration. Pass in NULL for default configuration. @@ -195,12 +199,13 @@ AS_EXTERN as_status as_create_event_loops(as_error* err, as_policy_event* policy, uint32_t capacity, as_event_loop** event_loops); /** - * Set the number of externally created event loops. This method should be called when the - * calling program wants to share event loops with the client. This reduces resource usage and + * Set the number of aerospike external event loops. This method should be called when the + * application wants to share event loops with the client. This reduces resource usage and * can increase performance. * - * This method is used in conjunction with as_event_set_external_loop() to fully define the - * the external loop to the client and obtain a reference the client's event loop abstraction. + * This method is used in conjunction with as_event_set_external_loop() or + * as_set_external_event_loop() to fully define the the external loop to the client and obtain a + * reference to the client's event loop abstraction. * * ~~~~~~~~~~{.c} * struct { @@ -238,15 +243,15 @@ AS_EXTERN bool as_event_set_external_loop_capacity(uint32_t capacity); /** - * Register an external event loop with the client with default event policy. - * + * Register an aerospike external event loop with the client with default event policy. + * * This method should be called when the calling program wants to share event loops with the client. * This reduces resource usage and can increase performance. * * This method must be called in the same thread as the event loop that is being registered. * * This method is used in conjunction with as_event_set_external_loop_capacity() to fully define - * the external loop to the client and obtain a reference the client's event loop abstraction. + * the external loop to the client and obtain a reference to the client's event loop abstraction. * * ~~~~~~~~~~{.c} * struct { @@ -285,7 +290,7 @@ AS_EXTERN as_event_loop* as_event_set_external_loop(void* loop); /** - * Register an external event loop with the client with specified event policy. + * Register an aerospike external event loop with the client with specified event policy. * * This method should be called when the calling program wants to share event loops with the client. * This reduces resource usage and can increase performance. @@ -293,7 +298,7 @@ as_event_set_external_loop(void* loop); * This method must be called in the same thread as the event loop that is being registered. * * This method is used in conjunction with as_event_set_external_loop_capacity() to fully define - * the external loop to the client and obtain a reference the client's event loop abstraction. + * the external loop to the client and obtain a reference to the client's event loop abstraction. * * ~~~~~~~~~~{.c} * struct { diff --git a/src/include/aerospike/as_peers.h b/src/include/aerospike/as_peers.h index 89aa4b548..b095b0153 100644 --- a/src/include/aerospike/as_peers.h +++ b/src/include/aerospike/as_peers.h @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 Aerospike, Inc. + * Copyright 2008-2024 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. @@ -32,6 +32,7 @@ extern "C" { typedef struct as_peers_s { as_vector /* as_node* */ nodes; + as_vector /* as_node */ nodes_to_remove; as_vector /* as_host */ invalid_hosts; uint32_t refresh_count; bool gen_changed; diff --git a/src/include/aerospike/as_query.h b/src/include/aerospike/as_query.h index cfdc1903b..7d74dfbae 100644 --- a/src/include/aerospike/as_query.h +++ b/src/include/aerospike/as_query.h @@ -859,7 +859,13 @@ as_query_apply(as_query* query, const char* module, const char* function, const /** * Set if records should be read in pages in conjunction with max_records policy. - * + * If true, the client will save the status of all partitions after the query completes. + * The partition status can be used to resume the query if terminated early by + * error, user callback, or max_records being reached. Use as_query_set_partitions() + * or as_partition_filter_set_partitions() to resume a query. + * + * The partition status will be destroyed when as_query_destroy() is called. + * * @relates as_query * @ingroup query_operations */ diff --git a/src/include/aerospike/as_status.h b/src/include/aerospike/as_status.h index 33dad8656..7f1e66f53 100644 --- a/src/include/aerospike/as_status.h +++ b/src/include/aerospike/as_status.h @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 Aerospike, Inc. + * Copyright 2008-2024 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. @@ -280,6 +280,11 @@ typedef enum as_status_e { */ AEROSPIKE_LOST_CONFLICT = 28, + /** + * Write can't complete until XDR finishes shipping. + */ + AEROSPIKE_XDR_KEY_BUSY = 32, + /** * There are no more records left for query. */ diff --git a/src/include/aerospike/version.h b/src/include/aerospike/version.h index 107d07d7c..66027292e 100644 --- a/src/include/aerospike/version.h +++ b/src/include/aerospike/version.h @@ -3,6 +3,6 @@ // N: minor // P: patch // B: build id -#define AEROSPIKE_CLIENT_VERSION 606030000L +#define AEROSPIKE_CLIENT_VERSION 606040000L extern char* aerospike_client_version; diff --git a/src/main/aerospike/as_cluster.c b/src/main/aerospike/as_cluster.c index 811f74296..66426bd32 100644 --- a/src/main/aerospike/as_cluster.c +++ b/src/main/aerospike/as_cluster.c @@ -438,8 +438,10 @@ as_cluster_seed_node(as_cluster* cluster, as_error* err, as_peers* peers, bool e } static void -as_cluster_find_nodes_to_remove(as_cluster* cluster, uint32_t refresh_count, as_vector* /* */ nodes_to_remove) +as_cluster_find_nodes_to_remove(as_cluster* cluster, as_peers* peers) { + uint32_t refresh_count = peers->refresh_count; + as_vector* nodes_to_remove = &peers->nodes_to_remove; as_nodes* nodes = cluster->nodes; for (uint32_t i = 0; i < nodes->size; i++) { @@ -810,6 +812,7 @@ static void as_cluster_destroy_peers(as_peers* peers) { as_vector_destroy(&peers->nodes); + as_vector_destroy(&peers->nodes_to_remove); as_vector* invalid_hosts = &peers->invalid_hosts; @@ -860,6 +863,7 @@ as_cluster_tend(as_cluster* cluster, as_error* err, bool is_init) as_error error_local; as_peers peers; as_vector_inita(&peers.nodes, sizeof(as_node*), 16); + as_vector_inita(&peers.nodes_to_remove, sizeof(as_node*), 8); as_vector_inita(&peers.invalid_hosts, sizeof(as_host), 4); peers.refresh_count = 0; peers.gen_changed = false; @@ -953,17 +957,13 @@ as_cluster_tend(as_cluster* cluster, as_error* err, bool is_init) } // Remove nodes determined by refreshed peers. - as_vector nodes_to_remove; - as_vector_inita(&nodes_to_remove, sizeof(as_node*), nodes->size); + as_cluster_find_nodes_to_remove(cluster, &peers); - as_cluster_find_nodes_to_remove(cluster, peers.refresh_count, &nodes_to_remove); - // Remove nodes in a batch. - if (nodes_to_remove.size > 0) { - as_cluster_remove_nodes(cluster, &nodes_to_remove); + if (peers.nodes_to_remove.size > 0) { + as_cluster_remove_nodes(cluster, &peers.nodes_to_remove); nodes = cluster->nodes; } - as_vector_destroy(&nodes_to_remove); } // Add peer nodes to cluster. diff --git a/src/main/aerospike/as_error.c b/src/main/aerospike/as_error.c index 4dc50bcbb..e8b2bfddc 100644 --- a/src/main/aerospike/as_error.c +++ b/src/main/aerospike/as_error.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 Aerospike, Inc. + * Copyright 2008-2024 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. @@ -77,6 +77,7 @@ as_error_string(as_status status) CASE_ASSIGN(AEROSPIKE_ERR_OP_NOT_APPLICABLE); CASE_ASSIGN(AEROSPIKE_FILTERED_OUT); CASE_ASSIGN(AEROSPIKE_LOST_CONFLICT); + CASE_ASSIGN(AEROSPIKE_XDR_KEY_BUSY); CASE_ASSIGN(AEROSPIKE_SECURITY_NOT_SUPPORTED); CASE_ASSIGN(AEROSPIKE_SECURITY_NOT_ENABLED); CASE_ASSIGN(AEROSPIKE_SECURITY_SCHEME_NOT_SUPPORTED); diff --git a/src/main/aerospike/as_info.c b/src/main/aerospike/as_info.c index 597926656..42290079b 100644 --- a/src/main/aerospike/as_info.c +++ b/src/main/aerospike/as_info.c @@ -29,6 +29,30 @@ * STATIC FUNCTIONS *****************************************************************************/ +static void +as_info_decode_error(char* begin) +{ + // Decode base64 message in place. + // UDF error format: ;file=;line=;message=\n + char* msg = strstr(begin, "message="); + + if (msg) { + msg += 8; + + uint32_t src_len = (uint32_t)strlen(msg); + + if (msg[src_len-1] == '\n') { + src_len--; // Ignore newline '\n' at the end + } + + uint32_t trg_len = 0; + + if (cf_b64_validate_and_decode_in_place((uint8_t*)msg, src_len, &trg_len)) { + msg[trg_len] = 0; + } + } +} + static as_status as_info_parse_error(char* begin, char** message) { @@ -60,6 +84,9 @@ as_info_parse_error(char* begin, char** message) if (rc == 0) { rc = AEROSPIKE_ERR_SERVER; } + + // Handle new error format for "udf-put" command. + as_info_decode_error(p); } else { *message = begin; @@ -68,25 +95,6 @@ as_info_parse_error(char* begin, char** message) return rc; } -static void -as_info_decode_error(char* begin) -{ - // Decode base64 message in place. - // UDF error format: ;file=;line=;message=\n - char* msg = strstr(begin, "message="); - - if (msg) { - msg += 8; - - uint32_t src_len = (uint32_t)strlen(msg) - 1; // Ignore newline '\n' at the end - uint32_t trg_len = 0; - - if (cf_b64_validate_and_decode_in_place((uint8_t*)msg, src_len, &trg_len)) { - msg[trg_len] = 0; - } - } -} - static bool as_info_keep_connection(as_status status) { diff --git a/src/main/aerospike/as_peers.c b/src/main/aerospike/as_peers.c index 63539fed6..802c62cde 100644 --- a/src/main/aerospike/as_peers.c +++ b/src/main/aerospike/as_peers.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2021 Aerospike, Inc. + * Copyright 2008-2024 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. @@ -24,9 +24,9 @@ const char* as_cluster_get_alternate_host(as_cluster* cluster, const char* hostname); -/****************************************************************************** - * FUNCTIONS - *****************************************************************************/ +//--------------------------------- +// Functions +//--------------------------------- as_node* as_peers_find_local_node(as_vector* nodes, const char* name) @@ -62,14 +62,44 @@ as_peers_find_cluster_node(as_cluster* cluster, const char* name) } static bool -as_peers_find_node(as_peers* peers, as_cluster* cluster, const char* name) +as_peers_find_node( + as_peers* peers, as_cluster* cluster, const char* name, as_vector* hosts, + as_node** replace_node + ) { // Check global node map for existing cluster. as_node* node = as_peers_find_cluster_node(cluster, name); if (node) { - node->friends++; - return true; + // Node name found. + as_address* address = as_node_get_address(node); + + if (node->failures == 0 || as_address_is_local((struct sockaddr*)&address->addr)) { + // If the node does not have cluster tend errors or is localhost, + // reject new peer as the IP address does not need to change. + node->friends++; + return true; + } + + // Retrieve node's IP address and port. + char addr_name[AS_IP_ADDRESS_SIZE]; + as_address_short_name((struct sockaddr*)&address->addr, addr_name, sizeof(addr_name)); + uint16_t port = as_address_port((struct sockaddr*)&address->addr); + + // Match peer hosts with node's IP address and port. + for (uint32_t i = 0; i < hosts->size; i++) { + as_host* host = as_vector_get(hosts, i); + + if (strcmp(host->name, addr_name) == 0 && host->port == port) { + // Main node host is also the same as one of the peer hosts. + // Peer should not be added. + node->friends++; + return true; + } + } + + // Node should be replaced with a new node same name and new IP address. + *replace_node = node; } // Check local node map for this tend iteration. @@ -77,6 +107,7 @@ as_peers_find_node(as_peers* peers, as_cluster* cluster, const char* name) if (node) { node->friends++; + *replace_node = NULL; return true; } return false; @@ -165,6 +196,21 @@ as_peers_validate_node( return validated; } +static bool +as_peers_validate(as_peers* peers, as_cluster* cluster, as_vector* hosts, const char* expected_name) +{ + for (uint32_t i = 0; i < hosts->size; i++) { + as_host* host = as_vector_get(hosts, i); + + host->name = (char*)as_cluster_get_alternate_host(cluster, host->name); + + if (as_peers_validate_node(peers, cluster, host, expected_name)) { + return true; + } + } + return false; +} + static as_status as_peers_expected_error(as_error* err, char expected, const char* p) { @@ -186,6 +232,7 @@ as_peers_parse_host(char* p, as_host* host, char* last) p++; host->port = (uint16_t)strtol(p, &p, 10); } + *last = *p; return p; } p++; @@ -198,6 +245,7 @@ as_peers_parse_host(char* p, as_host* host, char* last) if (*p == ':') { *p++ = 0; host->port = (uint16_t)strtol(p, &p, 10); + *last = *p; return p; } else if (*p == ',' || *p == ']') { @@ -208,7 +256,7 @@ as_peers_parse_host(char* p, as_host* host, char* last) p++; } } - return 0; + return NULL; } as_status @@ -251,11 +299,15 @@ as_peers_parse_peers(as_peers* peers, as_error* err, as_cluster* cluster, as_nod return AEROSPIKE_OK; } - bool peers_validated = true; + as_vector hosts; + as_vector_inita(&hosts, sizeof(as_host), 16); + + bool change_peers_generation = true; while (*p) { // Parse peer if (*p != '[') { + as_vector_destroy(&hosts); return as_peers_expected_error(err, '[', p); } p++; @@ -270,13 +322,6 @@ as_peers_parse_peers(as_peers* peers, as_error* err, as_cluster* cluster, as_nod p++; } - bool node_validated = false; - - if (as_peers_find_node(peers, cluster, node_name)) { - // Node already exists. Do not even try to connect to hosts. - node_validated = true; - } - node->peers_count++; // Parse peer TLS name @@ -291,65 +336,74 @@ as_peers_parse_peers(as_peers* peers, as_error* err, as_cluster* cluster, as_nod // Parse peer hosts if (*p != '[') { + as_vector_destroy(&hosts); return as_peers_expected_error(err, '[', p); } p++; - while (*p) { - if (*p == ']') { - p++; - break; - } - + as_vector_clear(&hosts); + + while (*p && *p != ']') { // Parse host as_host host = {.name = NULL, .tls_name = tls_name, .port = default_port}; char last = 0; p = as_peers_parse_host(p, &host, &last); - + if (! p) { + as_vector_destroy(&hosts); return as_error_update(err, AEROSPIKE_ERR_CLIENT, "Invalid peers host: %s", host.name); } - // Only add the first host that works for a node. - if (! node_validated) { - // Check global aliases for existing cluster. - host.name = (char*)as_cluster_get_alternate_host(cluster, host.name); - node_validated = as_peers_validate_node(peers, cluster, &host, node_name); - } - - if (last) { - *p = last; - } - - if (*p == ',') { - p++; + if (last == ']') { + as_vector_append(&hosts, &host); + break; } - else if (*p != ']') { + + if (last != ',') { + as_vector_destroy(&hosts); return as_peers_expected_error(err, ',', p); } + + p++; + as_vector_append(&hosts, &host); } + p++; + + as_node* replace_node = NULL; + bool node_found = as_peers_find_node(peers, cluster, node_name, &hosts, &replace_node); - if (! node_validated) { - peers_validated = false; + if (! node_found) { + if (as_peers_validate(peers, cluster, &hosts, node_name)) { + if (replace_node) { + as_vector_append(&peers->nodes_to_remove, &replace_node); + } + } + else { + change_peers_generation = false; + } } if (*p != ']') { + as_vector_destroy(&hosts); return as_peers_expected_error(err, ']', p); } p++; if (*p == ']') { // Only set new peers generation if all referenced peers are added to the cluster. - if (peers_validated) { + if (change_peers_generation) { node->peers_generation = generation; } + as_vector_destroy(&hosts); return AEROSPIKE_OK; } if (*p != ',') { + as_vector_destroy(&hosts); return as_peers_expected_error(err, ',', p); } p++; } + as_vector_destroy(&hosts); return as_error_update(err, AEROSPIKE_ERR_CLIENT, "Invalid peers host: %s", buf); } diff --git a/src/main/aerospike/version.c b/src/main/aerospike/version.c index 95d9680c3..91a203659 100644 --- a/src/main/aerospike/version.c +++ b/src/main/aerospike/version.c @@ -1 +1 @@ -char* aerospike_client_version = "6.6.3"; +char* aerospike_client_version = "6.6.4"; diff --git a/src/test/aerospike_query/query_background.c b/src/test/aerospike_query/query_background.c index 77a7d638d..617d331fc 100644 --- a/src/test/aerospike_query/query_background.c +++ b/src/test/aerospike_query/query_background.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2023 Aerospike, Inc. + * Copyright 2008-2024 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. @@ -62,7 +62,7 @@ extern bool g_has_ttl; *****************************************************************************/ static bool -write_recs() +write_recs(void) { as_error err; as_key key; diff --git a/src/test/aerospike_test.c b/src/test/aerospike_test.c index b7b287b2a..8bf1fbcc5 100644 --- a/src/test/aerospike_test.c +++ b/src/test/aerospike_test.c @@ -67,7 +67,7 @@ as_client_log_callback(as_log_level level, const char * func, const char * file, } static void -usage() +usage(void) { fprintf(stderr, "Usage:\n"); fprintf(stderr, " -h, --host [:][:],... Default: 127.0.0.1\n"); diff --git a/src/test/util/map_rec.c b/src/test/util/map_rec.c index ba200e381..8b14dbff6 100644 --- a/src/test/util/map_rec.c +++ b/src/test/util/map_rec.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2018 Aerospike, Inc. + * Copyright 2008-2024 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. @@ -53,7 +53,7 @@ const as_rec_hooks map_rec_hooks = { * FUNCTIONS *****************************************************************************/ -as_rec * map_rec_new() { +as_rec * map_rec_new(void) { as_map * m = (as_map *) as_hashmap_new(32); return as_rec_new(m, &map_rec_hooks); } diff --git a/src/test/util/test_aerospike.c b/src/test/util/test_aerospike.c index 4cbcd690d..33432d58b 100644 --- a/src/test/util/test_aerospike.c +++ b/src/test/util/test_aerospike.c @@ -1,5 +1,5 @@ /* - * Copyright 2008-2018 Aerospike, Inc. + * Copyright 2008-2024 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. @@ -41,7 +41,7 @@ static const as_aerospike_hooks test_aerospike_hooks = { * STATIC FUNCTIONS *****************************************************************************/ -as_aerospike * test_aerospike_new() { +as_aerospike * test_aerospike_new(void) { return as_aerospike_new(NULL, &test_aerospike_hooks); } diff --git a/vs/aerospike-client-c-libevent.nuspec b/vs/aerospike-client-c-libevent.nuspec index 5b49c37c9..09d86e675 100644 --- a/vs/aerospike-client-c-libevent.nuspec +++ b/vs/aerospike-client-c-libevent.nuspec @@ -2,7 +2,7 @@ aerospike-client-c-libevent - 6.6.3 + 6.6.4 Aerospike C Client with libevent Aerospike Aerospike diff --git a/vs/aerospike-client-c-libuv.nuspec b/vs/aerospike-client-c-libuv.nuspec index 1b9270552..da401ea5c 100644 --- a/vs/aerospike-client-c-libuv.nuspec +++ b/vs/aerospike-client-c-libuv.nuspec @@ -2,7 +2,7 @@ aerospike-client-c-libuv - 6.6.3 + 6.6.4 Aerospike C Client with libuv Aerospike Aerospike diff --git a/vs/aerospike-client-c.nuspec b/vs/aerospike-client-c.nuspec index 43998310e..02f8da0b3 100644 --- a/vs/aerospike-client-c.nuspec +++ b/vs/aerospike-client-c.nuspec @@ -2,7 +2,7 @@ aerospike-client-c - 6.6.3 + 6.6.4 Aerospike C Client Aerospike Aerospike