static void get_replica_response_handler(lcb_server_t *server, packet_info *info) { lcb_t root = server->instance; lcb_uint16_t nkey; const char *key; char *packet; lcb_uint16_t status = PACKET_STATUS(info); lcb_error_t rc = map_error(root, status); /** * Success? always perform the callback */ if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) { const protocol_binary_response_get *get = PACKET_EPHEMERAL_START(info); lcb_get_resp_t resp; setup_lcb_get_resp_t(&resp, PACKET_KEY(info), PACKET_NKEY(info), PACKET_VALUE(info), PACKET_NVALUE(info), ntohl(get->message.body.flags), PACKET_CAS(info), PACKET_DATATYPE(info)); root->callbacks.get(root, info->ct.cookie, rc, &resp); return; } key = get_key(server, &nkey, &packet); /** * Following code handles errors. */ if (info->ct.replica == -1) { /* Perform the callback. Either SELECT or ALL */ lcb_get_resp_t resp; setup_lcb_get_resp_t(&resp, key, nkey, NULL, 0, 0, 0, 0); PACKET_TRACE(TRACE_GET_END, info, rc, &resp); root->callbacks.get(root, info->ct.cookie, rc, &resp); release_key(server, packet); return; } /** LCB_REPLICA_FIRST */ if (status == PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET) { /** * the config was updated, start from first replica. * Reset the iteration count */ info->ct.replica = 0; } else { info->ct.replica++; } if (info->ct.replica < root->nreplicas) { /* try next replica */ protocol_binary_request_get req; lcb_server_t *new_server; int idx = vbucket_get_replica(root->vbucket_config, info->ct.vbucket, info->ct.replica); if (idx < 0 || idx > (int)root->nservers) { lcb_error_handler(root, LCB_NETWORK_ERROR, "GET_REPLICA: missing server"); release_key(server, packet); return; } new_server = root->servers + idx; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.opcode = CMD_GET_REPLICA; req.message.header.request.keylen = ntohs((lcb_uint16_t)nkey); req.message.header.request.vbucket = ntohs(info->ct.vbucket); req.message.header.request.bodylen = ntohl((lcb_uint32_t)nkey); req.message.header.request.opaque = ++root->seqno; TRACE_GET_BEGIN(&req, key, nkey, 0); lcb_server_retry_packet(new_server, &info->ct, req.bytes, sizeof(req.bytes)); lcb_server_write_packet(new_server, key, nkey); lcb_server_end_packet(new_server); lcb_server_send_packets(new_server); } else { /* give up and report the error */ lcb_get_resp_t resp; setup_lcb_get_resp_t(&resp, key, nkey, NULL, 0, 0, 0, 0); PACKET_TRACE(TRACE_GET_END, info, rc, &resp) root->callbacks.get(root, info->ct.cookie, rc, &resp); } release_key(server, packet); }
/** * Returns 1 if retried, 0 if the command should fail, -1 for an internal * error */ static int handle_not_my_vbucket(lcb_server_t *c, packet_info *resinfo, protocol_binary_request_header *oldreq, struct lcb_command_data_st *oldct) { int idx; char *body; lcb_size_t nbody, nr; lcb_server_t *new_srv; struct lcb_command_data_st ct; protocol_binary_request_header req; hrtime_t now; lcb_string config_string; lcb_error_t err = LCB_ERROR; lcb_log(LOGARGS(c, WARN), "NOT_MY_VBUCKET; Server=%p,ix=%d,real_start=%lu,vb=%d", (void *)c, c->index, (unsigned long)oldct->real_start, (int)ntohs(oldreq->request.vbucket)); lcb_string_init(&config_string); if (PACKET_NBODY(resinfo)) { lcb_string_append(&config_string, PACKET_VALUE(resinfo), PACKET_NVALUE(resinfo)); err = lcb_cccp_update(lcb_confmon_get_provider(c->instance->confmon, LCB_CLCONFIG_CCCP), c->curhost.host, &config_string); } lcb_string_release(&config_string); if (err != LCB_SUCCESS) { lcb_bootstrap_refresh(c->instance); } /* re-schedule command to new server */ if (!c->instance->settings.vb_noguess) { idx = vbucket_found_incorrect_master(c->instance->vbucket_config, ntohs(oldreq->request.vbucket), (int)c->index); } else { idx = c->index; } if (idx == -1) { lcb_log(LOGARGS(c, ERR), "no alternate server"); return 0; } lcb_log(LOGARGS(c, INFO), "Mapped key to new server %d -> %d", c->index, idx); now = gethrtime(); if (oldct->real_start) { hrtime_t min_ok = now - MCSERVER_TIMEOUT(c) * 1000; if (oldct->real_start < min_ok) { /** Timed out in a 'natural' manner */ return 0; } } req = *oldreq; lcb_assert((lcb_size_t)idx < c->instance->nservers); new_srv = c->instance->servers + idx; nr = ringbuffer_read(&c->cmd_log, req.bytes, sizeof(req)); lcb_assert(nr == sizeof(req)); req.request.opaque = ++c->instance->seqno; nbody = ntohl(req.request.bodylen); body = malloc(nbody); if (body == NULL) { lcb_error_handler(c->instance, LCB_CLIENT_ENOMEM, NULL); return -1; } nr = ringbuffer_read(&c->cmd_log, body, nbody); lcb_assert(nr == nbody); nr = ringbuffer_read(&c->output_cookies, &ct, sizeof(ct)); lcb_assert(nr == sizeof(ct)); /* Preserve the cookie and reset timestamp for the command. This * means that the library will retry the command until it will * get code different from LCB_NOT_MY_VBUCKET */ if (!ct.real_start) { ct.real_start = ct.start; } ct.start = now; lcb_server_retry_packet(new_srv, &ct, &req, sizeof(req)); /* FIXME dtrace instrumentation */ lcb_server_write_packet(new_srv, body, nbody); lcb_server_end_packet(new_srv); lcb_server_send_packets(new_srv); free(body); return 1; }
static void get_replica_response_handler(lcb_server_t *server, struct lcb_command_data_st *command_data, protocol_binary_response_header *res) { lcb_t root = server->instance; protocol_binary_response_get *get = (void *)res; lcb_uint16_t status = ntohs(res->response.status); lcb_uint16_t nkey = ntohs(res->response.keylen); const char *key = (const char *)res; char *packet; lcb_error_t rc = map_error(root, status); key += sizeof(get->bytes); if (key == NULL) { lcb_error_handler(server->instance, LCB_EINTERNAL, NULL); return; } /** * Success? always perform the callback */ if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) { const char *bytes = key + nkey; lcb_size_t nbytes = ntohl(res->response.bodylen) - nkey - res->response.extlen; lcb_get_resp_t resp; setup_lcb_get_resp_t(&resp, key, nkey, bytes, nbytes, ntohl(get->message.body.flags), res->response.cas, res->response.datatype); TRACE_GET_END(res->response.opaque, command_data->vbucket, res->response.opcode, rc, &resp); root->callbacks.get(root, command_data->cookie, rc, &resp); return; } key = get_key(server, &nkey, &packet); /** * Following code handles errors. */ if (command_data->replica == -1) { /* Perform the callback. Either SELECT or ALL */ lcb_get_resp_t resp; setup_lcb_get_resp_t(&resp, key, nkey, NULL, 0, 0, 0, 0); TRACE_GET_END(res->response.opaque, command_data->vbucket, res->response.opcode, rc, &resp); root->callbacks.get(root, command_data->cookie, rc, &resp); release_key(server, packet); return; } /** LCB_REPLICA_FIRST */ if (status == PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET) { /** * the config was updated, start from first replica. * Reset the iteration count */ command_data->replica = 0; } else { command_data->replica++; } if (command_data->replica < root->nreplicas) { /* try next replica */ protocol_binary_request_get req; lcb_server_t *new_server; int idx = vbucket_get_replica(root->vbucket_config, command_data->vbucket, command_data->replica); if (idx < 0 || idx > (int)root->nservers) { lcb_error_handler(root, LCB_NETWORK_ERROR, "GET_REPLICA: missing server"); release_key(server, packet); return; } new_server = root->servers + idx; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.opcode = CMD_GET_REPLICA; req.message.header.request.keylen = ntohs((lcb_uint16_t)nkey); req.message.header.request.vbucket = ntohs(command_data->vbucket); req.message.header.request.bodylen = ntohl((lcb_uint32_t)nkey); req.message.header.request.opaque = ++root->seqno; TRACE_GET_BEGIN(&req, key, nkey, 0); lcb_server_retry_packet(new_server, command_data, req.bytes, sizeof(req.bytes)); lcb_server_write_packet(new_server, key, nkey); lcb_server_end_packet(new_server); lcb_server_send_packets(new_server); } else { /* give up and report the error */ lcb_get_resp_t resp; setup_lcb_get_resp_t(&resp, key, nkey, NULL, 0, 0, 0, res->response.datatype); TRACE_GET_END(res->response.opaque, command_data->vbucket, res->response.opcode, rc, &resp); root->callbacks.get(root, command_data->cookie, rc, &resp); } release_key(server, packet); }
/** * Returns 1 if retried, 0 if the command should fail, -1 for an internal * error */ static int handle_not_my_vbucket(lcb_server_t *c, protocol_binary_request_header *oldreq, struct lcb_command_data_st *oldct) { int idx; char *body; lcb_size_t nbody, nr; lcb_server_t *new_srv; struct lcb_command_data_st ct; protocol_binary_request_header req; hrtime_t now; if (c->instance->compat.type == LCB_CACHED_CONFIG) { lcb_schedule_config_cache_refresh(c->instance); } /* re-schedule command to new server */ idx = vbucket_found_incorrect_master(c->instance->vbucket_config, ntohs(oldreq->request.vbucket), (int)c->index); if (idx == -1) { return 0; } now = gethrtime(); if (oldct->real_start) { hrtime_t min_ok = now - (c->connection.timeout.usec * 1000); if (oldct->real_start < min_ok) { /** Timed out in a 'natural' manner */ return 0; } } req = *oldreq; lcb_assert((lcb_size_t)idx < c->instance->nservers); new_srv = c->instance->servers + idx; nr = lcb_ringbuffer_read(&c->cmd_log, req.bytes, sizeof(req)); lcb_assert(nr == sizeof(req)); req.request.opaque = ++c->instance->seqno; nbody = ntohl(req.request.bodylen); body = malloc(nbody); if (body == NULL) { lcb_error_handler(c->instance, LCB_CLIENT_ENOMEM, NULL); return -1; } nr = lcb_ringbuffer_read(&c->cmd_log, body, nbody); lcb_assert(nr == nbody); nr = lcb_ringbuffer_read(&c->output_cookies, &ct, sizeof(ct)); lcb_assert(nr == sizeof(ct)); /* Preserve the cookie and reset timestamp for the command. This * means that the library will retry the command until it will * get code different from LCB_NOT_MY_VBUCKET */ if (!ct.real_start) { ct.real_start = ct.start; } ct.start = now; lcb_server_retry_packet(new_srv, &ct, &req, sizeof(req)); /* FIXME dtrace instrumentation */ lcb_server_write_packet(new_srv, body, nbody); lcb_server_end_packet(new_srv); lcb_server_send_packets(new_srv); free(body); return 1; }