/** * The is the proxy callback function. This will be passed to the old simple-scan * mechanism. When this gets called, it will create an as_val structure out of the * record and will call the callback that user supplied (folded into the udata structure) */ static int simplescan_cb(char *ns, cf_digest *keyd, char *set, cl_object *key, int result, uint32_t generation, uint32_t record_void_time, cl_bin *bins, uint16_t n_bins, void *udata) { scan_bridge * bridge = (scan_bridge *) udata; // Fill the bin data as_record _rec, * rec = &_rec; as_record_inita(rec, n_bins); clbins_to_asrecord(bins, (uint32_t)n_bins, rec); // Fill the metadata askey_from_clkey(&rec->key, ns, set, key); memcpy(rec->key.digest.value, keyd, sizeof(cf_digest)); rec->key.digest.init = true; rec->gen = generation; rec->ttl = record_void_time; // Call the callback that user wanted to callback bool rv = bridge->callback((as_val *) rec, bridge->udata); // The responsibility to free the bins is on the called callback function // In scan case, only LIST & MAP will have an active free citrusleaf_bins_free(bins, (int)n_bins); // release the record as_record_destroy(rec); return rv ? 0 : 1; }
bool scan_response_destroy(as_rec *rec) { cl_scan_response_rec * r = (cl_scan_response_rec *) rec; if (!r) return false; citrusleaf_bins_free(r->bins, r->n_bins); // if (r->bins) free(r->bins); if (r->ns) free(r->ns); if (r->set) free(r->set); if (r->ismalloc) free(r); rec->data = NULL; return true; }
/** * Store a record in the cluster. Note that the TTL (time to live) value * is specified inside of the rec (as_record) object. * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param rec The record containing the data to be written. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_put( aerospike * as, as_error * err, const as_policy_write * policy, const as_key * key, as_record * rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_write p; as_policy_write_resolve(&p, &as->config.policies, policy); cl_write_parameters wp; aspolicywrite_to_clwriteparameters(&p, rec, &wp); int nvalues = rec->bins.size; cl_bin * values = (cl_bin *) alloca(sizeof(cl_bin) * nvalues); asrecord_to_clbins(rec, values, nvalues); cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_put_digest_with_setname(as->cluster, key->ns, key->set, (cf_digest *) digest->value, values, nvalues, &wp); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_put(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, values, nvalues, &wp); break; } default: { // ERROR CASE break; } } // We are freeing the bins' objects, as opposed to bins themselves. citrusleaf_bins_free(values, nvalues); return as_error_fromrc(err,rc); }
/* * this is an actual instance of the scan, running on a scan thread * It reads on the node fd till it finds the last msg, in the meantime calling * task->callback on the returned data. The returned data is a bin of name SUCCESS/FAILURE * and the value of the bin is the return value from the udf. */ static int cl_scan_worker_do(cl_cluster_node * node, cl_scan_task * task) { uint8_t rd_stack_buf[STACK_BUF_SZ] = {0}; uint8_t * rd_buf = rd_stack_buf; size_t rd_buf_sz = 0; int fd = cl_cluster_node_fd_get(node, false, task->asc->nbconnect); if ( fd == -1 ) { LOG("[ERROR] cl_scan_worker_do: cannot get fd for node %s ",node->name); return CITRUSLEAF_FAIL_CLIENT; } // send it to the cluster - non blocking socket, but we're blocking if (0 != cf_socket_write_forever(fd, (uint8_t *) task->scan_buf, (size_t) task->scan_sz)) { close(fd); return CITRUSLEAF_FAIL_CLIENT; } cl_proto proto; int rc = CITRUSLEAF_OK; bool done = false; do { // multiple CL proto per response // Now turn around and read a fine cl_proto - that's the first 8 bytes // that has types and lengths if ( (rc = cf_socket_read_forever(fd, (uint8_t *) &proto, sizeof(cl_proto) ) ) ) { LOG("[ERROR] cl_scan_worker_do: network error: errno %d fd %d node name %s\n", rc, fd, node->name); close(fd); return CITRUSLEAF_FAIL_CLIENT; } cl_proto_swap(&proto); if ( proto.version != CL_PROTO_VERSION) { LOG("[ERROR] cl_scan_worker_do: network error: received protocol message of wrong version %d from node %s\n", proto.version, node->name); close(fd); return CITRUSLEAF_FAIL_CLIENT; } if ( proto.type != CL_PROTO_TYPE_CL_MSG && proto.type != CL_PROTO_TYPE_CL_MSG_COMPRESSED ) { LOG("[ERROR] cl_scan_worker_do: network error: received incorrect message version %d from node %s \n",proto.type, node->name); close(fd); return CITRUSLEAF_FAIL_CLIENT; } // second read for the remainder of the message - expect this to cover // lots of data, many lines if there's no error rd_buf_sz = proto.sz; if (rd_buf_sz > 0) { if (rd_buf_sz > sizeof(rd_stack_buf)){ rd_buf = malloc(rd_buf_sz); } else { rd_buf = rd_stack_buf; } if (rd_buf == NULL) { close(fd); return CITRUSLEAF_FAIL_CLIENT; } if ( (rc = cf_socket_read_forever(fd, rd_buf, rd_buf_sz)) ) { LOG("[ERROR] cl_scan_worker_do: network error: errno %d fd %d node name %s\n", rc, fd, node->name); if ( rd_buf != rd_stack_buf ) free(rd_buf); close(fd); return CITRUSLEAF_FAIL_CLIENT; } } // process all the cl_msg in this proto uint8_t * buf = rd_buf; uint pos = 0; cl_bin stack_bins[STACK_BINS]; cl_bin * bins; while (pos < rd_buf_sz) { uint8_t * buf_start = buf; cl_msg * msg = (cl_msg *) buf; cl_msg_swap_header(msg); buf += sizeof(cl_msg); if ( msg->header_sz != sizeof(cl_msg) ) { LOG("[ERROR] cl_scan_worker_do: received cl msg of unexpected size: expecting %zd found %d, internal error\n", sizeof(cl_msg),msg->header_sz); close(fd); return CITRUSLEAF_FAIL_CLIENT; } // parse through the fields cf_digest keyd; char ns_ret[33] = {0}; char * set_ret = NULL; cl_msg_field * mf = (cl_msg_field *)buf; for (int i=0; i < msg->n_fields; i++) { cl_msg_swap_field(mf); if (mf->type == CL_MSG_FIELD_TYPE_KEY) { LOG("[ERROR] cl_scan_worker_do: read: found a key - unexpected\n"); } else if (mf->type == CL_MSG_FIELD_TYPE_DIGEST_RIPE) { memcpy(&keyd, mf->data, sizeof(cf_digest)); } else if (mf->type == CL_MSG_FIELD_TYPE_NAMESPACE) { memcpy(ns_ret, mf->data, cl_msg_field_get_value_sz(mf)); ns_ret[ cl_msg_field_get_value_sz(mf) ] = 0; } else if (mf->type == CL_MSG_FIELD_TYPE_SET) { uint32_t set_name_len = cl_msg_field_get_value_sz(mf); set_ret = (char *)malloc(set_name_len + 1); memcpy(set_ret, mf->data, set_name_len); set_ret[ set_name_len ] = '\0'; } mf = cl_msg_field_get_next(mf); } buf = (uint8_t *) mf; if (msg->n_ops > STACK_BINS) { bins = malloc(sizeof(cl_bin) * msg->n_ops); } else { bins = stack_bins; } if (bins == NULL) { if (set_ret) { free(set_ret); } close(fd); return CITRUSLEAF_FAIL_CLIENT; } // parse through the bins/ops cl_msg_op * op = (cl_msg_op *) buf; for (int i=0;i<msg->n_ops;i++) { cl_msg_swap_op(op); #ifdef DEBUG_VERBOSE LOG("[DEBUG] cl_scan_worker_do: op receive: %p size %d op %d ptype %d pversion %d namesz %d \n", op,op->op_sz, op->op, op->particle_type, op->version, op->name_sz); #endif #ifdef DEBUG_VERBOSE dump_buf("individual op (host order)", (uint8_t *) op, op->op_sz + sizeof(uint32_t)); #endif cl_set_value_particular(op, &bins[i]); op = cl_msg_op_get_next(op); } buf = (uint8_t *) op; if (msg->result_code != CL_RESULT_OK) { rc = (int) msg->result_code; done = true; if (rc == CITRUSLEAF_FAIL_SCAN_ABORT) { LOG("[INFO] cl_scan_worker_do: Scan successfully aborted at node [%s]\n", node->name); } } else if (msg->info3 & CL_MSG_INFO3_LAST) { if ( cf_debug_enabled() ) { LOG("[INFO] cl_scan_worker_do: Received final message from node [%s], scan complete\n", node->name); } done = true; } else if ((msg->n_ops || (msg->info1 & CL_MSG_INFO1_NOBINDATA))) { cl_scan_response_rec rec; cl_scan_response_rec *recp = &rec; recp->ns = strdup(ns_ret); recp->keyd = keyd; recp->set = set_ret; recp->generation = msg->generation; recp->record_ttl = msg->record_ttl; recp->bins = bins; recp->n_bins = msg->n_ops; recp->ismalloc = false; as_rec r; as_rec *rp = &r; rp = as_rec_init(rp, recp, &scan_response_hooks); as_val * v = as_rec_get(rp, "SUCCESS"); if ( v != NULL && task->callback) { // Got a non null value for the resposne bin, // call callback on it and destroy the record task->callback(v, task->udata); as_rec_destroy(rp); } rc = CITRUSLEAF_OK; } // if done free it if (done) { citrusleaf_bins_free(bins, msg->n_ops); if (bins != stack_bins) { free(bins); bins = 0; } if (set_ret) { free(set_ret); set_ret = NULL; } } // don't have to free object internals. They point into the read buffer, where // a pointer is required pos += buf - buf_start; } if (rd_buf && (rd_buf != rd_stack_buf)) { free(rd_buf); rd_buf = 0; } } while ( done == false ); cl_cluster_node_fd_put(node, fd, false); #ifdef DEBUG_VERBOSE LOG("[DEBUG] cl_scan_worker_do: exited loop: rc %d\n", rc ); #endif return rc; }
int do_example(config *c) { int rv; // Put some test values cl_object o_key; citrusleaf_object_init_str(&o_key, "example_key"); cl_bin values[2]; strcpy(values[0].bin_name, "test_bin_one"); citrusleaf_object_init_str(&values[0].object, "example_value_one"); strcpy(values[1].bin_name, "test_bin_two"); citrusleaf_object_init_int(&values[1].object, 0xDEADBEEF); // set a non-default write parameter cl_write_parameters cl_wp; cl_write_parameters_set_default(&cl_wp); cl_wp.timeout_ms = 1000; if (0 != (rv = citrusleaf_put(c->asc, c->ns, c->set, &o_key, values, 2, &cl_wp))) { fprintf(stderr, "citrusleaf put failed: error %d\n",rv); return(-1); } fprintf(stderr, "citrusleaf put succeeded\n"); // Get all the values in this key (enjoy the fine c99 standard) cl_bin *cl_v = 0; uint32_t generation; int cl_v_len; if (0 != (rv = citrusleaf_get_all(c->asc, c->ns, c->set, &o_key, &cl_v, &cl_v_len, c->timeout_ms, &generation))) { fprintf(stderr, "get after put failed, but there should be a key here - %d\n",rv); if (cl_v) free(cl_v); return(-1); } fprintf(stderr, "get all returned %d bins\n",cl_v_len); for (int i=0;i<cl_v_len;i++) { fprintf(stderr, "%d: bin %s ",i,cl_v[i].bin_name); switch (cl_v[i].object.type) { case CL_STR: fprintf(stderr, "type string: value %s\n", cl_v[i].object.u.str); break; case CL_INT: fprintf(stderr, "type int: value %"PRId64"\n",cl_v[i].object.u.i64); break; default: fprintf(stderr, "type unknown! (%d)\n",(int)cl_v[i].object.type); break; } // could have done this -- but let's free the objects in the bins later // citrusleaf_object_free(&cl_v[i].object); } if (cl_v) { citrusleaf_bins_free(cl_v, cl_v_len); free(cl_v); // only one free for all bins } fprintf(stderr,"citrusleaf getall succeeded\n"); // Delete the key you just set if (0 != (rv = citrusleaf_delete(c->asc, c->ns, c->set, &o_key, 0/*default write params*/))) { fprintf(stderr, "citrusleaf delete failed: error %d\n",rv); return(-1); } fprintf(stderr, "citrusleaf delete succeeded\n"); return(0); }
/** * Look up a record by key, then return all bins. * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param rec The record to be populated with the data from request. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_get( aerospike * as, as_error * err, const as_policy_read * policy, const as_key * key, as_record ** rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_read p; as_policy_read_resolve(&p, &as->config.policies, policy); uint32_t timeout = p.timeout == UINT32_MAX ? 0 : p.timeout; uint32_t gen = 0; uint32_t ttl = 0; int nvalues = 0; cl_bin * values = NULL; cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_get_all_digest_getsetname(as->cluster, key->ns, (cf_digest *) digest->value, &values, &nvalues, timeout, &gen, NULL, &ttl); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_get_all(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, &values, &nvalues, timeout, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( rc == CITRUSLEAF_OK && rec != NULL ) { as_record * r = *rec; if ( r == NULL ) { r = as_record_new(0); } if ( r->bins.entries == NULL ) { r->bins.capacity = nvalues; r->bins.size = 0; r->bins.entries = malloc(sizeof(as_bin) * nvalues); r->bins._free = true; } clbins_to_asrecord(values, nvalues, r); r->gen = (uint16_t) gen; r->ttl = ttl; *rec = r; } if ( values != NULL ) { // We are freeing the bins' objects, as opposed to bins themselves. citrusleaf_bins_free(values, nvalues); free(values); } return as_error_fromrc(err,rc); }
/** * Lookup a record by key, then apply the UDF. * * ~~~~~~~~~~{.c} * as_key key; * as_key_init(&key, "ns", "set", "key"); * * as_arraylist args; * as_arraylist_init(&args, 2, 0); * as_arraylist_append_int64(&args, 1); * as_arraylist_append_int64(&args, 2); * * as_val * res = NULL; * * if ( aerospike_key_apply(&as, &err, NULL, &key, "math", "add", &args, &res) != AEROSPIKE_OK ) { * fprintf(stderr, "error(%d) %s at [%s:%d]", err.code, err.message, err.file, err.line); * } * else { * as_val_destroy(res); * } * * as_arraylist_destroy(&args); * ~~~~~~~~~~ * * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param module The module containing the function to execute. * @param function The function to execute. * @param arglist The arguments for the function. * @param result The return value from the function. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_apply( aerospike * as, as_error * err, const as_policy_apply * policy, const as_key * key, const char * module, const char * function, as_list * arglist, as_val ** result) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_apply p; as_policy_apply_resolve(&p, &as->config.policies, policy); cl_write_parameters wp; cl_write_parameters_set_default(&wp); wp.timeout_ms = p.timeout == UINT32_MAX ? 0 : p.timeout; cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_serializer ser; as_msgpack_init(&ser); as_string file; as_string_init(&file, (char *) module, true /*ismalloc*/); as_string func; as_string_init(&func, (char *) function, true /*ismalloc*/); as_buffer args; as_buffer_init(&args); as_serializer_serialize(&ser, (as_val *) arglist, &args); as_call call = { .file = &file, .func = &func, .args = &args }; uint64_t trid = 0; cl_bin * bins = 0; int n_bins = 0; cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = do_the_full_monte( as->cluster, 0, CL_MSG_INFO2_WRITE, 0, key->ns, key->set, 0, (cf_digest *) digest->value, &bins, CL_OP_WRITE, 0, &n_bins, NULL, &wp, &trid, NULL, &call, NULL ); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = do_the_full_monte( as->cluster, 0, CL_MSG_INFO2_WRITE, 0, key->ns, key->set, &okey, (cf_digest*)digest->value, &bins, CL_OP_WRITE, 0, &n_bins, NULL, &wp, &trid, NULL, &call, NULL ); break; } default: { // ERROR CASE break; } } as_buffer_destroy(&args); if (! (rc == CITRUSLEAF_OK || rc == CITRUSLEAF_FAIL_UDF_BAD_RESPONSE)) { as_error_fromrc(err, rc); } else { // Begin processing the data returned from the server, // IFF `result` argument is not NULL. // The reason is if `result` is NULL, then it implies the end user // does not care about the data returned from the server. if ( n_bins == 1 ) { cl_bin * bin = &bins[0]; if ( strcmp(bin->bin_name,"SUCCESS") == 0 ) { if ( result ) { as_val * val = NULL; clbin_to_asval(bin, &ser, &val); *result = val; } } else if ( strcmp(bin->bin_name,"FAILURE") == 0 ) { as_val * val = NULL; clbin_to_asval(bin, &ser, &val); if ( val->type == AS_STRING ) { as_string * s = as_string_fromval(val); as_error_update(err, AEROSPIKE_ERR_UDF, as_string_tostring(s)); } else { as_error_update(err, AEROSPIKE_ERR_SERVER, "unexpected failure bin type"); } as_val_destroy(val); } else { as_error_update(err, AEROSPIKE_ERR_SERVER, "unexpected bin name"); } } else { as_error_update(err, AEROSPIKE_ERR_SERVER, "unexpected number of bins"); } } if ( bins ) { citrusleaf_bins_free(bins, n_bins); free(bins); } as_serializer_destroy(&ser); return err->code; }
/** * Check if a record exists in the cluster via its key. * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param record The record to populated with metadata if record exists, otherwise NULL * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_exists( aerospike * as, as_error * err, const as_policy_read * policy, const as_key * key, as_record ** rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_read p; as_policy_read_resolve(&p, &as->config.policies, policy); uint32_t timeout = p.timeout == UINT32_MAX ? 0 : p.timeout; uint32_t gen = 0; uint32_t ttl = 0; // TODO - a version of 'exists' that returns all metadata int nvalues = 0; cl_bin * values = NULL; cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_exists_digest(as->cluster, key->ns, (cf_digest *) digest->value, values, nvalues, timeout, &gen, &ttl); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_exists_key(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, values, nvalues, timeout, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( values != NULL ) { // We are freeing the bins' objects, as opposed to bins themselves. citrusleaf_bins_free(values, nvalues); free(values); } switch(rc) { case CITRUSLEAF_OK: { as_record * r = *rec; if ( r == NULL ) { r = as_record_new(0); } r->gen = (uint16_t) gen; r->ttl = ttl; *rec = r; break; } default: *rec = NULL; break; } return as_error_fromrc(err,rc); }
/** * Lookup a record by key, then return specified bins. * * @param as The aerospike instance to use for this operation. * @param err The as_error to be populated if an error occurs. * @param policy The policy to use for this operation. If NULL, then the default policy will be used. * @param key The key of the record. * @param bins The bins to select. A NULL terminated array of NULL terminated strings. * @param rec The record to be populated with the data from request. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_select( aerospike * as, as_error * err, const as_policy_read * policy, const as_key * key, const char * bins[], as_record ** rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_read p; as_policy_read_resolve(&p, &as->config.policies, policy); uint32_t timeout = p.timeout == UINT32_MAX ? 0 : p.timeout; uint32_t gen = 0; uint32_t ttl = 0; int nvalues = 0; cl_bin * values = NULL; for (nvalues = 0; bins[nvalues] != NULL && bins[nvalues][0] != '\0'; nvalues++) ; values = (cl_bin *) alloca(sizeof(cl_bin) * nvalues); for ( int i = 0; i < nvalues; i++ ) { if ( strlen(bins[i]) > AS_BIN_NAME_MAX_LEN ) { return as_error_update(err, AEROSPIKE_ERR_PARAM, "bin name too long: %s", bins[i]); } strcpy(values[i].bin_name, bins[i]); citrusleaf_object_init(&values[i].object); } cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_get_digest(as->cluster, key->ns, (cf_digest *) digest->value, values, nvalues, timeout, &gen, &ttl); break; } case AS_POLICY_KEY_SEND: { cl_object okey; asval_to_clobject((as_val *) key->valuep, &okey); as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_get(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, values, nvalues, timeout, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( rc == CITRUSLEAF_OK && rec != NULL ) { as_record * r = *rec; if ( r == NULL ) { r = as_record_new(0); } if ( r->bins.entries == NULL ) { r->bins.capacity = nvalues; r->bins.size = 0; r->bins.entries = malloc(sizeof(as_bin) * nvalues); r->bins._free = true; } clbins_to_asrecord(values, nvalues, r); r->gen = (uint16_t) gen; r->ttl = ttl; *rec = r; } if ( values != NULL ) { // We are freeing the bins' objects, as opposed to bins themselves. citrusleaf_bins_free(values, nvalues); } return as_error_fromrc(err,rc); }