LIBCOUCHBASE_API libcouchbase_error_t libcouchbase_get_replica_by_key(libcouchbase_t instance, const void *command_cookie, const void *hashkey, libcouchbase_size_t nhashkey, libcouchbase_size_t num_keys, const void *const *keys, const libcouchbase_size_t *nkey) { libcouchbase_server_t *server; protocol_binary_request_get req; int vb, idx; libcouchbase_size_t ii, *affected_servers = NULL; /* we need a vbucket config before we can start getting data.. */ if (instance->vbucket_config == NULL) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_ETMPFAIL); } affected_servers = calloc(instance->nservers, sizeof(libcouchbase_size_t)); if (affected_servers == NULL) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_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_keys; ++ii) { if (nhashkey == 0) { nhashkey = nkey[ii]; hashkey = keys[ii]; } vb = vbucket_get_vbucket_by_key(instance->vbucket_config, hashkey, nhashkey); idx = vbucket_get_replica(instance->vbucket_config, vb, 0); if (idx < 0 || idx > (int)instance->nservers) { /* the config says that there is no server yet at that position (-1) */ free(affected_servers); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_NETWORK_ERROR); } affected_servers[idx]++; server = instance->servers + idx; req.message.header.request.keylen = ntohs((libcouchbase_uint16_t)nkey[ii]); req.message.header.request.vbucket = ntohs((libcouchbase_uint16_t)vb); req.message.header.request.bodylen = ntohl((libcouchbase_uint32_t)nkey[ii]); req.message.header.request.opaque = ++instance->seqno; libcouchbase_server_start_packet(server, command_cookie, req.bytes, sizeof(req.bytes)); libcouchbase_server_write_packet(server, keys[ii], nkey[ii]); libcouchbase_server_end_packet(server); } for (ii = 0; ii < instance->nservers; ++ii) { if (affected_servers[ii]) { server = instance->servers + ii; libcouchbase_server_send_packets(server); } } free(affected_servers); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_SUCCESS); }
LIBCOUCHBASE_API libcouchbase_error_t libcouchbase_arithmetic_by_key(libcouchbase_t instance, const void *hashkey, size_t nhashkey, const void *key, size_t nkey, int64_t delta, time_t exp, bool create, uint64_t initial) { uint16_t vb; libcouchbase_server_t *server; protocol_binary_request_incr req; // we need a vbucket config before we can start getting data.. libcouchbase_ensure_vbucket_config(instance); assert(instance->vbucket_config); if (nhashkey != 0) { vb = (uint16_t)vbucket_get_vbucket_by_key(instance->vbucket_config, hashkey, nhashkey); } else { vb = (uint16_t)vbucket_get_vbucket_by_key(instance->vbucket_config, key, nkey); } server = instance->servers + instance->vb_server_map[vb]; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_INCREMENT; req.message.header.request.keylen = ntohs((uint16_t)nkey); req.message.header.request.extlen = 20; req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs(vb); req.message.header.request.bodylen = ntohl((uint32_t)(nkey + 20)); req.message.header.request.opaque = ++instance->seqno; req.message.body.delta = ntohll((uint64_t)(delta)); req.message.body.initial = ntohll(initial); req.message.body.expiration = ntohl((uint32_t)exp); if (delta < 0) { req.message.header.request.opcode = PROTOCOL_BINARY_CMD_DECREMENT; req.message.body.delta = ntohll((uint64_t)(delta * -1)); } if (create) { memset(&req.message.body.expiration, 0xff, sizeof(req.message.body.expiration)); } libcouchbase_server_start_packet(server, req.bytes, sizeof(req.bytes)); libcouchbase_server_write_packet(server, key, nkey); libcouchbase_server_end_packet(server); libcouchbase_server_send_packets(server); return LIBCOUCHBASE_SUCCESS; }
static libcouchbase_error_t libcouchbase_single_get(libcouchbase_t instance, const void *command_cookie, const void *hashkey, libcouchbase_size_t nhashkey, const void *key, const libcouchbase_size_t nkey, const libcouchbase_time_t *exp, int lock) { libcouchbase_server_t *server; protocol_binary_request_gat req; int vb, idx; libcouchbase_size_t nbytes; if (nhashkey == 0) { nhashkey = nkey; hashkey = key; } (void)vbucket_map(instance->vbucket_config, hashkey, nhashkey, &vb, &idx); if (idx < 0 || idx > (int)instance->nservers) { /* the config says that there is no server yet at that position (-1) */ return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_NETWORK_ERROR); } server = instance->servers + idx; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.keylen = ntohs((libcouchbase_uint16_t)nkey); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs((libcouchbase_uint16_t)vb); req.message.header.request.bodylen = ntohl((libcouchbase_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((libcouchbase_uint32_t)exp[0]); req.message.header.request.bodylen = ntohl((libcouchbase_uint32_t)(nkey) + 4); nbytes = sizeof(req.bytes); } if (lock) { /* the expiration is optional for GETL command */ req.message.header.request.opcode = CMD_GET_LOCKED; } libcouchbase_server_start_packet(server, command_cookie, req.bytes, nbytes); libcouchbase_server_write_packet(server, key, nkey); libcouchbase_server_end_packet(server); libcouchbase_server_send_packets(server); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_SUCCESS); }
LIBCOUCHBASE_API libcouchbase_error_t libcouchbase_mtouch_by_key(libcouchbase_t instance, const void *command_cookie, const void *hashkey, libcouchbase_size_t nhashkey, libcouchbase_size_t num_keys, const void * const *keys, const libcouchbase_size_t *nkey, const libcouchbase_time_t *exp) { libcouchbase_server_t *server = NULL; libcouchbase_size_t ii; int vb, idx; /* we need a vbucket config before we can start getting data.. */ if (instance->vbucket_config == NULL) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_ETMPFAIL); } if (nhashkey != 0) { (void)vbucket_map(instance->vbucket_config, hashkey, nhashkey, &vb, &idx); server = instance->servers + (libcouchbase_size_t)idx; } for (ii = 0; ii < num_keys; ++ii) { protocol_binary_request_touch req; if (nhashkey == 0) { (void)vbucket_map(instance->vbucket_config, keys[ii], nkey[ii], &vb, &idx); server = instance->servers + (libcouchbase_size_t)idx; } memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_TOUCH; req.message.header.request.extlen = 4; req.message.header.request.keylen = ntohs((libcouchbase_uint16_t)nkey[ii]); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs((libcouchbase_uint16_t)vb); req.message.header.request.bodylen = ntohl((libcouchbase_uint32_t)(nkey[ii]) + 4); req.message.header.request.opaque = ++instance->seqno; /* @todo fix the relative time! */ req.message.body.expiration = htonl((libcouchbase_uint32_t)exp[ii]); libcouchbase_server_start_packet(server, command_cookie, req.bytes, sizeof(req.bytes)); libcouchbase_server_write_packet(server, keys[ii], nkey[ii]); libcouchbase_server_end_packet(server); } libcouchbase_server_send_packets(server); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_SUCCESS); }
LIBCOUCHBASE_API libcouchbase_error_t libcouchbase_remove_by_key(libcouchbase_t instance, const void *command_cookie, const void *hashkey, libcouchbase_size_t nhashkey, const void *key, libcouchbase_size_t nkey, libcouchbase_cas_t cas) { libcouchbase_server_t *server; protocol_binary_request_delete req; int vb, idx; /* we need a vbucket config before we can start removing the item.. */ if (instance->vbucket_config == NULL) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_ETMPFAIL); } if (nhashkey == 0) { nhashkey = nkey; hashkey = key; } (void)vbucket_map(instance->vbucket_config, hashkey, nhashkey, &vb, &idx); if (idx < 0 || idx > (int)instance->nservers) { /* the config says that there is no server yet at that position (-1) */ return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_NETWORK_ERROR); } server = instance->servers + idx; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_DELETE; req.message.header.request.keylen = ntohs((libcouchbase_uint16_t)nkey); req.message.header.request.extlen = 0; req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs((libcouchbase_uint16_t)vb); req.message.header.request.bodylen = ntohl((libcouchbase_uint32_t)nkey); req.message.header.request.opaque = ++instance->seqno; req.message.header.request.cas = cas; libcouchbase_server_start_packet(server, command_cookie, req.bytes, sizeof(req.bytes)); libcouchbase_server_write_packet(server, key, nkey); libcouchbase_server_end_packet(server); libcouchbase_server_send_packets(server); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_SUCCESS); }
LIBCOUCHBASE_API libcouchbase_error_t libcouchbase_mget_by_key(libcouchbase_t instance, const void *command_cookie, const void *hashkey, libcouchbase_size_t nhashkey, libcouchbase_size_t num_keys, const void * const *keys, const libcouchbase_size_t *nkey, const libcouchbase_time_t *exp) { libcouchbase_server_t *server = NULL; protocol_binary_request_noop noop; libcouchbase_size_t ii, *affected_servers = NULL; int vb, idx; struct server_info_st *servers = NULL; /* we need a vbucket config before we can start getting data.. */ if (instance->vbucket_config == NULL) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_ETMPFAIL); } if (num_keys == 1) { return libcouchbase_single_get(instance, command_cookie, hashkey, nhashkey, keys[0], nkey[0], exp); } affected_servers = calloc(instance->nservers, sizeof(libcouchbase_size_t)); if (affected_servers == NULL) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_ENOMEM); } if (nhashkey != 0) { (void)vbucket_map(instance->vbucket_config, hashkey, nhashkey, &vb, &idx); if (idx < 0 || (libcouchbase_size_t)idx > instance->nservers) { /* the config says that there is no server yet at that position (-1) */ return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_NETWORK_ERROR); } server = instance->servers + (libcouchbase_size_t)idx; affected_servers[idx]++; } else { servers = malloc(num_keys * sizeof(struct server_info_st)); if (servers == NULL) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_ENOMEM); } for (ii = 0; ii < num_keys; ++ii) { (void)vbucket_map(instance->vbucket_config, keys[ii], nkey[ii], &servers[ii].vb, &servers[ii].idx); if (servers[ii].idx < 0 || (libcouchbase_size_t)servers[ii].idx > instance->nservers) { /* the config says that there is no server yet at that position (-1) */ free(servers); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_NETWORK_ERROR); } affected_servers[servers[ii].idx]++; } } for (ii = 0; ii < num_keys; ++ii) { protocol_binary_request_gat req; if (nhashkey == 0) { server = instance->servers + (libcouchbase_size_t)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((libcouchbase_uint16_t)nkey[ii]); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs((libcouchbase_uint16_t)vb); req.message.header.request.bodylen = ntohl((libcouchbase_uint32_t)(nkey[ii])); req.message.header.request.opaque = ++instance->seqno; if (!exp) { req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GETQ; libcouchbase_server_start_packet(server, command_cookie, req.bytes, sizeof(req.bytes) - 4); } else { req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GATQ; req.message.header.request.extlen = 4; req.message.body.expiration = ntohl((libcouchbase_uint32_t)exp[ii]); req.message.header.request.bodylen = ntohl((libcouchbase_uint32_t)(nkey[ii]) + 4); libcouchbase_server_start_packet(server, command_cookie, req.bytes, sizeof(req.bytes)); } libcouchbase_server_write_packet(server, keys[ii], nkey[ii]); libcouchbase_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; libcouchbase_server_complete_packet(server, command_cookie, noop.bytes, sizeof(noop.bytes)); libcouchbase_server_send_packets(server); } } free(affected_servers); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_SUCCESS); }
LIBCOUCHBASE_API libcouchbase_error_t libcouchbase_mget_by_key(libcouchbase_t instance, const void *hashkey, size_t nhashkey, size_t num_keys, const void * const *keys, const size_t *nkey, const time_t *exp) { uint16_t vb; libcouchbase_server_t *server; protocol_binary_request_noop noop; size_t ii; // we need a vbucket config before we can start getting data.. libcouchbase_ensure_vbucket_config(instance); assert(instance->vbucket_config); if (nhashkey != 0) { vb = (uint16_t)vbucket_get_vbucket_by_key(instance->vbucket_config, hashkey, nhashkey); server = instance->servers + instance->vb_server_map[vb]; } for (ii = 0; ii < num_keys; ++ii) { protocol_binary_request_gat req; if (nhashkey == 0) { vb = (uint16_t)vbucket_get_vbucket_by_key(instance->vbucket_config, keys[ii], nkey[ii]); server = instance->servers + instance->vb_server_map[vb]; } memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.keylen = ntohs((uint16_t)nkey[ii]); req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs(vb); req.message.header.request.bodylen = ntohl((uint32_t)(nkey[ii])); req.message.header.request.opaque = ++instance->seqno; if (!exp) { req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GETQ; libcouchbase_server_start_packet(server, req.bytes, sizeof(req.bytes) - 4); } else { req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GATQ; req.message.header.request.extlen = 4; req.message.body.expiration = ntohl((uint32_t)exp[ii]); libcouchbase_server_start_packet(server, req.bytes, sizeof(req.bytes)); } libcouchbase_server_write_packet(server, keys[ii], nkey[ii]); libcouchbase_server_end_packet(server); } 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; if (nhashkey == 0) { // 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) { server = instance->servers + ii; if (server->output.avail > 0 || server->pending.avail > 0) { noop.message.header.request.opaque = ++instance->seqno; libcouchbase_server_complete_packet(server, noop.bytes, sizeof(noop.bytes)); libcouchbase_server_send_packets(server); } } } else { noop.message.header.request.opaque = ++instance->seqno; libcouchbase_server_complete_packet(server, noop.bytes, sizeof(noop.bytes)); libcouchbase_server_send_packets(server); } return LIBCOUCHBASE_SUCCESS; }
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_error_t libcouchbase_store_by_key(libcouchbase_t instance, const void *command_cookie, libcouchbase_storage_t operation, const void *hashkey, libcouchbase_size_t nhashkey, const void *key, libcouchbase_size_t nkey, const void *bytes, libcouchbase_size_t nbytes, libcouchbase_uint32_t flags, libcouchbase_time_t exp, libcouchbase_cas_t cas) { libcouchbase_server_t *server; protocol_binary_request_set req; libcouchbase_size_t headersize; libcouchbase_size_t bodylen; int vb, idx; /* we need a vbucket config before we can start getting data.. */ if (instance->vbucket_config == NULL) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_ETMPFAIL); } if (nhashkey == 0) { nhashkey = nkey; hashkey = key; } (void)vbucket_map(instance->vbucket_config, hashkey, nhashkey, &vb, &idx); if (idx < 0 || idx > (int)instance->nservers) { /* the config says that there is no server yet at that position (-1) */ return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_NETWORK_ERROR); } server = instance->servers + idx; memset(&req, 0, sizeof(req)); req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.keylen = ntohs((libcouchbase_uint16_t)nkey); req.message.header.request.extlen = 8; req.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES; req.message.header.request.vbucket = ntohs((libcouchbase_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((libcouchbase_uint32_t)exp); headersize = sizeof(req.bytes); switch (operation) { case LIBCOUCHBASE_ADD: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_ADD; break; case LIBCOUCHBASE_REPLACE: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_REPLACE; break; case LIBCOUCHBASE_SET: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SET; break; case LIBCOUCHBASE_APPEND: req.message.header.request.opcode = PROTOCOL_BINARY_CMD_APPEND; req.message.header.request.extlen = 0; headersize -= 8; break; case LIBCOUCHBASE_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 libcouchbase_synchandler_return(instance, libcouchbase_error_handler(instance, LIBCOUCHBASE_EINVAL, "Invalid value passed as storage operation")); } /* Make it known that this was a success. */ libcouchbase_error_handler(instance, LIBCOUCHBASE_SUCCESS, NULL); bodylen = nkey + nbytes + req.message.header.request.extlen; req.message.header.request.bodylen = htonl((libcouchbase_uint32_t)bodylen); libcouchbase_server_start_packet(server, command_cookie, &req, headersize); libcouchbase_server_write_packet(server, key, nkey); libcouchbase_server_write_packet(server, bytes, nbytes); libcouchbase_server_end_packet(server); libcouchbase_server_send_packets(server); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_SUCCESS); }
LIBCOUCHBASE_API libcouchbase_error_t libcouchbase_observe(libcouchbase_t instance, const void *command_cookie, libcouchbase_size_t num_keys, const void *const *keys, const libcouchbase_size_t *nkey) { int vbid, idx, jj; libcouchbase_size_t ii; libcouchbase_uint32_t opaque; struct observe_st *requests; /* we need a vbucket config before we can start getting data.. */ if (instance->vbucket_config == NULL) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_ETMPFAIL); } if (instance->dist_type != VBUCKET_DISTRIBUTION_VBUCKET) { return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_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_keys; ++ii) { vbid = vbucket_get_vbucket_by_key(instance->vbucket_config, keys[ii], nkey[ii]); 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 libcouchbase_synchandler_return(instance, LIBCOUCHBASE_NETWORK_ERROR); } else { continue; } } rr = requests + idx; if (!rr->allocated) { if (!init_request(rr)) { destroy_requests(requests, instance->nservers); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_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; } { libcouchbase_uint16_t vb = htons(vbid); libcouchbase_uint16_t len = htons((libcouchbase_uint16_t)nkey[ii]); ringbuffer_ensure_capacity(&rr->body, sizeof(vb) + sizeof(len) + nkey[ii]); rr->nbody += ringbuffer_write(&rr->body, &vb, sizeof(vb)); rr->nbody += ringbuffer_write(&rr->body, &len, sizeof(len)); rr->nbody += ringbuffer_write(&rr->body, keys[ii], nkey[ii]); } } } for (ii = 0; ii < instance->nservers; ++ii) { struct observe_st *rr = requests + ii; libcouchbase_server_t *server = instance->servers + ii; if (rr->allocated) { rr->req.message.header.request.bodylen = ntohl((libcouchbase_uint32_t)rr->nbody); libcouchbase_server_start_packet(server, command_cookie, rr->req.bytes, sizeof(rr->req.bytes)); if (ringbuffer_is_continous(&rr->body, RINGBUFFER_READ, rr->nbody)) { libcouchbase_server_write_packet(server, ringbuffer_get_read_head(&rr->body), rr->nbody); } else { char *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 libcouchbase_synchandler_return(instance, LIBCOUCHBASE_CLIENT_ENOMEM); } else { ringbuffer_read(&rr->body, tmp, rr->nbody); libcouchbase_server_write_packet(server, tmp, rr->nbody); } } libcouchbase_server_end_packet(server); libcouchbase_server_send_packets(server); } } destroy_requests(requests, instance->nservers); return libcouchbase_synchandler_return(instance, LIBCOUCHBASE_SUCCESS); }