/** * 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); }
/* * call-seq: * new(namespace, set, value) -> AerospikeNative::Key * new(namespace, set, value, digest) -> AerospikeNative::Key * * initialize new key */ VALUE key_initialize(int argc, VALUE* vArgs, VALUE vSelf) { VALUE vNamespace, vSet, vValue, vDigest = Qnil; as_key *ptr; as_digest* digest = NULL; if (argc > 4 || argc < 3) { // there should only be 3 or 4 arguments rb_raise(rb_eArgError, "wrong number of arguments (%d for 3..4)", argc); } vNamespace = vArgs[0]; Check_Type(vNamespace, T_STRING); vSet = vArgs[1]; Check_Type(vSet, T_STRING); vValue = vArgs[2]; if (argc == 4) { vDigest = vArgs[3]; } Data_Get_Struct(vSelf, as_key, ptr); if(TYPE(vValue) != T_NIL) { switch(TYPE(vValue)) { case T_FIXNUM: as_key_init_int64(ptr, StringValueCStr( vNamespace ), StringValueCStr( vSet ), FIX2LONG( vValue )); break; case T_STRING: as_key_init_str(ptr, StringValueCStr( vNamespace ), StringValueCStr( vSet ), StringValueCStr( vValue )); break; default: { VALUE vBytes = rb_funcall(vValue, rb_intern("to_msgpack"), 0); as_key_init_raw(ptr, StringValueCStr( vNamespace ), StringValueCStr( vSet ), StringValuePtr(vBytes), RSTRING_LEN(vBytes)); } } } else { Check_Type(vValue, T_NIL); Check_Type(vDigest, T_STRING); as_key_init_digest(ptr, StringValueCStr( vNamespace ), StringValueCStr( vSet ), StringValuePtr( vDigest )); } if (digest == NULL) { digest = as_key_digest(ptr); } rb_iv_set(vSelf, "@namespace", vNamespace); rb_iv_set(vSelf, "@set", vSet); rb_iv_set(vSelf, "@value", vValue); rb_iv_set(vSelf, "@digest", rb_str_new( digest->value, AS_DIGEST_VALUE_SIZE)); return vSelf; }
/** * Remove a record from the cluster. * * ~~~~~~~~~~{.c} * as_key key; * as_key_init(&key, "ns", "set", "key"); * * if ( aerospike_key_remove(&as, &err, NULL, &key) != AEROSPIKE_OK ) { * fprintf(stderr, "error(%d) %s at [%s:%d]", err.code, err.message, err.file, err.line); * } * ~~~~~~~~~~ * * @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. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_remove( aerospike * as, as_error * err, const as_policy_remove * policy, const as_key * key) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_remove p; as_policy_remove_resolve(&p, &as->config.policies, policy); cl_write_parameters wp; aspolicyremove_to_clwriteparameters(&p, &wp); cl_rv rc = CITRUSLEAF_OK; switch ( p.key ) { case AS_POLICY_KEY_DIGEST: { as_digest * digest = as_key_digest((as_key *) key); rc = citrusleaf_delete_digest(as->cluster, key->ns, (cf_digest *) digest->value, &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_delete(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, &wp); break; } default: { // ERROR CASE break; } } return as_error_fromrc(err,rc); }
static as_status batch_read( aerospike * as, as_error * err, const as_policy_batch * policy, const as_batch * batch, aerospike_batch_read_callback callback, void * udata, bool get_bin_data ) { as_error_reset(err); // Lazily initialize batch machinery: cl_cluster_batch_init(as->cluster); uint32_t n = batch->keys.size; as_batch_read* results = (as_batch_read*)alloca(sizeof(as_batch_read) * n); if (! results) { return as_error_update(err, AEROSPIKE_ERR_CLIENT, "failed results array allocation"); } cf_digest* digests = (cf_digest*)alloca(sizeof(cf_digest) * n); if (! digests) { return as_error_update(err, AEROSPIKE_ERR_CLIENT, "failed digests array allocation"); } // Because we're wrapping the old functionality, we only support a batch // with all keys in the same namespace. char* ns = batch->keys.entries[0].ns; for (uint32_t i = 0; i < n; i++) { if (strcmp(ns, batch->keys.entries[i].ns) != 0) { // Don't need to destroy results' records since they won't have any // associated allocations yet. return as_error_update(err, AEROSPIKE_ERR_PARAM, "batch keys must all be in the same namespace"); } as_batch_read * p_r = &results[i]; p_r->result = -1; // TODO - make an 'undefined' error as_record_init(&p_r->record, 0); p_r->key = (const as_key*)as_batch_keyat(batch, i); memcpy(&digests[i], as_key_digest((as_key*)p_r->key)->value, AS_DIGEST_VALUE_SIZE); } batch_bridge bridge; bridge.as = as; bridge.results = results; bridge.n = n; cl_rv rc = citrusleaf_batch_read(as->cluster, ns, digests, n, NULL, 0, get_bin_data, cl_batch_cb, &bridge); callback(results, n, udata); for (uint32_t i = 0; i < n; i++) { as_record_destroy(&results[i].record); } return as_error_fromrc(err, rc); }
/** * 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; }
/** * Lookup a record by key, then perform specified operations. * * ~~~~~~~~~~{.c} * as_key key; * as_key_init(&key, "ns", "set", "key"); * * as_operations ops; * as_operations_inita(&ops,2); * as_operations_append_int64(&ops, AS_OPERATOR_INCR, "bin1", 456); * as_operations_append_str(&ops, AS_OPERATOR_APPEND, "bin1", "def"); * * if ( aerospike_key_remove(&as, &err, NULL, &key, &ops) != AEROSPIKE_OK ) { * fprintf(stderr, "error(%d) %s at [%s:%d]", err.code, err.message, err.file, err.line); * } * ~~~~~~~~~~ * * @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 ops The operations to perform on the record. * @param rec The record to be populated with the data from AS_OPERATOR_READ operations. * * @return AEROSPIKE_OK if successful. Otherwise an error. */ as_status aerospike_key_operate( aerospike * as, as_error * err, const as_policy_operate * policy, const as_key * key, const as_operations * ops, as_record ** rec) { // we want to reset the error so, we have a clean state as_error_reset(err); // resolve policies as_policy_operate p; as_policy_operate_resolve(&p, &as->config.policies, policy); cl_write_parameters wp; aspolicyoperate_to_clwriteparameters(&p, ops, &wp); uint32_t gen = 0; uint32_t ttl = 0; int n_operations = ops->binops.size; cl_operation * operations = (cl_operation *) alloca(sizeof(cl_operation) * n_operations); int n_read_ops = 0; as_bin_name * read_op_bins = alloca(sizeof(as_bin_name) * n_operations); for(int i=0; i<n_operations; i++) { cl_operation * clop = &operations[i]; as_binop * op = &ops->binops.entries[i]; // Length check already done on as_bin name length. For performance we // we'll leave out this down-size check since this is a temporary shim // and we know the CL and AS limits are the same... // if ( strlen(op->bin.name) > CL_BINNAME_SIZE - 1 ) { // return as_error_update(err, AEROSPIKE_ERR_PARAM, "bin name too long: %s", op->bin.name); // } strcpy(clop->bin.bin_name, op->bin.name); clop->op = (cl_operator)op->op; // Collect bin names that are read. if (op->op == AS_OPERATOR_READ) { strcpy(read_op_bins[n_read_ops++], op->bin.name); } asbinvalue_to_clobject(op->bin.valuep, &clop->bin.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_operate_digest(as->cluster, key->ns, key->set, (cf_digest *) digest->value, operations, n_operations, &wp, &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_operate(as->cluster, key->ns, key->set, &okey, (cf_digest*)digest->value, operations, n_operations, &wp, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( n_read_ops != 0 && 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 = n_read_ops; r->bins.size = 0; r->bins.entries = malloc(sizeof(as_bin) * n_read_ops); r->bins._free = true; } r->gen = (uint16_t) gen; r->ttl = ttl; // This works around an existing client bug where the data returned for // a read operation is stored in the first bin struct with that bin // name, not necessarily the bin struct corresponding to the read. for (int i = 0; i < n_read_ops; i++) { for (int j = 0; j < n_operations; j++) { if (strcmp(read_op_bins[i], operations[j].bin.bin_name) == 0) { clbin_to_asrecord(&operations[j].bin, r); citrusleaf_object_free(&operations[j].bin.object); break; } } } *rec = r; } return as_error_fromrc(err,rc); }
/** * 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); }
/** * 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 exists The variable to populate with `true` if the record exists, otherwise `false`. * * @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, bool * exists) { // 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); rc = citrusleaf_exists_key(as->cluster, key->ns, key->set, &okey, values, nvalues, timeout, &gen, &ttl); break; } default: { // ERROR CASE break; } } if ( values != NULL ) { free(values); } switch(rc) { case CITRUSLEAF_OK: if ( exists ) { *exists = true; } return AEROSPIKE_OK; case CITRUSLEAF_FAIL_NOTFOUND: if ( exists ) { *exists = false; } return AEROSPIKE_OK; default: if ( exists ) { *exists = false; } return as_error_fromrc(err,rc); } }