void libcouchbase_server_buffer_start_packet(libcouchbase_server_t *c, const void *command_cookie, ringbuffer_t *buff, ringbuffer_t *buff_cookie, const void *data, libcouchbase_size_t size) { struct libcouchbase_command_data_st ct; memset(&ct, 0, sizeof(struct libcouchbase_command_data_st)); /* @todo we don't want to call gethrtime for each operation, */ /* so I need to pass it down the chain so that a large */ /* multiget can reuse the same timer... */ ct.start = gethrtime(); ct.cookie = command_cookie; if (ringbuffer_get_nbytes(buff_cookie) == 0) { c->next_timeout = ct.start; libcouchbase_update_timer(c->instance); } if (!ringbuffer_ensure_capacity(buff, size) || !ringbuffer_ensure_capacity(&c->cmd_log, size) || !ringbuffer_ensure_capacity(buff_cookie, sizeof(ct)) || ringbuffer_write(buff, data, size) != size || ringbuffer_write(&c->cmd_log, data, size) != size || ringbuffer_write(buff_cookie, &ct, sizeof(ct)) != sizeof(ct)) { abort(); } }
void libcouchbase_server_buffer_write_packet(libcouchbase_server_t *c, ringbuffer_t *buff, const void *data, libcouchbase_size_t size) { (void)c; if (!ringbuffer_ensure_capacity(buff, size) || !ringbuffer_ensure_capacity(&c->cmd_log, size) || ringbuffer_write(buff, data, size) != size || ringbuffer_write(&c->cmd_log, data, size) != size) { abort(); } }
static void request_config(cccp_provider *cccp) { protocol_binary_request_set_cluster_config req; lcb_connection_t conn = &cccp->connection; ringbuffer_t *buf = conn->output; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = CMD_GET_CLUSTER_CONFIG; req.message.header.request.opaque = 0xF00D; if (!buf) { if ((buf = calloc(1, sizeof(*buf))) == NULL) { mcio_error(cccp, LCB_CLIENT_ENOMEM); return; } conn->output = buf; } if (!ringbuffer_ensure_capacity(buf, sizeof(req.bytes))) { mcio_error(cccp, LCB_CLIENT_ENOMEM); } ringbuffer_write(buf, req.bytes, sizeof(req.bytes)); lcb_sockrw_set_want(conn, LCB_WRITE_EVENT, 1); lcb_sockrw_apply_want(conn); lcb_timer_rearm(cccp->timer, PROVIDER_SETTING(&cccp->base, config_node_timeout)); }
int ringbuffer_memcpy(ringbuffer_t *dst, ringbuffer_t *src, lcb_size_t nbytes) { ringbuffer_t copy = *src; struct lcb_iovec_st iov[2]; int ii = 0; lcb_size_t towrite = nbytes; lcb_size_t toread, nb; if (nbytes > ringbuffer_get_nbytes(src)) { /* EINVAL */ return -1; } if (!ringbuffer_ensure_capacity(dst, nbytes)) { /* Failed to allocate space */ return -1; } ringbuffer_get_iov(dst, RINGBUFFER_WRITE, iov); toread = minimum(iov[ii].iov_len, nbytes); do { assert(ii < 2); nb = ringbuffer_read(©, iov[ii].iov_base, toread); toread -= nb; towrite -= nb; ++ii; } while (towrite > 0); ringbuffer_produced(dst, nbytes); return 0; }
void lcbio_ctx_put(lcbio_CTX *ctx, const void *buf, unsigned nbuf) { lcbio__EASYRB *erb = ctx->output; if (!erb) { ctx->output = erb = calloc(1, sizeof(*ctx->output)); if (!erb) { lcbio_ctx_senderr(ctx, LCB_CLIENT_ENOMEM); return; } erb->parent = ctx; if (!ringbuffer_initialize(&erb->rb, nbuf)) { lcbio_ctx_senderr(ctx, LCB_CLIENT_ENOMEM); return; } } if (!ringbuffer_ensure_capacity(&erb->rb, nbuf)) { lcbio_ctx_senderr(ctx, LCB_CLIENT_ENOMEM); return; } ringbuffer_write(&erb->rb, buf, nbuf); }
static int do_fill_input_buffer(libcouchbase_server_t *c) { struct libcouchbase_iovec_st iov[2]; libcouchbase_ssize_t nr; if (!ringbuffer_ensure_capacity(&c->input, 8192)) { libcouchbase_error_handler(c->instance, LIBCOUCHBASE_CLIENT_ENOMEM, NULL); return -1; } ringbuffer_get_iov(&c->input, RINGBUFFER_WRITE, iov); nr = c->instance->io->recvv(c->instance->io, c->sock, iov, 2); if (nr == -1) { switch (c->instance->io->error) { case EINTR: break; case EWOULDBLOCK: return 0; default: libcouchbase_failout_server(c, LIBCOUCHBASE_NETWORK_ERROR); return -1; } } else if (nr == 0) { assert((iov[0].iov_len + iov[1].iov_len) != 0); /* TODO stash error message somewhere * "Connection closed... we should resend to other nodes or reconnect!!" */ libcouchbase_failout_server(c, LIBCOUCHBASE_NETWORK_ERROR); return -1; } else { ringbuffer_produced(&c->input, (libcouchbase_size_t)nr); } return 1; }
lcb_size_t ringbuffer_strcat(ringbuffer_t *buffer, const char *str) { lcb_size_t len = strlen(str); if (!ringbuffer_ensure_capacity(buffer, len)) { return 0; } return ringbuffer_write(buffer, str, len); }
void libcouchbase_server_buffer_retry_packet(libcouchbase_server_t *c, struct libcouchbase_command_data_st *ct, ringbuffer_t *buff, ringbuffer_t *buff_cookie, const void *data, libcouchbase_size_t size) { libcouchbase_size_t ct_size = sizeof(struct libcouchbase_command_data_st); if (ringbuffer_get_nbytes(buff_cookie) == 0) { c->next_timeout = ct->start; libcouchbase_update_timer(c->instance); } if (!ringbuffer_ensure_capacity(buff, size) || !ringbuffer_ensure_capacity(&c->cmd_log, size) || !ringbuffer_ensure_capacity(buff_cookie, ct_size) || ringbuffer_write(buff, data, size) != size || ringbuffer_write(&c->cmd_log, data, size) != size || ringbuffer_write(buff_cookie, ct, ct_size) != ct_size) { abort(); } }
int ringbuffer_append(ringbuffer_t *src, ringbuffer_t *dest) { char buffer[1024]; lcb_size_t nr, nw; while ((nr = ringbuffer_read(src, buffer, sizeof(buffer))) != 0) { lcb_assert(ringbuffer_ensure_capacity(dest, nr)); nw = ringbuffer_write(dest, buffer, nr); lcb_assert(nw == nr); } return 1; }
/** * Request a read of data into the buffer * @param conn the connection object * @param buf a ringbuffer structure. If the read request is successful, * the ringbuffer is destroyed. Its allocated data is owned by the IO plugin * for the duration of the operation. It may be restored via * ringbuffer_take_buffer once the operation has finished. */ lcb_sockrw_status_t lcb_sockrw_v1_start_read(lcb_connection_t conn, ringbuffer_t **buf, lcb_io_read_cb callback, lcb_io_error_cb error_callback) { int ret; lcb_io_opt_t io; struct lcb_buf_info *bi = &conn->sockptr->read_buffer; if (conn->sockptr->is_reading) { return LCB_SOCKRW_PENDING; } ringbuffer_ensure_capacity(*buf, conn->settings ? conn->settings->rbufsize : LCB_DEFAULT_RBUFSIZE); ringbuffer_get_iov(*buf, RINGBUFFER_WRITE, bi->iov); lcb_assert(bi->ringbuffer == NULL); lcb_assert(bi->root == NULL); bi->ringbuffer = *buf; bi->root = bi->ringbuffer->root; *buf = NULL; io = conn->io; ret = io->v.v1.start_read(io, conn->sockptr, callback); if (ret == 0) { conn->sockptr->is_reading = 1; return LCB_SOCKRW_PENDING; } else { *buf = bi->ringbuffer; memset(bi, 0, sizeof(*bi)); if (error_callback) { io->v.v1.send_error(io, conn->sockptr, error_callback); } } return LCB_SOCKRW_IO_ERROR; }
lcb_sockrw_status_t lcb_sockrw_v0_read(lcb_connection_t conn, ringbuffer_t *buf) { struct lcb_iovec_st iov[2]; lcb_ssize_t nr; lcb_io_opt_t io = conn->io; if (!ringbuffer_ensure_capacity(buf, conn->settings ? conn->settings->rbufsize : LCB_DEFAULT_RBUFSIZE)) { return LCB_SOCKRW_GENERIC_ERROR; } ringbuffer_get_iov(buf, RINGBUFFER_WRITE, iov); nr = io->v.v0.recvv(io, conn->sockfd, iov, 2); if (nr == -1) { switch (io->v.v0.error) { case EINTR: break; case EWOULDBLOCK: #ifdef USE_EAGAIN case EAGAIN: #endif return LCB_SOCKRW_WOULDBLOCK; default: return LCB_SOCKRW_IO_ERROR; return -1; } } else if (nr == 0) { lcb_assert((iov[0].iov_len + iov[1].iov_len) != 0); /* TODO stash error message somewhere * "Connection closed... we should resend to other nodes or reconnect!!" */ return LCB_SOCKRW_SHUTDOWN; } else { ringbuffer_produced(buf, (lcb_size_t)nr); } return LCB_SOCKRW_READ; }
int ringbuffer_append(ringbuffer_t *src, ringbuffer_t *dest) { char buffer[1024]; lcb_size_t nr, nw; while ((nr = ringbuffer_read(src, buffer, sizeof(buffer))) != 0) { if (!ringbuffer_ensure_capacity(dest, nr)) { abort(); return 0; } nw = ringbuffer_write(dest, buffer, nr); if (nw != nr) { abort(); return 0; } } return 1; }
/** * 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); }
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 lcb_error_t obs_ctxadd(lcb_MULTICMD_CTX *mctx, const lcb_CMDOBSERVE *cmd) { const void *hk; lcb_SIZE nhk; int vbid; unsigned maxix; int ii; OBSERVECTX *ctx = CTX_FROM_MULTI(mctx); lcb_t instance = ctx->instance; mc_CMDQUEUE *cq = &instance->cmdq; if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) { return LCB_EMPTY_KEY; } if (cq->config == NULL) { return LCB_CLIENT_ETMPFAIL; } if (instance->dist_type != LCBVB_DIST_VBUCKET) { return LCB_NOT_SUPPORTED; } mcreq_extract_hashkey(&cmd->key, &cmd->hashkey, 24, &hk, &nhk); vbid = lcbvb_k2vb(cq->config, hk, nhk); maxix = LCBVB_NREPLICAS(cq->config); for (ii = -1; ii < (int)maxix; ++ii) { struct observe_st *rr; lcb_U16 vb16, klen16; int ix; if (ii == -1) { ix = lcbvb_vbmaster(cq->config, vbid); if (ix < 0) { return LCB_NO_MATCHING_SERVER; } } else { ix = lcbvb_vbreplica(cq->config, vbid, ii); if (ix < 0) { continue; } } lcb_assert(ix < (int)ctx->nrequests); rr = ctx->requests + ix; if (!rr->allocated) { if (!init_request(rr)) { return LCB_CLIENT_ENOMEM; } } vb16 = htons((lcb_U16)vbid); klen16 = htons((lcb_U16)cmd->key.contig.nbytes); ringbuffer_ensure_capacity(&rr->body, sizeof(vb16) + sizeof(klen16)); ringbuffer_write(&rr->body, &vb16, sizeof vb16); ringbuffer_write(&rr->body, &klen16, sizeof klen16); ringbuffer_write(&rr->body, cmd->key.contig.bytes, cmd->key.contig.nbytes); ctx->remaining++; if (cmd->cmdflags & LCB_CMDOBSERVE_F_MASTER_ONLY) { break; } } return LCB_SUCCESS; }
static libcouchbase_error_t create_memcached(const struct libcouchbase_memcached_st *user, VBUCKET_CONFIG_HANDLE vbconfig) { ringbuffer_t buffer; char *copy = strdup(user->serverlist); char head[1024]; int first; char *ptr = copy; int fail; libcouchbase_ssize_t offset = 0; if (copy == NULL) { return LIBCOUCHBASE_CLIENT_ENOMEM; } if (ringbuffer_initialize(&buffer, 1024) == -1) { free(copy); return LIBCOUCHBASE_CLIENT_ENOMEM; } head[0] = '\0'; offset += snprintf(head + offset, sizeof(head) - offset, "%s", "{"); offset += snprintf(head + offset, sizeof(head) - offset, "%s", "\"bucketType\":\"memcached\","); offset += snprintf(head + offset, sizeof(head) - offset, "%s", "\"nodeLocator\":\"ketama\","); if (user->username != NULL) { offset += snprintf(head + offset, sizeof(head) - offset, "%s", "\"authType\":\"sasl\","); offset += snprintf(head + offset, sizeof(head) - offset, "%s", "\"name\":\""); offset += snprintf(head + offset, sizeof(head) - offset, "%s", user->username); offset += snprintf(head + offset, sizeof(head) - offset, "%s", "\","); if (user->password != NULL) { offset += snprintf(head + offset, sizeof(head) - offset, "%s", "\"saslPassword\":\""); offset += snprintf(head + offset, sizeof(head) - offset, "%s", user->password); offset += snprintf(head + offset, sizeof(head) - offset, "%s", "\","); } } offset += snprintf(head + offset, sizeof(head) - offset, "%s", "\"nodes\": ["); ringbuffer_write(&buffer, head, strlen(head)); /* Let's add the hosts... */ first = 1; do { char *tok; char *next = strchr(ptr, ';'); const char *port = "11211"; libcouchbase_ssize_t length; if (next != NULL) { *next = '\0'; } tok = strchr(ptr, ':'); if (tok != NULL) { *tok = '\0'; port = tok + 1; if ((tok = strchr(ptr, ':')) != NULL) { *tok = '\0'; /* Remove weight for now */ } } length = snprintf(head, sizeof(head), "%c{\"hostname\":\"%s\",\"ports\":{\"direct\":%s}}", first ? ' ' : ',', ptr, port); first = 0; if (ringbuffer_ensure_capacity(&buffer, length) == -1) { free(copy); return LIBCOUCHBASE_CLIENT_ENOMEM; } ringbuffer_write(&buffer, head, length); if (next != NULL) { ptr = next + 1; } else { ptr = NULL; } } while (ptr != NULL); if (ringbuffer_ensure_capacity(&buffer, 3) == -1) { free(copy); return LIBCOUCHBASE_CLIENT_ENOMEM; } ringbuffer_write(&buffer, "]}", 3); /* Include '\0' */ /* Now let's parse the config! */ fail = vbucket_config_parse(vbconfig, LIBVBUCKET_SOURCE_MEMORY, (char *)ringbuffer_get_read_head(&buffer)); free(copy); ringbuffer_destruct(&buffer); if (fail) { /* Hmm... internal error! */ return LIBCOUCHBASE_EINTERNAL; } return LIBCOUCHBASE_SUCCESS; }