static void testWrongServer(const char *fname) { VBUCKET_CONFIG_HANDLE vb = vbucket_config_parse_file(configPath(fname)); if (vb == NULL) { fprintf(stderr, "vbucket_config_parse_file error: %s\n", vbucket_get_error()); abort(); } /* Starts at 0 */ assert(vbucket_get_master(vb, 0) == 0); /* Does not change when I told it I found the wrong thing */ assert(vbucket_found_incorrect_master(vb, 0, 1) == 0); assert(vbucket_get_master(vb, 0) == 0); /* Does change if I tell it I got the right thing and it was wrong. */ assert(vbucket_found_incorrect_master(vb, 0, 0) == 1); assert(vbucket_get_master(vb, 0) == 1); /* ...and again */ assert(vbucket_found_incorrect_master(vb, 0, 1) == 2); assert(vbucket_get_master(vb, 0) == 2); /* ...and then wraps */ assert(vbucket_found_incorrect_master(vb, 0, 2) == 0); assert(vbucket_get_master(vb, 0) == 0); vbucket_config_destroy(vb); }
VBUCKET_CONFIG_DIFF* vbucket_compare(VBUCKET_CONFIG_HANDLE from, VBUCKET_CONFIG_HANDLE to) { VBUCKET_CONFIG_DIFF *rv = calloc(1, sizeof(VBUCKET_CONFIG_DIFF)); int num_servers = (from->num_servers > to->num_servers ? from->num_servers : to->num_servers) + 1; assert(rv); rv->servers_added = calloc(num_servers, sizeof(char*)); rv->servers_removed = calloc(num_servers, sizeof(char*)); /* Compute the added and removed servers */ compute_vb_list_diff(from, to, rv->servers_added); compute_vb_list_diff(to, from, rv->servers_removed); /* Verify the servers are equal in their positions */ if (to->num_servers == from->num_servers) { int i; rv->sequence_changed = false; for (i = 0; i < from->num_servers; i++) { rv->sequence_changed |= (0 != strcmp(vbucket_config_get_server(from, i), vbucket_config_get_server(to, i))); } } else { /* Just say yes */ rv->sequence_changed = true; } /* Consider the sequence changed if the auth credentials changed */ if (from->user != NULL && to->user != NULL) { rv->sequence_changed |= (strcmp(from->user, to->user) != 0); } else { rv->sequence_changed |= ((from->user != NULL) ^ (to->user != NULL)); } if (from->password != NULL && to->password != NULL) { rv->sequence_changed |= (strcmp(from->password, to->password) != 0); } else { rv->sequence_changed |= ((from->password != NULL) ^ (to->password != NULL)); } /* Count the number of vbucket differences */ if (to->num_vbuckets == from->num_vbuckets) { int i; for (i = 0; i < to->num_vbuckets; i++) { rv->n_vb_changes += (vbucket_get_master(from, i) == vbucket_get_master(to, i)) ? 0 : 1; } } else { rv->n_vb_changes = -1; } return rv; }
int vbucket_map(VBUCKET_CONFIG_HANDLE vb, const void *key, size_t nkey, int *vbucket_id, int *server_idx) { uint32_t digest, mid, prev; struct continuum_item_st *beginp, *endp, *midp, *highp, *lowp; if (vb->distribution == VBUCKET_DISTRIBUTION_KETAMA) { assert(vb->continuum); if (vbucket_id) { *vbucket_id = 0; } digest = hash_ketama(key, nkey); beginp = lowp = vb->continuum; endp = highp = vb->continuum + vb->num_continuum; /* divide and conquer array search to find server with next biggest * point after what this key hashes to */ while (1) { /* pick the middle point */ midp = lowp + (highp - lowp) / 2; if (midp == endp) { /* if at the end, roll back to zeroth */ *server_idx = beginp->index; break; } mid = midp->point; prev = (midp == beginp) ? 0 : (midp-1)->point; if (digest <= mid && digest > prev) { /* we found nearest server */ *server_idx = midp->index; break; } /* adjust the limits */ if (mid < digest) { lowp = midp + 1; } else { highp = midp - 1; } if (lowp > highp) { *server_idx = beginp->index; break; } } } else { *vbucket_id = vbucket_get_vbucket_by_key(vb, key, nkey); *server_idx = vbucket_get_master(vb, *vbucket_id); } return 0; }
uint32_t lvb_key_hash(mcs_st *ptr, const char *key, size_t key_length, int *vbucket) { assert(ptr->kind == MCS_KIND_LIBVBUCKET); assert(ptr->data != NULL); VBUCKET_CONFIG_HANDLE vch = (VBUCKET_CONFIG_HANDLE) ptr->data; int v = vbucket_get_vbucket_by_key(vch, key, key_length); if (vbucket != NULL) { *vbucket = v; } return (uint32_t) vbucket_get_master(vch, v); }
/** * This callback is invoked for packet relocation twice. It tries to relocate * commands to their destination server. Some commands may not be relocated * either because they have no explicit "Relocation Information" (i.e. no * specific vbucket) or because the command is tied to a specific server (i.e. * CMD_STAT) */ static int iterwipe_cb(mc_CMDQUEUE *cq, mc_PIPELINE *oldpl, mc_PACKET *oldpkt, void *arg) { protocol_binary_request_header hdr; mc_SERVER *srv = (mc_SERVER *)oldpl; mc_PIPELINE *newpl; mc_PACKET *newpkt; int newix; uint8_t op; (void)arg; memcpy(&hdr, SPAN_BUFFER(&oldpkt->kh_span), sizeof(hdr.bytes)); op = hdr.request.opcode; if (op == PROTOCOL_BINARY_CMD_OBSERVE || op == PROTOCOL_BINARY_CMD_STAT || op == PROTOCOL_BINARY_CMD_GET_CLUSTER_CONFIG) { return MCREQ_KEEP_PACKET; } newix = vbucket_get_master(cq->config, ntohs(hdr.request.vbucket)); if (newix < 0 || newix > (int)cq->npipelines) { return MCREQ_KEEP_PACKET; } if (!lcb_should_retry(srv->settings, oldpkt, LCB_MAX_ERROR)) { return MCREQ_KEEP_PACKET; } newpl = cq->pipelines[newix]; if (newpl == oldpl || newpl == NULL) { return MCREQ_KEEP_PACKET; } lcb_log(LOGARGS(cq->instance, DEBUG), "Remapped packet %p (SEQ=%u) from "SERVER_FMT " to " SERVER_FMT, (void*)oldpkt, oldpkt->opaque, SERVER_ARGS((mc_SERVER*)oldpl), SERVER_ARGS((mc_SERVER*)newpl)); /** Otherwise, copy over the packet and find the new vBucket to map to */ newpkt = mcreq_dup_packet(oldpkt); newpkt->flags &= ~MCREQ_STATE_FLAGS; mcreq_reenqueue_packet(newpl, newpkt); mcreq_packet_handled(oldpl, oldpkt); return MCREQ_REMOVE_PACKET; }
static void testConfig(const char *fname) { int whoops = 0; const struct key_st *k; int i = 0; VBUCKET_CONFIG_HANDLE vb = vbucket_config_parse_file(configPath(fname)); if (vb == NULL) { fprintf(stderr, "vbucket_config_parse_file error: %s\n", vbucket_get_error()); abort(); } while ((k = &keys[i++])->key != NULL) { int id = vbucket_get_vbucket_by_key(vb, k->key, strlen(k->key)); if (id != k->vbucket) { fprintf(stderr, "Expected vbucket %d for key '%s' but got %d\n", k->vbucket, k->key, id); whoops = 1; } } if (whoops) { abort(); } assert(vbucket_config_get_num_servers(vb) == 3 || vbucket_config_get_num_servers(vb) == 4); assert(vbucket_config_get_num_replicas(vb) == 2); for (i = 0; i < 3; ++i) { assert(strcmp(vbucket_config_get_server(vb, i), servers[i]) == 0); } for (i = 0; i < 4; ++i) { assert(vbucket_get_master(vb, i) == vbuckets[i].master); assert(vbucket_get_replica(vb, i, 0) == vbuckets[i].replicas[0]); assert(vbucket_get_replica(vb, i, 1) == vbuckets[i].replicas[1]); } assert(vbucket_config_get_user(vb) == NULL); assert(vbucket_config_get_password(vb) == NULL); vbucket_config_destroy(vb); }
static void testWrongServerFFT(const char *fname) { VBUCKET_CONFIG_HANDLE vb = vbucket_config_parse_file(configPath(fname)); int rv = 0; int nvb = 0; int i = 0; if (vb == NULL) { fprintf(stderr, "vbucket_config_parse_file error: %s\n", vbucket_get_error()); abort(); } /* found incorrect master should not be the same as get master now */ nvb = vbucket_config_get_num_vbuckets(vb); for (i = 0; i < nvb; i++) { rv = vbucket_get_master(vb, i); assert(rv != vbucket_found_incorrect_master(vb, i, rv)); } /* the ideal test case should be that we check that the vbucket */ /* and the fvbucket map are identical at this point. TODO untill */ /* we have a vbucketlib function that diffs vbuckets and fvbuckets */ vbucket_config_destroy(vb); }
static void observe_response_handler(lcb_server_t *server, packet_info *info) { lcb_t root = server->instance; lcb_error_t rc = map_error(root, PACKET_STATUS(info)); lcb_uint32_t ttp; lcb_uint32_t ttr; lcb_size_t pos; VBUCKET_CONFIG_HANDLE config; const char *end, *ptr; /** * If we have an error we must decode the request instead */ if (rc != LCB_SUCCESS) { protocol_binary_request_header req; lcb_size_t nr; nr = ringbuffer_peek(&server->cmd_log, req.bytes, sizeof(req.bytes)); if (nr != sizeof(req.bytes)) { lcb_error_handler(server->instance, LCB_EINTERNAL, NULL); abort(); } if (req.request.bodylen) { lcb_size_t npacket = sizeof(req.bytes) + ntohl(req.request.bodylen); char *packet = server->cmd_log.read_head; int allocated = 0; if (!ringbuffer_is_continous(&server->cmd_log, RINGBUFFER_READ, npacket)) { packet = malloc(npacket); if (packet == NULL) { lcb_error_handler(root, LCB_CLIENT_ENOMEM, NULL); abort(); } nr = ringbuffer_peek(&server->cmd_log, packet, npacket); if (nr != npacket) { lcb_error_handler(root, LCB_EINTERNAL, NULL); free(packet); abort(); } allocated = 1; } lcb_failout_observe_request(server, &info->ct, packet, npacket, rc); if (allocated) { free(packet); } } return; } /** The CAS field is split into TTP/TTR values */ ptr = (char *)&info->res.response.cas; memcpy(&ttp, ptr, sizeof(ttp)); memcpy(&ttr, ptr + sizeof(ttp), sizeof(ttp)); ttp = ntohl(ttp); ttr = ntohl(ttr); /** Actual payload sequence of (vb, nkey, key). Repeats multiple times */ ptr = info->payload; end = (char *)ptr + PACKET_NBODY(info); config = root->vbucket_config; for (pos = 0; ptr < end; pos++) { lcb_cas_t cas; lcb_uint8_t obs; lcb_uint16_t nkey, vb; const char *key; lcb_observe_resp_t resp; memcpy(&vb, ptr, sizeof(vb)); vb = ntohs(vb); ptr += sizeof(vb); memcpy(&nkey, ptr, sizeof(nkey)); nkey = ntohs(nkey); ptr += sizeof(nkey); key = (const char *)ptr; ptr += nkey; obs = *((lcb_uint8_t *)ptr); ptr += sizeof(obs); memcpy(&cas, ptr, sizeof(cas)); ptr += sizeof(cas); setup_lcb_observe_resp_t(&resp, key, nkey, cas, obs, server->index == vbucket_get_master(config, vb), ttp, ttr); PACKET_TRACE(TRACE_OBSERVE_PROGRESS, info, rc, &resp); lcb_observe_invoke_callback(root, &info->ct, rc, &resp); } /* run callback with null-null-null to signal the end of transfer */ if ((info->ct.flags & LCB_CMD_F_OBS_BCAST) && lcb_lookup_server_with_command(root, CMD_OBSERVE, PACKET_OPAQUE(info), server) < 0) { lcb_observe_resp_t resp; memset(&resp, 0, sizeof(resp)); PACKET_TRACE_NORES(TRACE_OBSERVE_END, info, rc); lcb_observe_invoke_callback(root, &info->ct, LCB_SUCCESS, &resp); } }
/** * Send a preformatted packet to the cluster * This prototype copies the buffers into the internal structures * instead of just keeping a reference... */ LIBCOUCHBASE_API lcb_error_t lcb_forward_packet(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_packet_fwd_cmd_t *const *commands) { lcb_size_t ii; lcb_error_handler(instance, LCB_SUCCESS, NULL); /* we need a vbucket config before we can start getting data.. */ if (instance->vbucket_config == NULL) { switch (instance->type) { case LCB_TYPE_CLUSTER: return lcb_synchandler_return(instance, LCB_EBADHANDLE); case LCB_TYPE_BUCKET: default: return lcb_synchandler_return(instance, LCB_CLIENT_ETMPFAIL); } } /* Now handle all of the requests */ for (ii = 0; ii < num; ++ii) { lcb_server_t *server; protocol_binary_request_no_extras *req; int idx; int vb = 0; /* @todo ensure that we support the current command */ /* @todo currently the entire header needs to be in the first * chunk */ assert(commands[ii]->v.v0.buffer.iov[0].iov_len >= 24); /* I need to update the sequence number in the packet! */ req = (void *)commands[ii]->v.v0.buffer.iov[0].iov_base; vb = ntohs(req->message.header.request.vbucket); if (commands[ii]->v.v0.to_master) { idx = vbucket_get_master(instance->vbucket_config, vb); } else { idx = vbucket_get_replica(instance->vbucket_config, vb, commands[ii]->v.v0.replica_index); } if (idx < 0 || idx > (int)instance->nservers) { return lcb_synchandler_return(instance, LCB_NO_MATCHING_SERVER); } server = instance->servers + idx; ++instance->seqno; /* avoid alignment crash by using memcpy */ memcpy(&req->message.header.request.opaque, &instance->seqno, 4); lcb_server_start_packet(server, command_cookie, commands[ii]->v.v0.buffer.iov[0].iov_base, commands[ii]->v.v0.buffer.iov[0].iov_len); lcb_server_write_packet(server, commands[ii]->v.v0.buffer.iov[1].iov_base, commands[ii]->v.v0.buffer.iov[1].iov_len); lcb_server_end_packet(server); lcb_server_send_packets(server); } return lcb_synchandler_return(instance, LCB_SUCCESS); }
static void H_observe(mc_PIPELINE *pipeline, mc_PACKET *request, packet_info *response, lcb_error_t immerr) { lcb_t root = pipeline->parent->instance; lcb_error_t rc; uint32_t ttp; uint32_t ttr; lcb_size_t pos; VBUCKET_CONFIG_HANDLE config; const char *end, *ptr; mc_REQDATAEX *rd = request->u_rdata.exdata; MK_ERROR(root, rc, response, immerr); if (rc != LCB_SUCCESS) { if (! (request->flags & MCREQ_F_INVOKED)) { rd->callback(pipeline, request, rc, NULL); } return; } /** The CAS field is split into TTP/TTR values */ ptr = (char *)&response->res.response.cas; memcpy(&ttp, ptr, sizeof(ttp)); memcpy(&ttr, ptr + sizeof(ttp), sizeof(ttp)); ttp = ntohl(ttp); ttr = ntohl(ttr); /** Actual payload sequence of (vb, nkey, key). Repeats multiple times */ ptr = response->payload; end = (char *)ptr + PACKET_NBODY(response); config = pipeline->parent->config; for (pos = 0; ptr < end; pos++) { lcb_cas_t cas; lcb_uint8_t obs; lcb_uint16_t nkey, vb; const char *key; lcb_observe_resp_t resp; memset(&resp, 0, sizeof(resp)); memcpy(&vb, ptr, sizeof(vb)); vb = ntohs(vb); ptr += sizeof(vb); memcpy(&nkey, ptr, sizeof(nkey)); nkey = ntohs(nkey); ptr += sizeof(nkey); key = (const char *)ptr; ptr += nkey; obs = *((lcb_uint8_t *)ptr); ptr += sizeof(obs); memcpy(&cas, ptr, sizeof(cas)); ptr += sizeof(cas); resp.v.v0.key = key; resp.v.v0.nkey = nkey; resp.v.v0.cas = cas; resp.v.v0.status = obs; resp.v.v0.ttp = 0; resp.v.v0.ttr = 0; resp.v.v0.from_master = pipeline->index == vbucket_get_master(config, vb); if (! (request->flags & MCREQ_F_INVOKED)) { rd->callback(pipeline, request, rc, &resp); } } }
static void observe_response_handler(lcb_server_t *server, struct lcb_command_data_st *command_data, protocol_binary_response_header *res) { lcb_t root = server->instance; lcb_uint16_t status = ntohs(res->response.status); lcb_error_t rc = map_error(root, status); lcb_uint32_t ttp; lcb_uint32_t ttr; lcb_size_t pos; VBUCKET_CONFIG_HANDLE config; const char *end, *ptr = (const char *)&res->response.cas; /** * If we have an error we must decode the request instead */ if (rc != LCB_SUCCESS) { protocol_binary_request_header req; lcb_size_t nr; nr = ringbuffer_peek(&server->cmd_log, req.bytes, sizeof(req.bytes)); if (nr != sizeof(req.bytes)) { lcb_error_handler(server->instance, LCB_EINTERNAL, NULL); abort(); } if (req.request.bodylen) { lcb_size_t npacket = sizeof(req.bytes) + ntohl(req.request.bodylen); char *packet = server->cmd_log.read_head; int allocated = 0; if (!ringbuffer_is_continous(&server->cmd_log, RINGBUFFER_READ, npacket)) { packet = malloc(npacket); if (packet == NULL) { lcb_error_handler(root, LCB_CLIENT_ENOMEM, NULL); abort(); } nr = ringbuffer_peek(&server->cmd_log, packet, npacket); if (nr != npacket) { lcb_error_handler(root, LCB_EINTERNAL, NULL); free(packet); abort(); } allocated = 1; } lcb_failout_observe_request(server, command_data, packet, npacket, rc); if (allocated) { free(packet); } } return; } memcpy(&ttp, ptr, sizeof(ttp)); ttp = ntohl(ttp); memcpy(&ttr, ptr + sizeof(ttp), sizeof(ttr)); ttr = ntohl(ttr); ptr = (const char *)res + sizeof(res->bytes); end = ptr + ntohl(res->response.bodylen); config = root->vbucket_config; for (pos = 0; ptr < end; pos++) { lcb_cas_t cas; lcb_uint8_t obs; lcb_uint16_t nkey, vb; const char *key; lcb_observe_resp_t resp; memcpy(&vb, ptr, sizeof(vb)); vb = ntohs(vb); ptr += sizeof(vb); memcpy(&nkey, ptr, sizeof(nkey)); nkey = ntohs(nkey); ptr += sizeof(nkey); key = (const char *)ptr; ptr += nkey; obs = *((lcb_uint8_t *)ptr); ptr += sizeof(obs); memcpy(&cas, ptr, sizeof(cas)); ptr += sizeof(cas); setup_lcb_observe_resp_t(&resp, key, nkey, cas, obs, server->index == vbucket_get_master(config, vb), ttp, ttr); TRACE_OBSERVE_PROGRESS(res->response.opaque, command_data->vbucket, res->response.opcode, rc, &resp); lcb_observe_invoke_callback(root, command_data, rc, &resp); } /* run callback with null-null-null to signal the end of transfer */ if ((command_data->flags & LCB_CMD_F_OBS_BCAST) && lcb_lookup_server_with_command(root, CMD_OBSERVE, res->response.opaque, server) < 0) { lcb_observe_resp_t resp; memset(&resp, 0, sizeof(resp)); TRACE_OBSERVE_END(res->response.opaque, command_data->vbucket, res->response.opcode, rc); lcb_observe_invoke_callback(root, command_data, LCB_SUCCESS, &resp); } }