static as_status
as_admin_execute(aerospike* as, as_error* err, const as_policy_admin* policy, uint8_t* buffer, uint8_t* end)
{
    uint32_t timeout_ms = (policy)? policy->timeout : as->config.policies.admin.timeout;
    if (timeout_ms <= 0) {
        timeout_ms = DEFAULT_TIMEOUT;
    }
    uint64_t deadline_ms = as_socket_deadline(timeout_ms);
    as_cluster* cluster = as->cluster;
    as_node* node = as_node_get_random(cluster);

    if (! node) {
        return as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to find server node.");
    }

    int fd;
    as_status status = as_node_get_connection(err, node, deadline_ms, &fd);

    if (status) {
        as_node_release(node);
        return status;
    }

    status = as_admin_send(err, fd, buffer, end, deadline_ms);

    if (status) {
        as_node_close_connection(node, fd);
        as_node_release(node);
        return status;
    }

    status = as_socket_read_deadline(err, fd, buffer, HEADER_SIZE, deadline_ms);

    if (status) {
        as_node_close_connection(node, fd);
        as_node_release(node);
        return status;
    }

    as_node_put_connection(node, fd);
    as_node_release(node);

    status = buffer[RESULT_CODE];

    if (status) {
        return as_error_set_message(err, status, as_error_string(status));
    }
    return status;
}
static as_status
as_admin_read_list(aerospike* as, as_error* err, const as_policy_admin* policy, uint8_t* command, uint8_t* end, as_admin_parse_fn parse_fn, as_vector* list)
{
    int timeout_ms = (policy)? policy->timeout : as->config.policies.admin.timeout;
    if (timeout_ms <= 0) {
        timeout_ms = DEFAULT_TIMEOUT;
    }
    uint64_t deadline_ms = as_socket_deadline(timeout_ms);
    as_cluster* cluster = as->cluster;
    as_node* node = as_node_get_random(cluster);

    if (! node) {
        return as_error_set_message(err, AEROSPIKE_ERR_CLIENT, "Failed to find server node.");
    }

    int fd;
    as_status status = as_node_get_connection(err, node, deadline_ms, &fd);

    if (status) {
        as_node_release(node);
        return status;
    }

    status = as_admin_send(err, fd, command, end, deadline_ms);

    if (status) {
        as_node_close_connection(node, fd);
        as_node_release(node);
        return status;
    }

    status = as_admin_read_blocks(err, fd, deadline_ms, parse_fn, list);

    if (status) {
        as_node_close_connection(node, fd);
        as_node_release(node);
        return status;
    }

    as_node_put_connection(node, fd);
    as_node_release(node);
    return status;
}
static int
as_read_users(aerospike* as, const as_policy_admin* policy, uint8_t* buffer, uint8_t* end, as_vector* /*<as_user_roles*>*/ users)
{
	int timeout_ms = (policy)? policy->timeout : as->config.policies.admin.timeout;
	if (timeout_ms <= 0) {
		timeout_ms = DEFAULT_TIMEOUT;
	}
	uint64_t deadline_ms = cf_getms() + timeout_ms;
	as_node* node = as_node_get_random(as->cluster);
	
	if (! node) {
		return CITRUSLEAF_FAIL_CLIENT;
	}
	
	int fd;
	int status = as_node_get_connection(node, &fd);
	
	if (status) {
		as_node_release(node);
		return status;
	}
	
	if (as_send(fd, buffer, end, deadline_ms, timeout_ms)) {
		cf_close(fd);
		as_node_release(node);
		return CITRUSLEAF_FAIL_TIMEOUT;
	}
	
	status = as_read_user_blocks(fd, buffer, deadline_ms, timeout_ms, users);
	
	if (status >= 0) {
		as_node_put_connection(node, fd);
	}
	else {
		cf_close(fd);
	}
	as_node_release(node);
	return status;
}
static int
as_execute(aerospike* as, const as_policy_admin* policy, uint8_t* buffer, uint8_t* end)
{
	int timeout_ms = (policy)? policy->timeout : as->config.policies.admin.timeout;
	if (timeout_ms <= 0) {
		timeout_ms = DEFAULT_TIMEOUT;
	}
	uint64_t deadline_ms = cf_getms() + timeout_ms;
	as_node* node = as_node_get_random(as->cluster);
	
	if (! node) {
		return CITRUSLEAF_FAIL_CLIENT;
	}
	
	int fd;
	int status = as_node_get_connection(node, &fd);
	
	if (status) {
		as_node_release(node);
		return status;
	}

	if (as_send(fd, buffer, end, deadline_ms, timeout_ms)) {
		cf_close(fd);
		as_node_release(node);
		return CITRUSLEAF_FAIL_TIMEOUT;
	}
	
	if (cf_socket_read_timeout(fd, buffer, HEADER_SIZE, deadline_ms, timeout_ms)) {
		cf_close(fd);
		as_node_release(node);
		return CITRUSLEAF_FAIL_TIMEOUT;
	}
	
	as_node_put_connection(node, fd);
	as_node_release(node);
	return buffer[RESULT_CODE];
}
as_status
as_command_execute(as_cluster* cluster, as_error * err, as_command_node* cn, uint8_t* command, size_t command_len,
                   uint32_t timeout_ms, uint32_t retry,
                   as_parse_results_fn parse_results_fn, void* parse_results_data
                  )
{
    uint64_t deadline_ms = as_socket_deadline(timeout_ms);
    uint32_t sleep_between_retries_ms = 0;
    uint32_t failed_nodes = 0;
    uint32_t failed_conns = 0;
    uint32_t iterations = 0;
    bool release_node;

    // Execute command until successful, timed out or maximum iterations have been reached.
    while (true) {
        as_node* node;

        if (cn->node) {
            node = cn->node;
            release_node = false;
        }
        else {
            node = as_node_get(cluster, cn->ns, cn->digest, cn->write, cn->replica);
            release_node = true;
        }

        if (!node) {
            failed_nodes++;
            sleep_between_retries_ms = 10;
            goto Retry;
        }

        int fd;
        as_status status = as_node_get_connection(err, node, deadline_ms, &fd);

        if (status) {
            if (release_node) {
                as_node_release(node);
            }
            failed_conns++;
            sleep_between_retries_ms = 1;
            goto Retry;
        }

        // Send command.
        status = as_socket_write_deadline(err, fd, command, command_len, deadline_ms);

        if (status) {
            // Socket errors are considered temporary anomalies.  Retry.
            // Close socket to flush out possible garbage.  Do not put back in pool.
            as_close(fd);
            if (release_node) {
                as_node_release(node);
            }
            sleep_between_retries_ms = 0;
            goto Retry;
        }

        // Parse results returned by server.
        status = parse_results_fn(err, fd, deadline_ms, parse_results_data);

        if (status == AEROSPIKE_OK) {
            // Reset error code if retry had occurred.
            if (iterations > 0) {
                as_error_reset(err);
            }
        }
        else {
            switch (status) {
            // Retry on timeout.
            case AEROSPIKE_ERR_TIMEOUT:
                as_close(fd);
                if (release_node) {
                    as_node_release(node);
                }
                sleep_between_retries_ms = 0;
                goto Retry;

            // Close socket on errors that can leave unread data in socket.
            case AEROSPIKE_ERR_QUERY_ABORTED:
            case AEROSPIKE_ERR_SCAN_ABORTED:
            case AEROSPIKE_ERR_CLIENT_ABORT:
            case AEROSPIKE_ERR_CLIENT:
                as_close(fd);
                if (release_node) {
                    as_node_release(node);
                }
                err->code = status;
                return status;

            default:
                err->code = status;
                break;
            }
        }

        // Put connection back in pool.
        as_node_put_connection(node, fd, cluster->conn_queue_size);

        // Release resources.
        if (release_node) {
            as_node_release(node);
        }
        return status;

Retry:
        // Check if max retries reached.
        if (++iterations > retry) {
            break;
        }

        // Check for client timeout.
        if (deadline_ms > 0) {
            int remaining_ms = (int)(deadline_ms - cf_getms() - sleep_between_retries_ms);

            if (remaining_ms <= 0) {
                break;
            }

            // Reset timeout in send buffer (destined for server).
            *(uint32_t*)(command + 22) = cf_swap_to_be32(remaining_ms);
        }

        if (sleep_between_retries_ms > 0) {
            // Sleep before trying again.
            usleep(sleep_between_retries_ms * 1000);
        }
    }

    return as_error_update(err, AEROSPIKE_ERR_TIMEOUT,
                           "Client timeout: timeout=%d iterations=%u failedNodes=%u failedConns=%u",
                           timeout_ms, iterations, failed_nodes, failed_conns);
}