示例#1
0
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
     */
}
示例#2
0
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;
}
示例#3
0
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);
}
示例#4
0
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;
}
示例#5
0
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);
}