lcb_error_t lcb_failout_server(lcb_server_t *server, lcb_error_t error) { lcb_purge_single_server(server, error); ringbuffer_reset(&server->cmd_log); ringbuffer_reset(&server->output_cookies); ringbuffer_reset(&server->pending); ringbuffer_reset(&server->pending_cookies); server->connection_ready = 0; lcb_server_release_connection(server, error); return error; }
static void Cw_handler(lcb_sockdata_t *sd, int status, void *arg) { lcbio__EASYRB *erb = arg; lcbio_CTX *ctx = erb->parent; (void)sd; ctx->npending--; if (!ctx->output) { ctx->output = erb; ringbuffer_reset(&erb->rb); } else { ringbuffer_destruct(&erb->rb); free(erb); } if (ctx->state == ES_ACTIVE && status) { invoke_entered_errcb(ctx, convert_lcberr(ctx, LCBIO_IOERR)); } if (ctx->state != ES_ACTIVE && ctx->npending == 0) { free_ctx(ctx); } }
void lcb_sockrw_v1_onwrite_common(lcb_sockdata_t *sock, lcb_io_writebuf_t *wbuf, ringbuffer_t **dst) { struct lcb_buf_info *bi = &wbuf->buffer; lcb_io_opt_t io = sock->parent; if (*dst) { lcb_assert(*dst != bi->ringbuffer); /** * We can't override the existing buffer, so just return */ io->v.v1.release_writebuf(io, sock, wbuf); return; } *dst = bi->ringbuffer; ringbuffer_reset(*dst); bi->ringbuffer = NULL; bi->root = NULL; io->v.v1.release_writebuf(io, sock, wbuf); (void)sock; }
void lcb_server_connected(lcb_server_t *server) { lcb_connection_t conn = &server->connection; server->connection_ready = 1; if (server->pending.nbytes > 0) { /* ** @todo we might want to do this a bit more optimal later on.. ** We're only using the pending ringbuffer while we're ** doing the SASL auth, so it shouldn't contain that ** much data.. */ ringbuffer_t copy = server->pending; ringbuffer_reset(&server->cmd_log); ringbuffer_reset(&server->output_cookies); ringbuffer_reset(conn->output); if (!ringbuffer_append(&server->pending, conn->output) || !ringbuffer_append(&server->pending_cookies, &server->output_cookies) || !ringbuffer_append(©, &server->cmd_log)) { ringbuffer_reset(&server->cmd_log); ringbuffer_reset(&server->output_cookies); lcb_server_release_connection(server, LCB_CLIENT_ENOMEM); lcb_connection_cleanup(conn); lcb_error_handler(server->instance, LCB_CLIENT_ENOMEM, NULL); return; } ringbuffer_reset(&server->pending); ringbuffer_reset(&server->pending_cookies); lcb_assert(conn->output->nbytes); lcb_server_send_packets(server); } }
void lcb_connection_close(lcb_connection_t conn) { lcb_io_opt_t io; conn->state = LCB_CONNSTATE_UNINIT; if (conn->instance == NULL || conn->instance->io == NULL) { lcb_assert(conn->sockfd < 0 && conn->sockptr == NULL); return; } io = conn->instance->io; if (io->version == 0) { if (conn->sockfd != INVALID_SOCKET) { if (conn->evinfo.ptr) { io->v.v0.delete_event(io, conn->sockfd, conn->evinfo.ptr); } io->v.v0.close(io, conn->sockfd); conn->sockfd = INVALID_SOCKET; } } else { if (conn->sockptr) { conn->sockptr->closed = 1; conn->sockptr->lcbconn = NULL; io->v.v1.close_socket(io, conn->sockptr); conn->sockptr = NULL; } } if (conn->input) { ringbuffer_reset(conn->input); } if (conn->output) { ringbuffer_reset(conn->output); } }
static lcb_error_t reset_buffer(ringbuffer_t **rb, lcb_size_t defsz) { if (*rb) { ringbuffer_reset(*rb); return LCB_SUCCESS; } *rb = calloc(1, sizeof(**rb)); if (*rb == NULL) { return LCB_CLIENT_ENOMEM; } if (!ringbuffer_initialize(*rb, defsz)) { return LCB_CLIENT_ENOMEM; } return LCB_SUCCESS; }
/* * 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); }