static void initial_connect_timeout_handler(libcouchbase_socket_t sock, short which, void *arg) { libcouchbase_t instance = arg; libcouchbase_error_handler(instance, LIBCOUCHBASE_CONNECT_ERROR, "Could not connect to server within allotted time"); if (instance->sock != INVALID_SOCKET) { /* Do we need to delete the event? */ instance->io->delete_event(instance->io, instance->sock, instance->event); instance->io->close(instance->io, instance->sock); instance->sock = INVALID_SOCKET; } instance->io->delete_timer(instance->io, instance->timeout.event); instance->timeout.next = 0; libcouchbase_maybe_breakout(instance); (void)sock; (void)which; /* Notice we do not re-set the vbucket_state_listener. This is by design, * as we are still in the same state, and are just reporting an error * back to the user */ }
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; }
void libcouchbase_server_event_handler(libcouchbase_socket_t sock, short which, void *arg) { libcouchbase_server_t *c = arg; (void)sock; if (which & LIBCOUCHBASE_READ_EVENT) { if (do_read_data(c) != 0) { /* TODO stash error message somewhere * "Failed to read from connection to \"%s:%s\"", c->hostname, c->port */ libcouchbase_failout_server(c, LIBCOUCHBASE_NETWORK_ERROR); return; } } if (which & LIBCOUCHBASE_WRITE_EVENT) { if (c->connected) { hrtime_t now = gethrtime(); hrtime_t tmo = c->instance->timeout.usec; tmo *= 1000; if (c->next_timeout != 0 && (now > (tmo + c->next_timeout))) { libcouchbase_purge_single_server(c, &c->cmd_log, &c->output_cookies, tmo, now, LIBCOUCHBASE_ETIMEDOUT); } } if (do_send_data(c) != 0) { /* TODO stash error message somewhere * "Failed to send to the connection to \"%s:%s\"", c->hostname, c->port */ libcouchbase_failout_server(c, LIBCOUCHBASE_NETWORK_ERROR); return; } } if (c->output.nbytes == 0) { c->instance->io->update_event(c->instance->io, c->sock, c->event, LIBCOUCHBASE_READ_EVENT, c, libcouchbase_server_event_handler); } else { c->instance->io->update_event(c->instance->io, c->sock, c->event, LIBCOUCHBASE_RW_EVENT, c, libcouchbase_server_event_handler); } libcouchbase_maybe_breakout(c->instance); /* Make it known that this was a success. */ libcouchbase_error_handler(c->instance, LIBCOUCHBASE_SUCCESS, NULL); }
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); }