Beispiel #1
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;
}
Beispiel #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);
}
Beispiel #3
0
int vbucket_map(VBUCKET_CONFIG_HANDLE vb, const void *key, size_t nkey,
                int *vbucket_id, int *server_idx)
{
    uint32_t digest, mid, prev;
    struct continuum_item_st *beginp, *endp, *midp, *highp, *lowp;

    if (vb->distribution == VBUCKET_DISTRIBUTION_KETAMA) {
        assert(vb->continuum);
        if (vbucket_id) {
            *vbucket_id = 0;
        }
        digest = hash_ketama(key, nkey);
        beginp = lowp = vb->continuum;
        endp = highp = vb->continuum + vb->num_continuum;

        /* divide and conquer array search to find server with next biggest
         * point after what this key hashes to */
        while (1)
        {
            /* pick the middle point */
            midp = lowp + (highp - lowp) / 2;

            if (midp == endp) {
                /* if at the end, roll back to zeroth */
                *server_idx = beginp->index;
                break;
            }

            mid = midp->point;
            prev = (midp == beginp) ? 0 : (midp-1)->point;

            if (digest <= mid && digest > prev) {
                /* we found nearest server */
                *server_idx = midp->index;
                break;
            }

            /* adjust the limits */
            if (mid < digest) {
                lowp = midp + 1;
            } else {
                highp = midp - 1;
            }

            if (lowp > highp) {
                *server_idx = beginp->index;
                break;
            }
        }
    } else {
        *vbucket_id = vbucket_get_vbucket_by_key(vb, key, nkey);
        *server_idx = vbucket_get_master(vb, *vbucket_id);
    }
    return 0;
}
Beispiel #4
0
uint32_t lvb_key_hash(mcs_st *ptr, const char *key, size_t key_length, int *vbucket) {
    assert(ptr->kind == MCS_KIND_LIBVBUCKET);
    assert(ptr->data != NULL);

    VBUCKET_CONFIG_HANDLE vch = (VBUCKET_CONFIG_HANDLE) ptr->data;

    int v = vbucket_get_vbucket_by_key(vch, key, key_length);
    if (vbucket != NULL) {
        *vbucket = v;
    }

    return (uint32_t) vbucket_get_master(vch, v);
}
Beispiel #5
0
static void testConfig(const char *fname) {
    int whoops = 0;
    const struct key_st *k;
    int i = 0;

    VBUCKET_CONFIG_HANDLE vb = vbucket_config_parse_file(configPath(fname));
    if (vb == NULL) {
        fprintf(stderr, "vbucket_config_parse_file error: %s\n",
                vbucket_get_error());
        abort();
    }

    while ((k = &keys[i++])->key != NULL) {
        int id = vbucket_get_vbucket_by_key(vb, k->key, strlen(k->key));
        if (id != k->vbucket) {
            fprintf(stderr, "Expected vbucket %d for key '%s' but got %d\n",
                    k->vbucket, k->key, id);
            whoops = 1;
        }
    }

    if (whoops) {
        abort();
    }

    assert(vbucket_config_get_num_servers(vb) == 3 || vbucket_config_get_num_servers(vb) == 4);
    assert(vbucket_config_get_num_replicas(vb) == 2);

    for (i = 0; i < 3; ++i) {
        assert(strcmp(vbucket_config_get_server(vb, i), servers[i]) == 0);
    }

    for (i = 0; i < 4; ++i) {
        assert(vbucket_get_master(vb, i) == vbuckets[i].master);
        assert(vbucket_get_replica(vb, i, 0) == vbuckets[i].replicas[0]);
        assert(vbucket_get_replica(vb, i, 1) == vbuckets[i].replicas[1]);
    }

    assert(vbucket_config_get_user(vb) == NULL);
    assert(vbucket_config_get_password(vb) == NULL);

    vbucket_config_destroy(vb);
}
Beispiel #6
0
/**
 * Extended version of observe command. This allows us to service
 * various forms of higher level operations which use observe in one way
 * or another
 */
