LIBCOUCHBASE_API lcb_error_t lcb_compact(lcb_t instance, const void *command_cookie, uint16_t vb, uint64_t purge_before_ts, uint64_t purge_before_seq, uint8_t drop_deletes) { fprintf(stderr,"inside compact method..\n"); lcb_server_t *server; protocol_binary_request_hello req; lcb_size_t headersize; server = instance->servers; uint16_t keylen = 0; uint32_t bodylen = 0; //const void *key = useragent; fprintf(stderr,"setting the useragent..\n"); memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_COMPACTDB; req.message.header.request.keylen = ntohs(keylen); req.message.header.request.extlen = 24; req.message.header.request.vbucket = ntohs((lcb_uint16_t)vb); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.opaque = ++instance->seqno; struct compact_st { uint64_t pbs; uint64_t pbseq; uint8_t dds; uint8_t bytes[7]; }; struct compact_st* compact_t; compact_t = malloc(sizeof(struct compact_st)); compact_t->pbs = htonll(purge_before_ts); compact_t->pbseq = htonll(purge_before_seq); compact_t->dds = drop_deletes; //uint8_t bytes[24]; // Make it known that this was a success. lcb_error_handler(instance, LCB_SUCCESS, NULL); req.message.header.request.bodylen = htonl(bodylen); //fprintf(stderr,"after setting bodylen.."); lcb_server_start_packet(server, NULL, &req, sizeof(req.message.header)); //fprintf(stderr,"after starting packet.."); lcb_server_write_packet(server, compact_t, (lcb_size_t)sizeof(struct compact_st)); //fprintf(stderr,"after writing key.."); // lcb_server_write_packet(server, compact_t, (lcb_size_t)sizeof(bytes)); //fprintf(stderr,"after writing feature.."); lcb_server_end_packet(server); //fprintf(stderr,"after end packet.."); lcb_server_send_packets(server); //fprintf(stderr,"after sending packet.."); return lcb_synchandler_return(instance, LCB_SUCCESS); }
LIBCOUCHBASE_API lcb_error_t lcb_unlock(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_unlock_cmd_t *const *items) { lcb_size_t ii; /* 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); } } for (ii = 0; ii < num; ++ii) { lcb_server_t *server; protocol_binary_request_no_extras req; int vb, idx; const void *hashkey = items[ii]->v.v0.hashkey; lcb_size_t nhashkey = items[ii]->v.v0.nhashkey; const void *key = items[ii]->v.v0.key; lcb_size_t nkey = items[ii]->v.v0.nkey; lcb_cas_t cas = items[ii]->v.v0.cas; if (nhashkey == 0) { hashkey = key; nhashkey = nkey; } (void)vbucket_map(instance->vbucket_config, hashkey, nhashkey, &vb, &idx); if (idx < 0 || idx > (int)instance->nservers) { return lcb_synchandler_return(instance, LCB_NO_MATCHING_SERVER); } server = instance->servers + idx; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.keylen = ntohs((lcb_uint16_t)nkey); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs((lcb_uint16_t)vb); req.message.header.request.bodylen = ntohl((lcb_uint32_t)(nkey)); req.message.header.request.cas = cas; req.message.header.request.opaque = ++instance->seqno; req.message.header.request.opcode = CMD_UNLOCK_KEY; lcb_server_start_packet(server, command_cookie, req.bytes, sizeof(req.bytes)); lcb_server_write_packet(server, key, nkey); lcb_server_end_packet(server); lcb_server_send_packets(server); } return lcb_synchandler_return(instance, LCB_SUCCESS); }
LIBCOUCHBASE_API lcb_error_t lcb_hello(lcb_t instance, const void *command_cookie) { fprintf(stderr,"inside hello method..\n"); lcb_server_t *server; protocol_binary_request_hello req; lcb_size_t headersize; lcb_size_t bodylen; server = instance->servers; const char *useragent = "my prototype"; uint16_t feature = htons(PROTOCOL_BINARY_FEATURE_DATATYPE); /*const void *key = useragent;*/ fprintf(stderr,"setting the useragent..\n"); memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_HELLO; req.message.header.request.keylen = ntohs(strlen(useragent)); req.message.header.request.extlen = 0; req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.opaque = ++instance->seqno; /* Make it known that this was a success. */ lcb_error_handler(instance, LCB_SUCCESS, NULL); req.message.header.request.bodylen = htonl((uint32_t)(strlen(useragent) + sizeof(feature))); /*fprintf(stderr,"after setting bodylen..");*/ lcb_server_start_packet(server, NULL, &req, sizeof(req.message.header)); /*fprintf(stderr,"after starting packet..");*/ lcb_server_write_packet(server, useragent, (lcb_size_t)strlen(useragent)); /*fprintf(stderr,"after writing key.."); */ lcb_server_write_packet(server, &feature, (lcb_size_t)sizeof(feature)); /*fprintf(stderr,"after writing feature.."); */ lcb_server_end_packet(server); /*fprintf(stderr,"after end packet.."); */ lcb_server_send_packets(server); /*fprintf(stderr,"after sending packet.."); */ return lcb_synchandler_return(instance, LCB_SUCCESS); }
/** * Extended version of observe command. This allows us to service * various forms of higher level operations which use observe in one way * or another */ lcb_error_t lcb_observe_ex(lcb_t instance, const void *command_cookie, lcb_size_t num, const void *const *items, lcb_observe_type_t type) { lcb_size_t ii; lcb_size_t maxix; lcb_uint32_t opaque; struct lcb_command_data_st ct; struct observe_requests_st reqs; memset(&reqs, 0, sizeof(reqs)); if (instance->type != LCB_TYPE_BUCKET) { return lcb_synchandler_return(instance, LCB_EBADHANDLE); } if (instance->config.handle == NULL) { return lcb_synchandler_return(instance, LCB_CLIENT_ETMPFAIL); } if (instance->config.dist_type != VBUCKET_DISTRIBUTION_VBUCKET) { return lcb_synchandler_return(instance, LCB_NOT_SUPPORTED); } opaque = ++instance->seqno; ct.cookie = command_cookie; maxix = instance->config.nreplicas; if (type == LCB_OBSERVE_TYPE_CHECK) { maxix = 0; } else { if (type == LCB_OBSERVE_TYPE_DURABILITY) { ct.flags = LCB_CMD_F_OBS_DURABILITY | LCB_CMD_F_OBS_BCAST; } else { ct.flags = LCB_CMD_F_OBS_BCAST; } } reqs.nrequests = instance->nservers; reqs.requests = calloc(reqs.nrequests, sizeof(*reqs.requests)); for (ii = 0; ii < num; ii++) { const void *key, *hashkey; lcb_size_t nkey, nhashkey; int vbid, jj; if (type == LCB_OBSERVE_TYPE_DURABILITY) { const lcb_durability_entry_t *ent = items[ii]; key = ent->request.v.v0.key; nkey = ent->request.v.v0.nkey; hashkey = ent->request.v.v0.hashkey; nhashkey = ent->request.v.v0.nhashkey; } else { const lcb_observe_cmd_t *ocmd = items[ii]; key = ocmd->v.v0.key; nkey = ocmd->v.v0.nkey; hashkey = ocmd->v.v0.hashkey; nhashkey = ocmd->v.v0.nhashkey; } if (!nhashkey) { hashkey = key; nhashkey = nkey; } vbid = vbucket_get_vbucket_by_key(instance->config.handle, hashkey, nhashkey); for (jj = -1; jj < (int)maxix; jj++) { struct observe_st *rr; int idx = vbucket_get_replica(instance->config.handle, vbid, jj); if (idx < 0 || idx > (int)instance->nservers) { if (jj == -1) { destroy_requests(&reqs); return lcb_synchandler_return(instance, LCB_NO_MATCHING_SERVER); } continue; } lcb_assert(idx < (int)reqs.nrequests); rr = reqs.requests + idx; if (!rr->allocated) { if (!init_request(rr)) { destroy_requests(&reqs); return lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM); } } { lcb_uint16_t vb = htons((lcb_uint16_t)vbid); lcb_uint16_t len = htons((lcb_uint16_t)nkey); rr->packet.message.header.request.magic = PROTOCOL_BINARY_REQ; rr->packet.message.header.request.opcode = CMD_OBSERVE; rr->packet.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; rr->packet.message.header.request.opaque = opaque; ringbuffer_ensure_capacity(&rr->body, sizeof(vb) + sizeof(len) + nkey); rr->nbody += ringbuffer_write(&rr->body, &vb, sizeof(vb)); rr->nbody += ringbuffer_write(&rr->body, &len, sizeof(len)); rr->nbody += ringbuffer_write(&rr->body, key, nkey); } } } for (ii = 0; ii < reqs.nrequests; ii++) { struct observe_st *rr = reqs.requests + ii; struct lcb_server_st *server = instance->servers + ii; char *tmp; if (!rr->allocated) { continue; } rr->packet.message.header.request.bodylen = ntohl((lcb_uint32_t)rr->nbody); ct.start = gethrtime(); lcb_server_start_packet_ct(server, &ct, rr->packet.bytes, sizeof(rr->packet.bytes)); if (ringbuffer_is_continous(&rr->body, RINGBUFFER_READ, rr->nbody)) { tmp = ringbuffer_get_read_head(&rr->body); TRACE_OBSERVE_BEGIN(&rr->packet, server->authority, tmp, rr->nbody); lcb_server_write_packet(server, tmp, rr->nbody); } else { tmp = malloc(ringbuffer_get_nbytes(&rr->body)); if (!tmp) { /* FIXME by this time some of requests might be scheduled */ destroy_requests(&reqs); return lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM); } else { ringbuffer_read(&rr->body, tmp, rr->nbody); TRACE_OBSERVE_BEGIN(&rr->packet, server->authority, tmp, rr->nbody); lcb_server_write_packet(server, tmp, rr->nbody); } } lcb_server_end_packet(server); lcb_server_send_packets(server); } destroy_requests(&reqs); return lcb_synchandler_return(instance, LCB_SUCCESS); }
/** * Spool a store request * * @author Trond Norbye * @todo add documentation * @todo fix the expiration so that it works relative/absolute etc.. * @todo we might want to wait to write the data to the sockets if the * user want to run a batch of store requests? */ LIBCOUCHBASE_API lcb_error_t lcb_store(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_store_cmd_t *const *items) { lcb_size_t ii; lcb_error_t err; vbcheck_ctx vbc; VBC_SANITY(instance); err = vbcheck_ctx_init(&vbc, instance, num); if (err != LCB_SUCCESS) { return lcb_synchandler_return(instance, err); } for (ii = 0; ii < num; ii++) { const void *k; lcb_size_t n; VBC_GETK0(items[ii], k, n); err = vbcheck_populate(&vbc, instance, ii, k, n); if (err != LCB_SUCCESS) { vbcheck_ctx_clean(&vbc); return lcb_synchandler_return(instance, err); } } for (ii = 0; ii < num; ++ii) { lcb_server_t *server; protocol_binary_request_set req; lcb_size_t headersize; lcb_size_t bodylen; lcb_storage_t operation = items[ii]->v.v0.operation; const void *key = items[ii]->v.v0.key; lcb_size_t nkey = items[ii]->v.v0.nkey; lcb_cas_t cas = items[ii]->v.v0.cas; lcb_uint32_t flags = items[ii]->v.v0.flags; lcb_time_t exp = items[ii]->v.v0.exptime; const void *bytes = items[ii]->v.v0.bytes; lcb_size_t nbytes = items[ii]->v.v0.nbytes; vbcheck_keyinfo *ki = vbc.ptr_ki + ii; server = instance->servers + ki->ix; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.keylen = ntohs((lcb_uint16_t)nkey); req.message.header.request.extlen = 8; req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs(ki->vb); req.message.header.request.opaque = ++instance->seqno; req.message.header.request.cas = cas; req.message.body.flags = htonl(flags); req.message.body.expiration = htonl((lcb_uint32_t)exp); headersize = sizeof(req.bytes); switch (operation) { case LCB_ADD: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_ADD; break; case LCB_REPLACE: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_REPLACE; break; case LCB_SET: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SET; break; case LCB_APPEND: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_APPEND; req.message.header.request.extlen = 0; headersize -= 8; break; case LCB_PREPEND: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_PREPEND; req.message.header.request.extlen = 0; headersize -= 8; break; default: /* We were given an unknown storage operation. */ return lcb_synchandler_return(instance, lcb_error_handler(instance, LCB_EINVAL, "Invalid value passed as storage operation")); } /* Make it known that this was a success. */ lcb_error_handler(instance, LCB_SUCCESS, NULL); bodylen = nkey + nbytes + req.message.header.request.extlen; req.message.header.request.bodylen = htonl((lcb_uint32_t)bodylen); lcb_server_start_packet(server, command_cookie, &req, headersize); lcb_server_write_packet(server, key, nkey); lcb_server_write_packet(server, bytes, nbytes); lcb_server_end_packet(server); } for (ii = 0; ii < instance->nservers; ii++) { if (vbc.ptr_srv[ii]) { lcb_server_send_packets(instance->servers + ii); } } vbcheck_ctx_clean(&vbc); return lcb_synchandler_return(instance, LCB_SUCCESS); }
/** * Spool a store request * * @author Trond Norbye * @todo add documentation * @todo fix the expiration so that it works relative/absolute etc.. * @todo we might want to wait to write the data to the sockets if the * user want to run a batch of store requests? */ LIBCOUCHBASE_API lcb_error_t lcb_store(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_store_cmd_t *const *items) { lcb_size_t ii; /* 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); } } for (ii = 0; ii < num; ++ii) { lcb_server_t *server; protocol_binary_request_set req; lcb_size_t headersize; lcb_size_t bodylen; int vb, idx; lcb_storage_t operation = items[ii]->v.v0.operation; const void *key = items[ii]->v.v0.key; lcb_size_t nkey = items[ii]->v.v0.nkey; lcb_cas_t cas = items[ii]->v.v0.cas; lcb_uint32_t flags = items[ii]->v.v0.flags; lcb_time_t exp = items[ii]->v.v0.exptime; const void *bytes = items[ii]->v.v0.bytes; lcb_size_t nbytes = items[ii]->v.v0.nbytes; const void *hashkey = items[ii]->v.v0.hashkey; lcb_size_t nhashkey = items[ii]->v.v0.nhashkey; if (nhashkey == 0) { hashkey = key; nhashkey = nkey; } (void)vbucket_map(instance->vbucket_config, hashkey, nhashkey, &vb, &idx); if (idx < 0 || idx > (int)instance->nservers) { return lcb_synchandler_return(instance, LCB_NO_MATCHING_SERVER); } server = instance->servers + idx; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.keylen = ntohs((lcb_uint16_t)nkey); req.message.header.request.extlen = 8; req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs((lcb_uint16_t)vb); req.message.header.request.opaque = ++instance->seqno; req.message.header.request.cas = cas; req.message.body.flags = htonl(flags); req.message.body.expiration = htonl((lcb_uint32_t)exp); headersize = sizeof(req.bytes); switch (operation) { case LCB_ADD: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_ADD; break; case LCB_REPLACE: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_REPLACE; break; case LCB_SET: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SET; break; case LCB_APPEND: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_APPEND; req.message.header.request.extlen = 0; headersize -= 8; break; case LCB_PREPEND: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_PREPEND; req.message.header.request.extlen = 0; headersize -= 8; break; default: /* We were given an unknown storage operation. */ return lcb_synchandler_return(instance, lcb_error_handler(instance, LCB_EINVAL, "Invalid value passed as storage operation")); } /* Make it known that this was a success. */ lcb_error_handler(instance, LCB_SUCCESS, NULL); bodylen = nkey + nbytes + req.message.header.request.extlen; req.message.header.request.bodylen = htonl((lcb_uint32_t)bodylen); TRACE_STORE_BEGIN(&req, key, nkey, bytes, nbytes, flags, exp); lcb_server_start_packet(server, command_cookie, &req, headersize); lcb_server_write_packet(server, key, nkey); lcb_server_write_packet(server, bytes, nbytes); lcb_server_end_packet(server); lcb_server_send_packets(server); } return lcb_synchandler_return(instance, LCB_SUCCESS); }
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); }
/** * 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 lcb_error_t single_get(lcb_t instance, const void *command_cookie, const lcb_get_cmd_t *item) { lcb_server_t *server; protocol_binary_request_gat req; int vb, idx; lcb_size_t nbytes; const void *hashkey = item->v.v0.hashkey; lcb_size_t nhashkey = item->v.v0.nhashkey; const void *key = item->v.v0.key; lcb_size_t nkey = item->v.v0.nkey; lcb_time_t exp = item->v.v0.exptime; /* 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); } } if (nhashkey == 0) { hashkey = key; nhashkey = nkey; } (void)vbucket_map(instance->vbucket_config, hashkey, nhashkey, &vb, &idx); if (idx < 0 || idx > (int)instance->nservers) { return lcb_synchandler_return(instance, LCB_NO_MATCHING_SERVER); } server = instance->servers + idx; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.keylen = ntohs((lcb_uint16_t)nkey); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs((lcb_uint16_t)vb); req.message.header.request.bodylen = ntohl((lcb_uint32_t)(nkey)); req.message.header.request.opaque = ++instance->seqno; if (!exp) { req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GET; nbytes = sizeof(req.bytes) - 4; } else { req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GAT; req.message.header.request.extlen = 4; req.message.body.expiration = ntohl((lcb_uint32_t)exp); req.message.header.request.bodylen = ntohl((lcb_uint32_t)(nkey) + 4); nbytes = sizeof(req.bytes); } if (item->v.v0.lock) { /* the expiration is optional for GETL command */ req.message.header.request.opcode = CMD_GET_LOCKED; } lcb_server_start_packet(server, command_cookie, req.bytes, nbytes); lcb_server_write_packet(server, key, nkey); lcb_server_end_packet(server); lcb_server_send_packets(server); return lcb_synchandler_return(instance, LCB_SUCCESS); }
static lcb_error_t multi_get(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_get_cmd_t *const *items) { lcb_server_t *server = NULL; protocol_binary_request_noop noop; lcb_size_t ii, *affected_servers = NULL; struct server_info_st *servers = 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); } } affected_servers = calloc(instance->nservers, sizeof(lcb_size_t)); if (affected_servers == NULL) { return lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM); } servers = malloc(num * sizeof(struct server_info_st)); if (servers == NULL) { free(affected_servers); return lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM); } for (ii = 0; ii < num; ++ii) { const void *key = items[ii]->v.v0.key; lcb_size_t nkey = items[ii]->v.v0.nkey; const void *hashkey = items[ii]->v.v0.hashkey; lcb_size_t nhashkey = items[ii]->v.v0.nhashkey; if (nhashkey == 0) { hashkey = key; nhashkey = nkey; } (void)vbucket_map(instance->vbucket_config, hashkey, nhashkey, &servers[ii].vb, &servers[ii].idx); if (servers[ii].idx < 0 || servers[ii].idx > (int)instance->nservers) { free(servers); free(affected_servers); return lcb_synchandler_return(instance, LCB_NO_MATCHING_SERVER); } affected_servers[servers[ii].idx]++; } for (ii = 0; ii < num; ++ii) { protocol_binary_request_gat req; const void *key = items[ii]->v.v0.key; lcb_size_t nkey = items[ii]->v.v0.nkey; lcb_time_t exp = items[ii]->v.v0.exptime; lcb_size_t nreq = sizeof(req.bytes); int vb; server = instance->servers + servers[ii].idx; vb = servers[ii].vb; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.keylen = ntohs((lcb_uint16_t)nkey); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs((lcb_uint16_t)vb); req.message.header.request.bodylen = ntohl((lcb_uint32_t)(nkey)); req.message.header.request.opaque = ++instance->seqno; if (!exp) { req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GETQ; nreq -= 4; } else { req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GATQ; req.message.header.request.extlen = 4; req.message.body.expiration = ntohl((lcb_uint32_t)exp); req.message.header.request.bodylen = ntohl((lcb_uint32_t)(nkey) + 4); } if (items[ii]->v.v0.lock) { /* the expiration is optional for GETL command */ req.message.header.request.opcode = CMD_GET_LOCKED; } lcb_server_start_packet(server, command_cookie, req.bytes, nreq); lcb_server_write_packet(server, key, nkey); lcb_server_end_packet(server); } free(servers); memset(&noop, 0, sizeof(noop)); noop.message.header.request.magic = PROTOCOL_BINARY_REQ; noop.message.header.request.opcode = PROTOCOL_BINARY_CMD_NOOP; noop.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; /* ** We don't know which server we sent the data to, so examine ** where to send the noop */ for (ii = 0; ii < instance->nservers; ++ii) { if (affected_servers[ii]) { server = instance->servers + ii; noop.message.header.request.opaque = ++instance->seqno; lcb_server_complete_packet(server, command_cookie, noop.bytes, sizeof(noop.bytes)); lcb_server_send_packets(server); } } free(affected_servers); return lcb_synchandler_return(instance, LCB_SUCCESS); }
LIBCOUCHBASE_API lcb_error_t lcb_get_replica(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_get_replica_cmd_t *const *items) { lcb_server_t *server; protocol_binary_request_get req; int vb, idx; lcb_size_t ii, *affected_servers = 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); } } affected_servers = calloc(instance->nservers, sizeof(lcb_size_t)); if (affected_servers == NULL) { return lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM); } 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; for (ii = 0; ii < num; ++ii) { const void *key; lcb_size_t nkey; int r0, r1; lcb_replica_t strategy; struct lcb_command_data_st ct; memset(&ct, 0, sizeof(struct lcb_command_data_st)); ct.start = gethrtime(); ct.cookie = command_cookie; strategy = LCB_REPLICA_FIRST; r0 = 0; /* begin */ r1 = 0; /* end */ switch (items[ii]->version) { case 0: key = items[ii]->v.v0.key; nkey = items[ii]->v.v0.nkey; break; case 1: key = items[ii]->v.v1.key; nkey = items[ii]->v.v1.nkey; strategy = items[ii]->v.v1.strategy; switch (strategy) { case LCB_REPLICA_FIRST: r0 = r1 = 0; /* iterate replicas in a sequence until first * successful response */ ct.replica = 0; break; case LCB_REPLICA_SELECT: r0 = r1 = items[ii]->v.v1.index; if (r0 >= instance->nreplicas) { return lcb_synchandler_return(instance, LCB_EINVAL); } ct.replica = -1; /* do not iterate */ break; case LCB_REPLICA_ALL: r0 = 0; r1 = instance->nreplicas; ct.replica = -1; /* do not iterate */ break; } break; default: return lcb_synchandler_return(instance, LCB_EINVAL); } do { vb = vbucket_get_vbucket_by_key(instance->vbucket_config, key, nkey); idx = vbucket_get_replica(instance->vbucket_config, vb, r0); if (idx < 0 || idx > (int)instance->nservers) { free(affected_servers); /* FIXME: when 'packet' patch will be applied, here * should be rollback of all the previous commands * queued */ return lcb_synchandler_return(instance, LCB_NO_MATCHING_SERVER); } affected_servers[idx]++; server = instance->servers + idx; req.message.header.request.keylen = ntohs((lcb_uint16_t)nkey); req.message.header.request.vbucket = ntohs((lcb_uint16_t)vb); req.message.header.request.bodylen = ntohl((lcb_uint32_t)nkey); req.message.header.request.opaque = ++instance->seqno; lcb_server_start_packet_ex(server, &ct, req.bytes, sizeof(req.bytes)); lcb_server_write_packet(server, key, nkey); lcb_server_end_packet(server); ++r0; } while (r0 < r1); } for (ii = 0; ii < instance->nservers; ++ii) { if (affected_servers[ii]) { server = instance->servers + ii; lcb_server_send_packets(server); } } free(affected_servers); return lcb_synchandler_return(instance, LCB_SUCCESS); }
LIBCOUCHBASE_API lcb_error_t lcb_observe(lcb_t instance, const void *command_cookie, lcb_size_t num, const lcb_observe_cmd_t *const *items) { int vbid, idx, jj; lcb_size_t ii; lcb_uint32_t opaque; struct observe_st *requests; /* 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); } } if (instance->dist_type != VBUCKET_DISTRIBUTION_VBUCKET) { return lcb_synchandler_return(instance, LCB_NOT_SUPPORTED); } /* the list of pointers to body buffers for each server */ requests = calloc(instance->nservers, sizeof(struct observe_st)); opaque = ++instance->seqno; for (ii = 0; ii < num; ++ii) { const void *key = items[ii]->v.v0.key; lcb_size_t nkey = items[ii]->v.v0.nkey; const void *hashkey = items[ii]->v.v0.hashkey; lcb_size_t nhashkey = items[ii]->v.v0.nhashkey; if (nhashkey == 0) { hashkey = key; nhashkey = nkey; } vbid = vbucket_get_vbucket_by_key(instance->vbucket_config, hashkey, nhashkey); for (jj = -1; jj < instance->nreplicas; ++jj) { struct observe_st *rr; /* it will increment jj to get server index, so (-1 + 1) = 0 (master) */ idx = vbucket_get_replica(instance->vbucket_config, vbid, jj); if ((idx < 0 || idx > (int)instance->nservers)) { /* the config says that there is no server yet at that position (-1) */ if (jj == -1) { /* master node must be available */ destroy_requests(requests, instance->nservers); return lcb_synchandler_return(instance, LCB_NETWORK_ERROR); } else { continue; } } rr = requests + idx; if (!rr->allocated) { if (!init_request(rr)) { destroy_requests(requests, instance->nservers); return lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM); } rr->req.message.header.request.magic = PROTOCOL_BINARY_REQ; rr->req.message.header.request.opcode = CMD_OBSERVE; rr->req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; rr->req.message.header.request.opaque = opaque; } { lcb_uint16_t vb = htons((lcb_uint16_t)vbid); lcb_uint16_t len = htons((lcb_uint16_t)nkey); ringbuffer_ensure_capacity(&rr->body, sizeof(vb) + sizeof(len) + nkey); rr->nbody += ringbuffer_write(&rr->body, &vb, sizeof(vb)); rr->nbody += ringbuffer_write(&rr->body, &len, sizeof(len)); rr->nbody += ringbuffer_write(&rr->body, key, nkey); } } } for (ii = 0; ii < instance->nservers; ++ii) { struct observe_st *rr = requests + ii; lcb_server_t *server = instance->servers + ii; if (rr->allocated) { char *tmp; rr->req.message.header.request.bodylen = ntohl((lcb_uint32_t)rr->nbody); lcb_server_start_packet(server, command_cookie, rr->req.bytes, sizeof(rr->req.bytes)); if (ringbuffer_is_continous(&rr->body, RINGBUFFER_READ, rr->nbody)) { tmp = ringbuffer_get_read_head(&rr->body); TRACE_OBSERVE_BEGIN(&rr->req, server->authority, tmp, rr->nbody); lcb_server_write_packet(server, tmp, rr->nbody); } else { tmp = malloc(ringbuffer_get_nbytes(&rr->body)); if (!tmp) { /* FIXME by this time some of requests might be scheduled */ destroy_requests(requests, instance->nservers); return lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM); } else { ringbuffer_read(&rr->body, tmp, rr->nbody); TRACE_OBSERVE_BEGIN(&rr->req, server->authority, tmp, rr->nbody); lcb_server_write_packet(server, tmp, rr->nbody); } } lcb_server_end_packet(server); lcb_server_send_packets(server); } } destroy_requests(requests, instance->nservers); return lcb_synchandler_return(instance, LCB_SUCCESS); }
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, 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; }
/** * 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; }