/** * Get a pointer to the key. If the buffer isn't continous we need to * allocate a temporary chunk of memory and copy the packet over there. * packet will return the pointer to the newly allocated packet or * NULL if we didn't have to allocate anything. * * @param server the server owning the key * @param nkey the number of bytes in the key * @param packet where to store the result * @return pointer to the key */ static const char *get_key(lcb_server_t *server, lcb_uint16_t *nkey, char **packet) { protocol_binary_request_header req; lcb_size_t nr = ringbuffer_peek(&server->cmd_log, req.bytes, sizeof(req)); lcb_size_t packetsize = ntohl(req.request.bodylen) + (lcb_uint32_t)sizeof(req); char *keyptr; *packet = server->cmd_log.read_head; lcb_assert(nr == sizeof(req)); *nkey = ntohs(req.request.keylen); keyptr = *packet + sizeof(req) + req.request.extlen; *packet = NULL; if (!ringbuffer_is_continous(&server->cmd_log, RINGBUFFER_READ, packetsize)) { *packet = malloc(packetsize); if (*packet == NULL) { lcb_error_handler(server->instance, LCB_CLIENT_ENOMEM, NULL); return NULL; } nr = ringbuffer_peek(&server->cmd_log, *packet, packetsize); if (nr != packetsize) { lcb_error_handler(server->instance, LCB_EINTERNAL, NULL); free(*packet); return NULL; } keyptr = *packet + sizeof(req) + req.request.extlen; } return keyptr; }
/** * 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); }
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); } }
/* * Drop all packets with sequence number less than specified. * * The packets are considered as stale and the caller will receive * appropriate error code in the operation callback. * * Returns 0 on success */ int lcb_server_purge_implicit_responses(lcb_server_t *c, lcb_uint32_t seqno, hrtime_t end, int all) { protocol_binary_request_header req; /** Instance level allocated buffers */ ringbuffer_t *cmdlog, *cookies; lcb_size_t nr = ringbuffer_peek(&c->cmd_log, req.bytes, sizeof(req)); /* There should at _LEAST_ be _ONE_ message in here if we're not * trying to purge _ALL_ of the messages in the queue */ if (all && nr == 0) { return 0; } /** * Reading the command log is not re-entrant safe, as an additional * command to the same server may result in the command log being modified. * To this end, we must first buffer all the commands in a separate * ringbuffer (or simple buffer) for that matter, and only *then* * invoke the callbacks */ lcb_assert(nr == sizeof(req)); if (req.request.opaque >= seqno) { return 0; } cmdlog = &c->instance->purged_buf; cookies = &c->instance->purged_cookies; ringbuffer_reset(cmdlog); ringbuffer_reset(cookies); /** * Move all the commands we want to purge into the relevant ("local") buffers. * We will later read from these local buffers */ while (req.request.opaque < seqno) { lcb_size_t packetsize = ntohl(req.request.bodylen) + (lcb_uint32_t)sizeof(req); ringbuffer_memcpy(cmdlog, &c->cmd_log, packetsize); ringbuffer_consumed(&c->cmd_log, packetsize); ringbuffer_memcpy(cookies, &c->output_cookies, sizeof(struct lcb_command_data_st)); ringbuffer_consumed(&c->output_cookies, sizeof(struct lcb_command_data_st)); nr = ringbuffer_peek(&c->cmd_log, req.bytes, sizeof(req.bytes)); if (!nr) { break; } lcb_assert(nr == sizeof(req)); } nr = ringbuffer_peek(cmdlog, req.bytes, sizeof(req)); lcb_assert(nr == sizeof(req)); if (!all) { lcb_assert(c->cmd_log.nbytes); } do { struct lcb_command_data_st ct; char *packet = cmdlog->read_head; lcb_size_t packetsize = ntohl(req.request.bodylen) + (lcb_uint32_t)sizeof(req); char *keyptr; union { lcb_get_resp_t get; lcb_store_resp_t store; lcb_remove_resp_t remove; lcb_touch_resp_t touch; lcb_unlock_resp_t unlock; lcb_arithmetic_resp_t arithmetic; lcb_observe_resp_t observe; } resp; nr = ringbuffer_read(cookies, &ct, sizeof(ct)); lcb_assert(nr == sizeof(ct)); if (c->instance->histogram) { lcb_record_metrics(c->instance, end - ct.start, req.request.opcode); } if (!ringbuffer_is_continous(cmdlog, RINGBUFFER_READ, packetsize)) { packet = malloc(packetsize); if (packet == NULL) { lcb_error_handler(c->instance, LCB_CLIENT_ENOMEM, NULL); return -1; } nr = ringbuffer_peek(cmdlog, packet, packetsize); if (nr != packetsize) { lcb_error_handler(c->instance, LCB_EINTERNAL, NULL); free(packet); return -1; } } switch (req.request.opcode) { case PROTOCOL_BINARY_CMD_GATQ: case PROTOCOL_BINARY_CMD_GETQ: keyptr = packet + sizeof(req) + req.request.extlen; setup_lcb_get_resp_t(&resp.get, keyptr, ntohs(req.request.keylen), NULL, 0, 0, 0, 0); TRACE_GET_END(req.request.opaque, ntohs(req.request.vbucket), req.request.opcode, LCB_KEY_ENOENT, &resp.get); c->instance->callbacks.get(c->instance, ct.cookie, LCB_KEY_ENOENT, &resp.get); break; case CMD_OBSERVE: lcb_failout_observe_request(c, &ct, packet, sizeof(req.bytes) + ntohl(req.request.bodylen), LCB_SERVER_BUG); break; case PROTOCOL_BINARY_CMD_NOOP: break; default: { char errinfo[128] = { '\0' }; snprintf(errinfo, 128, "Unknown implicit send message op=%0x", req.request.opcode); lcb_error_handler(c->instance, LCB_EINTERNAL, errinfo); return -1; } } if (packet != cmdlog->read_head) { free(packet); } ringbuffer_consumed(cmdlog, packetsize); nr = ringbuffer_peek(cmdlog, req.bytes, sizeof(req)); if (nr == 0) { return 0; } lcb_assert(nr == sizeof(req)); } while (1); /* CONSTCOND */ return 0; }
static void purge_single_server(lcb_server_t *server, lcb_error_t error, hrtime_t min_nonstale, hrtime_t *tmo_next) { protocol_binary_request_header req; struct lcb_command_data_st ct; lcb_size_t nr; char *packet; lcb_size_t packetsize; char *keyptr; ringbuffer_t rest; ringbuffer_t *stream = &server->cmd_log; ringbuffer_t *cookies; ringbuffer_t *mirror = NULL; /* mirror buffer should be purged with main stream */ lcb_connection_t conn = &server->connection; lcb_size_t send_size = 0; lcb_size_t stream_size = ringbuffer_get_nbytes(stream); hrtime_t now = gethrtime(); if (server->connection_ready) { cookies = &server->output_cookies; } else { cookies = &server->pending_cookies; mirror = &server->pending; } if (conn->output) { /* This will usually be false for v1 */ send_size = ringbuffer_get_nbytes(conn->output); } lcb_assert(ringbuffer_initialize(&rest, 1024)); do { int allocated = 0; lcb_uint32_t headersize; lcb_uint16_t nkey; nr = ringbuffer_peek(cookies, &ct, sizeof(ct)); if (nr != sizeof(ct)) { break; } nr = ringbuffer_peek(stream, req.bytes, sizeof(req)); if (nr != sizeof(req)) { break; } packetsize = (lcb_uint32_t)sizeof(req) + ntohl(req.request.bodylen); if (stream->nbytes < packetsize) { break; } if (min_nonstale && ct.start >= min_nonstale) { lcb_log(LOGARGS(server, INFO), "Still have %d ms remaining for command", (ct.start - min_nonstale) / 1000000); if (tmo_next) { *tmo_next = (ct.start - min_nonstale) + 1; } break; } lcb_log(LOGARGS(server, INFO), "Command with cookie=%p timed out from server %s:%s", ct.cookie, server->curhost.host, server->curhost.port); ringbuffer_consumed(cookies, sizeof(ct)); lcb_assert(nr == sizeof(req)); packet = stream->read_head; if (server->instance->histogram) { lcb_record_metrics(server->instance, now - ct.start, req.request.opcode); } if (server->connection_ready && stream_size > send_size && (stream_size - packetsize) < send_size) { /* Copy the rest of the current packet into the temporary stream */ /* I do believe I have some IOV functions to do that? */ lcb_size_t nbytes = packetsize - (stream_size - send_size); lcb_assert(ringbuffer_memcpy(&rest, conn->output, nbytes) == 0); ringbuffer_consumed(conn->output, nbytes); send_size -= nbytes; } stream_size -= packetsize; headersize = (lcb_uint32_t)sizeof(req) + req.request.extlen + htons(req.request.keylen); if (!ringbuffer_is_continous(stream, RINGBUFFER_READ, headersize)) { packet = malloc(headersize); if (packet == NULL) { lcb_error_handler(server->instance, LCB_CLIENT_ENOMEM, NULL); abort(); } nr = ringbuffer_peek(stream, packet, headersize); if (nr != headersize) { lcb_error_handler(server->instance, LCB_EINTERNAL, NULL); free(packet); abort(); } allocated = 1; } keyptr = packet + sizeof(req) + req.request.extlen; nkey = ntohs(req.request.keylen); failout_single_request(server, &req, &ct, error, keyptr, nkey, packet); if (allocated) { free(packet); } ringbuffer_consumed(stream, packetsize); if (mirror) { ringbuffer_consumed(mirror, packetsize); } } while (1); /* CONSTCOND */ if (server->connection_ready && conn->output) { /* Preserve the rest of the stream */ lcb_size_t nbytes = ringbuffer_get_nbytes(stream); send_size = ringbuffer_get_nbytes(conn->output); if (send_size >= nbytes) { ringbuffer_consumed(conn->output, send_size - nbytes); lcb_assert(ringbuffer_memcpy(&rest, conn->output, nbytes) == 0); } ringbuffer_reset(conn->output); ringbuffer_append(&rest, conn->output); } ringbuffer_destruct(&rest); lcb_maybe_breakout(server->instance); }
static int parse_single(libcouchbase_server_t *c, hrtime_t stop) { protocol_binary_request_header req; protocol_binary_response_header header; libcouchbase_size_t nr; char *packet; libcouchbase_size_t packetsize; struct libcouchbase_command_data_st ct; nr = ringbuffer_peek(&c->input, header.bytes, sizeof(header)); if (nr < sizeof(header)) { return 0; } packetsize = ntohl(header.response.bodylen) + (libcouchbase_uint32_t)sizeof(header); if (c->input.nbytes < packetsize) { return 0; } /* Is it already timed out? */ nr = ringbuffer_peek(&c->cmd_log, req.bytes, sizeof(req)); if (nr < sizeof(req) || /* the command log doesn't know about it */ (header.response.opaque < req.request.opaque && header.response.opaque > 0)) { /* sasl comes with zero opaque */ /* already processed. */ ringbuffer_consumed(&c->input, packetsize); return 1; } packet = c->input.read_head; /* we have everything! */ if (!ringbuffer_is_continous(&c->input, RINGBUFFER_READ, packetsize)) { /* The buffer isn't continous.. for now just copy it out and ** operate on the copy ;) */ if ((packet = malloc(packetsize)) == NULL) { libcouchbase_error_handler(c->instance, LIBCOUCHBASE_CLIENT_ENOMEM, NULL); return -1; } nr = ringbuffer_read(&c->input, packet, packetsize); if (nr != packetsize) { libcouchbase_error_handler(c->instance, LIBCOUCHBASE_EINTERNAL, NULL); free(packet); return -1; } } nr = ringbuffer_peek(&c->output_cookies, &ct, sizeof(ct)); if (nr != sizeof(ct)) { libcouchbase_error_handler(c->instance, LIBCOUCHBASE_EINTERNAL, NULL); if (packet != c->input.read_head) { free(packet); } return -1; } ct.vbucket = ntohs(req.request.vbucket); switch (header.response.magic) { case PROTOCOL_BINARY_REQ: c->instance->request_handler[header.response.opcode](c, &ct, (void *)packet); break; case PROTOCOL_BINARY_RES: { int was_connected = c->connected; if (libcouchbase_server_purge_implicit_responses(c, header.response.opaque, stop) != 0) { if (packet != c->input.read_head) { free(packet); } return -1; } if (c->instance->histogram) { libcouchbase_record_metrics(c->instance, stop - ct.start, header.response.opcode); } if (ntohs(header.response.status) != PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET || header.response.opcode == CMD_GET_REPLICA) { c->instance->response_handler[header.response.opcode](c, &ct, (void *)packet); /* keep command and cookie until we get complete STAT response */ if (was_connected && (header.response.opcode != PROTOCOL_BINARY_CMD_STAT || header.response.keylen == 0)) { nr = ringbuffer_read(&c->cmd_log, req.bytes, sizeof(req)); assert(nr == sizeof(req)); ringbuffer_consumed(&c->cmd_log, ntohl(req.request.bodylen)); ringbuffer_consumed(&c->output_cookies, sizeof(ct)); } } else { int idx; char *body; libcouchbase_size_t nbody; libcouchbase_server_t *new_srv; /* re-schedule command to new server */ nr = ringbuffer_read(&c->cmd_log, req.bytes, sizeof(req)); assert(nr == sizeof(req)); idx = vbucket_found_incorrect_master(c->instance->vbucket_config, ntohs(req.request.vbucket), (int)c->index); assert((libcouchbase_size_t)idx < c->instance->nservers); new_srv = c->instance->servers + idx; req.request.opaque = ++c->instance->seqno; nbody = ntohl(req.request.bodylen); body = malloc(nbody); if (body == NULL) { libcouchbase_error_handler(c->instance, LIBCOUCHBASE_CLIENT_ENOMEM, NULL); return -1; } nr = ringbuffer_read(&c->cmd_log, body, nbody); assert(nr == nbody); nr = ringbuffer_read(&c->output_cookies, &ct, sizeof(ct)); assert(nr == sizeof(ct)); /* Preserve the cookie and timestamp for the command. This means * that the library will retry the command until its time will * out and the client will get LIBCOUCHBASE_ETIMEDOUT error in * command callback */ libcouchbase_server_retry_packet(new_srv, &ct, &req, sizeof(req)); libcouchbase_server_write_packet(new_srv, body, nbody); libcouchbase_server_end_packet(new_srv); libcouchbase_server_send_packets(new_srv); free(body); } break; } default: libcouchbase_error_handler(c->instance, LIBCOUCHBASE_PROTOCOL_ERROR, NULL); if (packet != c->input.read_head) { free(packet); } return -1; } if (packet != c->input.read_head) { free(packet); } else { ringbuffer_consumed(&c->input, packetsize); } return 1; }
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 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); } }