lcb_error_t lcb_observe_ex(lcb_t instance,
                           const void *command_cookie,
                           lcb_size_t num,
                           const void *const *items,
                           lcb_observe_type_t type)
{
    lcb_size_t ii;
    lcb_size_t maxix;
    lcb_uint32_t opaque;
    struct lcb_command_data_st ct;
    struct observe_requests_st reqs;

    memset(&reqs, 0, sizeof(reqs));

    if (instance->type != LCB_TYPE_BUCKET) {
        return lcb_synchandler_return(instance, LCB_EBADHANDLE);
    }

    if (instance->config.handle == NULL) {
        return lcb_synchandler_return(instance, LCB_CLIENT_ETMPFAIL);
    }

    if (instance->config.dist_type != VBUCKET_DISTRIBUTION_VBUCKET) {
        return lcb_synchandler_return(instance, LCB_NOT_SUPPORTED);
    }

    opaque = ++instance->seqno;
    ct.cookie = command_cookie;
    maxix = instance->config.nreplicas;

    if (type == LCB_OBSERVE_TYPE_CHECK) {
        maxix = 0;

    } else {
        if (type == LCB_OBSERVE_TYPE_DURABILITY) {
            ct.flags = LCB_CMD_F_OBS_DURABILITY | LCB_CMD_F_OBS_BCAST;

        } else {
            ct.flags = LCB_CMD_F_OBS_BCAST;
        }
    }

    reqs.nrequests = instance->nservers;
    reqs.requests = calloc(reqs.nrequests, sizeof(*reqs.requests));

    for (ii = 0; ii < num; ii++) {
        const void *key, *hashkey;
        lcb_size_t nkey, nhashkey;
        int vbid, jj;

        if (type == LCB_OBSERVE_TYPE_DURABILITY) {
            const lcb_durability_entry_t *ent = items[ii];
            key = ent->request.v.v0.key;
            nkey = ent->request.v.v0.nkey;
            hashkey = ent->request.v.v0.hashkey;
            nhashkey = ent->request.v.v0.nhashkey;
        } else {
            const lcb_observe_cmd_t *ocmd = items[ii];
            key = ocmd->v.v0.key;
            nkey = ocmd->v.v0.nkey;
            hashkey = ocmd->v.v0.hashkey;
            nhashkey = ocmd->v.v0.nhashkey;
        }
        if (!nhashkey) {
            hashkey = key;
            nhashkey = nkey;
        }

        vbid = vbucket_get_vbucket_by_key(instance->config.handle,
                                          hashkey, nhashkey);

        for (jj = -1; jj < (int)maxix; jj++) {
            struct observe_st *rr;

            int idx = vbucket_get_replica(instance->config.handle,
                                          vbid, jj);

            if (idx < 0 || idx > (int)instance->nservers) {
                if (jj == -1) {
                    destroy_requests(&reqs);
                    return lcb_synchandler_return(instance, LCB_NO_MATCHING_SERVER);
                }
                continue;
            }
            lcb_assert(idx < (int)reqs.nrequests);
            rr = reqs.requests + idx;

            if (!rr->allocated) {
                if (!init_request(rr)) {
                    destroy_requests(&reqs);
                    return lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM);
                }
            }

            {
                lcb_uint16_t vb = htons((lcb_uint16_t)vbid);
                lcb_uint16_t len = htons((lcb_uint16_t)nkey);

                rr->packet.message.header.request.magic = PROTOCOL_BINARY_REQ;
                rr->packet.message.header.request.opcode = CMD_OBSERVE;
                rr->packet.message.header.request.datatype = PROTOCOL_BINARY_RAW_BYTES;
                rr->packet.message.header.request.opaque = opaque;

                ringbuffer_ensure_capacity(&rr->body,
                                           sizeof(vb) + sizeof(len) + nkey);
                rr->nbody += ringbuffer_write(&rr->body, &vb, sizeof(vb));
                rr->nbody += ringbuffer_write(&rr->body, &len, sizeof(len));
                rr->nbody += ringbuffer_write(&rr->body, key, nkey);
            }
        }
    }

    for (ii = 0; ii < reqs.nrequests; ii++) {
        struct observe_st *rr = reqs.requests + ii;
        struct lcb_server_st *server = instance->servers + ii;
        char *tmp;

        if (!rr->allocated) {
            continue;
        }

        rr->packet.message.header.request.bodylen = ntohl((lcb_uint32_t)rr->nbody);
        ct.start = gethrtime();

        lcb_server_start_packet_ct(server, &ct, rr->packet.bytes,
                                   sizeof(rr->packet.bytes));

        if (ringbuffer_is_continous(&rr->body, RINGBUFFER_READ, rr->nbody)) {
            tmp = ringbuffer_get_read_head(&rr->body);
            TRACE_OBSERVE_BEGIN(&rr->packet, server->authority, tmp, rr->nbody);
            lcb_server_write_packet(server, tmp, rr->nbody);
        } else {
            tmp = malloc(ringbuffer_get_nbytes(&rr->body));
            if (!tmp) {
                /* FIXME by this time some of requests might be scheduled */
                destroy_requests(&reqs);
                return lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM);
            } else {
                ringbuffer_read(&rr->body, tmp, rr->nbody);
                TRACE_OBSERVE_BEGIN(&rr->packet, server->authority, tmp, rr->nbody);
                lcb_server_write_packet(server, tmp, rr->nbody);
            }
        }
        lcb_server_end_packet(server);
        lcb_server_send_packets(server);
    }

    destroy_requests(&reqs);
    return lcb_synchandler_return(instance, LCB_SUCCESS);
}
Beispiel #7
0
int main(int argc, char **argv) {
    VBUCKET_CONFIG_HANDLE vb = NULL;
    int rval;
    int num_keys_per_vbucket;
    int num_keys_to_generate;
    int num_vbuckets;
    char *** keys;
    int i, j, v, k, total;
    char *key;

    if (argc < 4) {
        printf("vbucketkeygen mapfile <keys per vbucket> <keys to generate>\n\n");
        printf("  vbucketkeygen will output a list of keys that equally\n");
        printf("    distribute amongst every vbucket.\n\n");
        printf("  vbucketkeygen expects a vBucketServerMap JSON mapfile, and\n");
        printf("  will print the keyname and vBucketId.\n");
        printf("  You may use '-' instead for the filename to specify stdin.\n\n");
        printf("  Examples:\n");
        printf("    ./vbucketkeygen file.json 10 10000\n\n");
        printf("    curl http://HOST:8091/pools/default/buckets/default | \\\n");
        printf("       ./vbucketkeygen - 5 10000\n");
        exit(1);
    }


    if (strcmp("-", argv[1]) == 0) {
        char buf[50000];
        if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
            fprintf(stderr, "ERROR: vbucketkeygen found no input on stdin\n");
            exit(1);
        }
        buf[sizeof(buf) - 1] = '\0';

        vb = vbucket_config_parse_string(buf);
    } else {
        vb = vbucket_config_parse_file(argv[1]);
    }

    if (vb == NULL) {
        fprintf(stderr, "ERROR: vbucket_config_parse_string error: %s\n", vbucket_get_error());
        exit(1);
    }

    rval = 0;
    num_keys_per_vbucket = atoi(argv[2]);
    num_keys_to_generate = atoi(argv[3]);
    num_vbuckets = vbucket_config_get_num_vbuckets(vb);

    /* allocate memory and set each key to null since strdup will allocate that */
    keys = malloc(sizeof(char***) * num_vbuckets);
    for (i = 0; i < num_vbuckets; i++) {
        keys[i] = malloc(sizeof(char**) * num_keys_per_vbucket);
    }
    for (i = 0; i < num_vbuckets; i++) {
        for (j = 0 ; j < num_keys_per_vbucket ; j++) {
            keys[i][j] = 0;
        }
    }

    /* generate keys and copy them to the keys structure */
    key = malloc(sizeof(char) * (MAX_KEY_SIZE+1));
    for (i = 0; i < num_keys_to_generate; i++) {
        snprintf(key, MAX_KEY_SIZE + 1, "key_%010d", i);
        v = vbucket_get_vbucket_by_key(vb, key, strlen(key));
        for (k = 0; k < num_keys_per_vbucket; k++) {
            if (keys[v][k] == 0) {
                keys[v][k] = strdup(key);
                break;
            }
        }
    }

    /* print out <key> <vbucket> and count up total keys
       so we can check that every vbucket has the correct
       number of keys */
    total = 0;
    for (i = 0; i < num_vbuckets; i++) {
        for (j = 0 ; j < num_keys_per_vbucket ; j++) {
            if (keys[i][j] != 0) {
                printf("%s %d\n", keys[i][j], i);
                total++;
            }
        }
    }

    if (total < (num_vbuckets * num_keys_per_vbucket)) {
        fprintf(stderr, "some vbuckets don't have enough keys\n");
        rval = 1;
    }

    vbucket_config_destroy(vb);

    return rval;
}
Beispiel #8
0
LIBCOUCHBASE_API
lcb_error_t lcb_get_replica(lcb_t instance,
                            const void *command_cookie,
                            lcb_size_t num,
                            const lcb_get_replica_cmd_t *const *items)
{
    lcb_server_t *server;
    protocol_binary_request_get req;
    int vb, idx;
    lcb_size_t ii, *affected_servers = NULL;

    /* we need a vbucket config before we can start getting data.. */
    if (instance->vbucket_config == NULL) {
        switch (instance->type) {
        case LCB_TYPE_CLUSTER:
            return lcb_synchandler_return(instance, LCB_EBADHANDLE);
        case LCB_TYPE_BUCKET:
        default:
            return lcb_synchandler_return(instance, LCB_CLIENT_ETMPFAIL);
        }
    }

    affected_servers = calloc(instance->nservers, sizeof(lcb_size_t));
    if (affected_servers == NULL) {
        return lcb_synchandler_return(instance, LCB_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; ++ii) {
        const void *key;
        lcb_size_t nkey;
        int r0, r1;
        lcb_replica_t strategy;
        struct lcb_command_data_st ct;

        memset(&ct, 0, sizeof(struct lcb_command_data_st));
        ct.start = gethrtime();
        ct.cookie = command_cookie;
        strategy = LCB_REPLICA_FIRST;
        r0 = 0; /* begin */
        r1 = 0; /* end */

        switch (items[ii]->version) {
        case 0:
            key = items[ii]->v.v0.key;
            nkey = items[ii]->v.v0.nkey;
            break;
        case 1:
            key = items[ii]->v.v1.key;
            nkey = items[ii]->v.v1.nkey;
            strategy = items[ii]->v.v1.strategy;
            switch (strategy) {
            case LCB_REPLICA_FIRST:
                r0 = r1 = 0;
                /* iterate replicas in a sequence until first
                 * successful response */
                ct.replica = 0;
                break;
            case LCB_REPLICA_SELECT:
                r0 = r1 = items[ii]->v.v1.index;
                if (r0 >= instance->nreplicas) {
                    return lcb_synchandler_return(instance, LCB_EINVAL);
                }
                ct.replica = -1; /* do not iterate */
                break;
            case LCB_REPLICA_ALL:
                r0 = 0;
                r1 = instance->nreplicas;
                ct.replica = -1; /* do not iterate */
                break;
            }
            break;
        default:
            return lcb_synchandler_return(instance, LCB_EINVAL);
        }

        do {
            vb = vbucket_get_vbucket_by_key(instance->vbucket_config,
                                            key, nkey);
            idx = vbucket_get_replica(instance->vbucket_config, vb, r0);
            if (idx < 0 || idx > (int)instance->nservers) {
                free(affected_servers);
                /* FIXME: when 'packet' patch will be applied, here
                 * should be rollback of all the previous commands
                 * queued */
                return lcb_synchandler_return(instance, LCB_NO_MATCHING_SERVER);
            }
            affected_servers[idx]++;
            server = instance->servers + idx;
            req.message.header.request.keylen = ntohs((lcb_uint16_t)nkey);
            req.message.header.request.vbucket = ntohs((lcb_uint16_t)vb);
            req.message.header.request.bodylen = ntohl((lcb_uint32_t)nkey);
            req.message.header.request.opaque = ++instance->seqno;

            lcb_server_start_packet_ex(server, &ct, req.bytes,
                                       sizeof(req.bytes));
            lcb_server_write_packet(server, key, nkey);
            lcb_server_end_packet(server);
            ++r0;
        } while (r0 < r1);
    }

    for (ii = 0; ii < instance->nservers; ++ii) {
        if (affected_servers[ii]) {
            server = instance->servers + ii;
            lcb_server_send_packets(server);
        }
    }

    free(affected_servers);
    return lcb_synchandler_return(instance, LCB_SUCCESS);
}
Beispiel #9
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;
}
Beispiel #10
0
LIBCOUCHBASE_API
lcb_error_t lcb_observe(lcb_t instance,
                        const void *command_cookie,
                        lcb_size_t num,
                        const lcb_observe_cmd_t *const *items)
{
    int vbid, idx, jj;
    lcb_size_t ii;
    lcb_uint32_t opaque;
    struct observe_st *requests;

    /* we need a vbucket config before we can start getting data.. */
    if (instance->vbucket_config == NULL) {
        switch (instance->type) {
        case LCB_TYPE_CLUSTER:
            return lcb_synchandler_return(instance, LCB_EBADHANDLE);
        case LCB_TYPE_BUCKET:
        default:
            return lcb_synchandler_return(instance, LCB_CLIENT_ETMPFAIL);
        }
    }

    if (instance->dist_type != VBUCKET_DISTRIBUTION_VBUCKET) {
        return lcb_synchandler_return(instance, LCB_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; ++ii) {
        const void *key = items[ii]->v.v0.key;
        lcb_size_t nkey = items[ii]->v.v0.nkey;
        const void *hashkey = items[ii]->v.v0.hashkey;
        lcb_size_t nhashkey = items[ii]->v.v0.nhashkey;

        if (nhashkey == 0) {
            hashkey = key;
            nhashkey = nkey;
        }

        vbid = vbucket_get_vbucket_by_key(instance->vbucket_config, hashkey,
                                          nhashkey);
        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 lcb_synchandler_return(instance, LCB_NETWORK_ERROR);
                } else {
                    continue;
                }
            }
            rr = requests + idx;
            if (!rr->allocated) {
                if (!init_request(rr)) {
                    destroy_requests(requests, instance->nservers);
                    return lcb_synchandler_return(instance, LCB_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;
            }

            {
                lcb_uint16_t vb = htons((lcb_uint16_t)vbid);
                lcb_uint16_t len = htons((lcb_uint16_t)nkey);
                ringbuffer_ensure_capacity(&rr->body, sizeof(vb) + sizeof(len) + nkey);
                rr->nbody += ringbuffer_write(&rr->body, &vb, sizeof(vb));
                rr->nbody += ringbuffer_write(&rr->body, &len, sizeof(len));
                rr->nbody += ringbuffer_write(&rr->body, key, nkey);
            }
        }
    }

    for (ii = 0; ii < instance->nservers; ++ii) {
        struct observe_st *rr = requests + ii;
        lcb_server_t *server = instance->servers + ii;

        if (rr->allocated) {
            char *tmp;
            rr->req.message.header.request.bodylen = ntohl((lcb_uint32_t)rr->nbody);
            lcb_server_start_packet(server, command_cookie, rr->req.bytes, sizeof(rr->req.bytes));
            if (ringbuffer_is_continous(&rr->body, RINGBUFFER_READ, rr->nbody)) {
                tmp = ringbuffer_get_read_head(&rr->body);
                TRACE_OBSERVE_BEGIN(&rr->req, server->authority, tmp, rr->nbody);
                lcb_server_write_packet(server, tmp, rr->nbody);
            } else {
                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 lcb_synchandler_return(instance, LCB_CLIENT_ENOMEM);
                } else {
                    ringbuffer_read(&rr->body, tmp, rr->nbody);
                    TRACE_OBSERVE_BEGIN(&rr->req, server->authority, tmp, rr->nbody);
                    lcb_server_write_packet(server, tmp, rr->nbody);
                }
            }
            lcb_server_end_packet(server);
            lcb_server_send_packets(server);
        }
    }

    destroy_requests(requests, instance->nservers);
    return lcb_synchandler_return(instance, LCB_SUCCESS);
}