Exemple #1
0
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)
{
    libcouchbase_server_t *server;
    protocol_binary_request_gat req;
    int vb, idx;

    if (nhashkey == 0) {
        nhashkey = nkey;
        hashkey = key;
    }
    (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;

    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;
        libcouchbase_server_start_packet(server, command_cookie, req.bytes,
                                         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);
        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);
}
Exemple #2
0
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);
}
Exemple #3
0
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;
}
Exemple #4
0
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);
}
Exemple #5
0
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);
}
Exemple #6
0
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);
}
Exemple #7
0
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;
}
Exemple #8
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);
}
Exemple #9
0
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);
}