/* * Internal function: udf_aerospike_execute_updates * * Parameters: * rec - udf record to be updated * * Return values * 0 on success * -1 on failure * * Description: * Execute set of udf_record updates. If these updates are successfully * applied atomically, the storage record is closed (committed to the disk) * and reopened. The cache is freed up at the end. * * Callers: * udf_aerospike_rec_create, interface func - aerospike:create(r) * udf_aerospike_rec_update, interface func - aerospike:update(r) * udf_aerospike__execute_updates is the key function which is executed in these * functions. The return value is directly passed on to the lua. */ int udf_aerospike__execute_updates(udf_record * urecord) { int rc = 0; as_storage_rd *rd = urecord->rd; as_index_ref * r_ref = urecord->r_ref; if ( urecord->nupdates == 0 ) { cf_detail(AS_UDF, "No Update when execute update is called"); return 0; } // fail updates in case update is not allowed. Queries and scans do not // not allow updates. Updates will never be true .. just being paranoid if (!(urecord->flag & UDF_RECORD_FLAG_ALLOW_UPDATES)) { cf_warning(AS_UDF, "Udf: execute updates: allow updates false; FAIL"); return -1; } // Commit semantics is either all the update make it or none of it rc = udf_aerospike__apply_update_atomic(urecord); // allocate down if bins are deleted / not in use if (rd->ns && rd->ns->storage_data_in_memory && ! rd->ns->single_bin) { int32_t delta_bins = (int32_t)as_bin_inuse_count(rd) - (int32_t)rd->n_bins; if (delta_bins) { as_bin_allocate_bin_space(r_ref->r, rd, delta_bins); } } return rc; }
// Does not check bin name length. // Checks bin name quota - use appropriately. as_bin * as_bin_get_or_create(as_storage_rd *rd, const char *name) { if (rd->ns->single_bin) { if (! as_bin_inuse_has(rd)) { as_bin_init_nameless(rd->bins); } return rd->bins; } uint32_t id = (uint32_t)-1; uint16_t i; as_bin *b; if (cf_vmapx_get_index(rd->ns->p_bin_name_vmap, name, &id) == CF_VMAPX_OK) { for (i = 0; i < rd->n_bins; i++) { b = &rd->bins[i]; if (! as_bin_inuse(b)) { break; } if ((uint32_t)b->id == id) { return b; } } } else { if (cf_vmapx_count(rd->ns->p_bin_name_vmap) >= BIN_NAMES_QUOTA) { cf_warning(AS_BIN, "{%s} bin-name quota full - can't add new bin-name %s", rd->ns->name, name); return NULL; } i = as_bin_inuse_count(rd); } if (i >= rd->n_bins) { cf_crash(AS_BIN, "ran out of allocated bins in rd"); } b = &rd->bins[i]; if (id == (uint32_t)-1) { as_bin_init(rd->ns, b, name); } else { as_bin_init_nameless(b); b->id = (uint16_t)id; } return b; }
int as_msg_make_response_bufbuilder(as_record *r, as_storage_rd *rd, cf_buf_builder **bb_r, bool nobindata, char *nsname, bool use_sets, bool include_key, cf_vector *binlist) { // Sanity checks. Either rd should be there or nobindata and nsname should be present. if (!(rd || (nobindata && nsname))) { cf_detail(AS_PROTO, "Neither storage record nor nobindata is set. Skipping the record."); return 0; } // figure out the size of the entire buffer int set_name_len = 0; const char *set_name = NULL; int ns_len = rd ? strlen(rd->ns->name) : strlen(nsname); if (use_sets && as_index_get_set_id(r) != INVALID_SET_ID) { as_namespace *ns = NULL; if (rd) { ns = rd->ns; } else if (nsname) { ns = as_namespace_get_byname(nsname); } if (!ns) { cf_info(AS_PROTO, "Cannot get namespace, needed to get set information. Skipping record."); return -1; } set_name = as_index_get_set_name(r, ns); if (set_name) { set_name_len = strlen(set_name); } } uint8_t* key = NULL; uint32_t key_size = 0; if (include_key && as_index_is_flag_set(r, AS_INDEX_FLAG_KEY_STORED)) { if (! as_storage_record_get_key(rd)) { cf_info(AS_PROTO, "can't get key - skipping record"); return -1; } key = rd->key; key_size = rd->key_size; } uint16_t n_fields = 2; int msg_sz = sizeof(as_msg); msg_sz += sizeof(as_msg_field) + sizeof(cf_digest); msg_sz += sizeof(as_msg_field) + ns_len; if (set_name) { n_fields++; msg_sz += sizeof(as_msg_field) + set_name_len; } if (key) { n_fields++; msg_sz += sizeof(as_msg_field) + key_size; } int list_bins = 0; int in_use_bins = 0; if (rd) { in_use_bins = as_bin_inuse_count(rd); } if (nobindata == false) { if(binlist) { int binlist_sz = cf_vector_size(binlist); for(uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); cf_debug(AS_PROTO, " Binname projected inside is |%s| \n", binname); as_bin *p_bin = as_bin_get (rd, (uint8_t*)binname, strlen(binname)); if (!p_bin) { cf_debug(AS_PROTO, "To be projected bin |%s| not found \n", binname); continue; } cf_debug(AS_PROTO, "Adding bin |%s| to projected bins |%s| \n", binname); list_bins++; msg_sz += sizeof(as_msg_op); msg_sz += rd->ns->single_bin ? 0 : strlen(binname); uint32_t psz; if (as_bin_is_hidden(p_bin)) { psz = 0; } else { as_particle_tobuf(p_bin, 0, &psz); // get size } msg_sz += psz; } } else { msg_sz += sizeof(as_msg_op) * in_use_bins; // the bin headers for (uint16_t i = 0; i < in_use_bins; i++) { as_bin *p_bin = &rd->bins[i]; msg_sz += rd->ns->single_bin ? 0 : strlen(as_bin_get_name_from_id(rd->ns, p_bin->id)); uint32_t psz; if (as_bin_is_hidden(p_bin)) { psz = 0; } else { as_particle_tobuf(p_bin, 0, &psz); // get size } msg_sz += psz; } } } uint8_t *b; cf_buf_builder_reserve(bb_r, msg_sz, &b); // set up the header uint8_t *buf = b; as_msg *msgp = (as_msg *) buf; msgp->header_sz = sizeof(as_msg); msgp->info1 = (nobindata ? AS_MSG_INFO1_GET_NOBINDATA : 0); msgp->info2 = 0; msgp->info3 = 0; msgp->unused = 0; msgp->result_code = 0; msgp->generation = r->generation; msgp->record_ttl = r->void_time; msgp->transaction_ttl = 0; msgp->n_fields = n_fields; if (rd) { if (binlist) msgp->n_ops = list_bins; else msgp->n_ops = in_use_bins; } else { msgp->n_ops = 0; } as_msg_swap_header(msgp); buf += sizeof(as_msg); as_msg_field *mf = (as_msg_field *) buf; mf->field_sz = sizeof(cf_digest) + 1; mf->type = AS_MSG_FIELD_TYPE_DIGEST_RIPE; if (rd) { memcpy(mf->data, &rd->keyd, sizeof(cf_digest)); } else { memcpy(mf->data, &r->key, sizeof(cf_digest)); } as_msg_swap_field(mf); buf += sizeof(as_msg_field) + sizeof(cf_digest); mf = (as_msg_field *) buf; mf->field_sz = ns_len + 1; mf->type = AS_MSG_FIELD_TYPE_NAMESPACE; if (rd) { memcpy(mf->data, rd->ns->name, ns_len); } else { memcpy(mf->data, nsname, ns_len); } as_msg_swap_field(mf); buf += sizeof(as_msg_field) + ns_len; if (set_name) { mf = (as_msg_field *) buf; mf->field_sz = set_name_len + 1; mf->type = AS_MSG_FIELD_TYPE_SET; memcpy(mf->data, set_name, set_name_len); as_msg_swap_field(mf); buf += sizeof(as_msg_field) + set_name_len; } if (key) { mf = (as_msg_field *) buf; mf->field_sz = key_size + 1; mf->type = AS_MSG_FIELD_TYPE_KEY; memcpy(mf->data, key, key_size); as_msg_swap_field(mf); buf += sizeof(as_msg_field) + key_size; } if (nobindata) { goto Out; } if(binlist) { int binlist_sz = cf_vector_size(binlist); for(uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); cf_debug(AS_PROTO, " Binname projected inside is |%s| \n", binname); as_bin *p_bin = as_bin_get (rd, (uint8_t*)binname, strlen(binname)); if (!p_bin) // should it be checked before ??? continue; as_msg_op *op = (as_msg_op *)buf; buf += sizeof(as_msg_op); op->op = AS_MSG_OP_READ; op->name_sz = as_bin_memcpy_name(rd->ns, op->name, p_bin); buf += op->name_sz; // Since there are two variable bits, the size is everything after // the data bytes - and this is only the head, we're patching up // the rest in a minute. op->op_sz = 4 + op->name_sz; if (as_bin_inuse(p_bin)) { op->particle_type = as_particle_type_convert(as_bin_get_particle_type(p_bin)); op->version = as_bin_get_version(p_bin, rd->ns->single_bin); uint32_t psz = msg_sz - (buf - b); // size remaining in buffer, for safety if (as_bin_is_hidden(p_bin)) { op->particle_type = AS_PARTICLE_TYPE_NULL; psz = 0; } else { if (0 != as_particle_tobuf(p_bin, buf, &psz)) { cf_warning(AS_PROTO, "particle to buf: could not copy data!"); } } buf += psz; op->op_sz += psz; } else { cf_debug(AS_PROTO, "Whoops !! bin not in use"); op->particle_type = AS_PARTICLE_TYPE_NULL; } as_msg_swap_op(op); } } else { // over all bins, copy into the buffer for (uint16_t i = 0; i < in_use_bins; i++) { as_msg_op *op = (as_msg_op *)buf; buf += sizeof(as_msg_op); op->op = AS_MSG_OP_READ; op->name_sz = as_bin_memcpy_name(rd->ns, op->name, &rd->bins[i]); buf += op->name_sz; // Since there are two variable bits, the size is everything after // the data bytes - and this is only the head, we're patching up // the rest in a minute. op->op_sz = 4 + op->name_sz; if (as_bin_inuse(&rd->bins[i])) { op->particle_type = as_particle_type_convert(as_bin_get_particle_type(&rd->bins[i])); op->version = as_bin_get_version(&rd->bins[i], rd->ns->single_bin); uint32_t psz = msg_sz - (buf - b); // size remaining in buffer, for safety if (as_bin_is_hidden(&rd->bins[i])) { op->particle_type = AS_PARTICLE_TYPE_NULL; psz = 0; } else { if (0 != as_particle_tobuf(&rd->bins[i], buf, &psz)) { cf_warning(AS_PROTO, "particle to buf: could not copy data!"); } } buf += psz; op->op_sz += psz; } else { op->particle_type = AS_PARTICLE_TYPE_NULL; } as_msg_swap_op(op); } } Out: return(0); }
// NB: this uses the same logic as the bufbuild function // as_msg_make_response_bufbuilder() but does not build a buffer and simply // returns sizing information. This is required for query runtime memory // accounting. // returns -1 in case of error // otherwise returns resize value size_t as_msg_response_msgsize(as_record *r, as_storage_rd *rd, bool nobindata, char *nsname, bool use_sets, cf_vector *binlist) { // Sanity checks. Either rd should be there or nobindata and nsname should be present. if (!(rd || (nobindata && nsname))) { cf_detail(AS_PROTO, "Neither storage record nor nobindata is set. Skipping the record."); return -1; } // figure out the size of the entire buffer int set_name_len = 0; const char *set_name = NULL; int ns_len = rd ? strlen(rd->ns->name) : strlen(nsname); if (use_sets && as_index_get_set_id(r) != INVALID_SET_ID) { as_namespace *ns = NULL; if (rd) { ns = rd->ns; } else if (nsname) { ns = as_namespace_get_byname(nsname); } if (!ns) { cf_info(AS_PROTO, "Cannot get namespace, needed to get set information. Skipping record."); return -1; } set_name = as_index_get_set_name(r, ns); if (set_name) { set_name_len = strlen(set_name); } } int msg_sz = sizeof(as_msg); msg_sz += sizeof(as_msg_field) + sizeof(cf_digest); msg_sz += sizeof(as_msg_field) + ns_len; if (set_name) { msg_sz += sizeof(as_msg_field) + set_name_len; } int in_use_bins = as_bin_inuse_count(rd); int list_bins = 0; if (nobindata == false) { if(binlist) { int binlist_sz = cf_vector_size(binlist); for(uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); cf_debug(AS_PROTO, " Binname projected inside is |%s| \n", binname); as_bin *p_bin = as_bin_get (rd, (uint8_t*)binname, strlen(binname)); if (!p_bin) { cf_debug(AS_PROTO, "To be projected bin |%s| not found \n", binname); continue; } cf_debug(AS_PROTO, "Adding bin |%s| to projected bins |%s| \n", binname); list_bins++; msg_sz += sizeof(as_msg_op); msg_sz += rd->ns->single_bin ? 0 : strlen(binname); uint32_t psz; as_particle_tobuf(p_bin, 0, &psz); // get size msg_sz += psz; } } else { msg_sz += sizeof(as_msg_op) * in_use_bins; // the bin headers for (uint16_t i = 0; i < in_use_bins; i++) { as_bin *p_bin = &rd->bins[i]; msg_sz += rd->ns->single_bin ? 0 : strlen(as_bin_get_name_from_id(rd->ns, p_bin->id)); uint32_t psz; as_particle_tobuf(p_bin, 0, &psz); // get size msg_sz += psz; } } } return msg_sz; }
/* * Internal function: udf_aerospike__apply_update_atomic * * Parameters: * rec -- udf_record to be updated * * Return Values: * 0 success * -1 failure * * Description: * This function applies all the updates atomically. That is, * if one of the bin update/delete/create fails, the entire function * will fail. If the nth update fails, all the n-1 updates are rolled * back to their initial values * * Special Notes: * i. The basic checks of bin name being too long or if there is enough space * on the disk for the bin values is done before allocating space for any * of the bins. * * ii. If one of the updates to be rolled back is a bin creation, * udf_aerospike_delbin is called. This will not free up the bin metadata. * So there will be a small memory mismatch b/w replica (which did not get the * record at all and hence no memory is accounted) and the master will be seen. * To avoid such cases, we are doing checks upfront. * * Callers: * udf_aerospike__execute_updates * In this function, if udf_aerospike__apply_update_atomic fails, the record * is not committed to the storage. On success, record is closed which commits to * the storage and reopened for the next set of udf updates. * The return value from udf_aerospike__apply_update_atomic is passed on to the * callers of this function. */ int udf_aerospike__apply_update_atomic(udf_record *urecord) { int rc = 0; int failindex = 0; int new_bins = 0; // How many new bins have to be created in this update as_storage_rd * rd = urecord->rd; // This will iterate over all the updates and apply them to storage. // The items will remain, and be used as cache values. If an error // occurred during setbin(), we rollback all the operation which // is and return failure cf_detail(AS_UDF, "execute updates: %d updates", urecord->nupdates); // loop twice to make sure the updates are performed first so in case // something wrong it can be rolled back. The deletes will go through // successfully generally. // In first iteration, just calculate how many new bins need to be created for(int i = 0; i < urecord->nupdates; i++ ) { if ( urecord->updates[i].dirty ) { char * k = urecord->updates[i].name; if ( k != NULL ) { if ( !as_bin_get(rd, (uint8_t *)k, strlen(k)) ) { new_bins++; } } } } // Free bins - total bins not in use in the record // Delta bins - new bins that need to be created int free_bins = urecord->rd->n_bins - as_bin_inuse_count(urecord->rd); int delta_bins = new_bins - free_bins; cf_detail(AS_UDF, "Total bins %d, In use bins %d, Free bins %d , New bins %d, Delta bins %d", urecord->rd->n_bins, as_bin_inuse_count(urecord->rd), free_bins, new_bins, delta_bins); // Allocate space for all the new bins that need to be created beforehand if (delta_bins > 0 && rd->ns->storage_data_in_memory && ! rd->ns->single_bin) { as_bin_allocate_bin_space(urecord->r_ref->r, rd, delta_bins); } bool has_sindex = as_sindex_ns_has_sindex(rd->ns); if (has_sindex) { SINDEX_GRLOCK(); } // In second iteration apply updates. for(int i = 0; i < urecord->nupdates; i++ ) { if ( urecord->updates[i].dirty && rc == 0) { char * k = urecord->updates[i].name; as_val * v = urecord->updates[i].value; bool h = urecord->updates[i].ishidden; urecord->updates[i].oldvalue = NULL; urecord->updates[i].washidden = false; if ( k != NULL ) { if ( v == NULL || v->type == AS_NIL ) { // if the value is NIL, then do a delete cf_detail(AS_UDF, "execute update: position %d deletes bin %s", i, k); urecord->updates[i].oldvalue = udf_record_storage_get(urecord, k); urecord->updates[i].washidden = udf_record_bin_ishidden(urecord, k); // Only case delete fails if bin is not found that is // as good as delete. Ignore return code !! udf_aerospike_delbin(urecord, k); } else { // otherwise, it is a set cf_detail(AS_UDF, "execute update: position %d sets bin %s", i, k); urecord->updates[i].oldvalue = udf_record_storage_get(urecord, k); urecord->updates[i].washidden = udf_record_bin_ishidden(urecord, k); rc = udf_aerospike_setbin(urecord, k, v, h); if (rc) { failindex = i; goto Rollback; } } } } } if (has_sindex) { SINDEX_GUNLOCK(); } for(int i = 0; i < urecord->nupdates; i++ ) { if ((urecord->updates[i].dirty) && (urecord->updates[i].oldvalue)) { as_val_destroy(urecord->updates[i].oldvalue); cf_debug(AS_UDF, "REGULAR as_val_destroy()"); } } // Commit successful do miscellaneous task // Set updated flag to true urecord->flag |= UDF_RECORD_FLAG_HAS_UPDATES; // Set up record to be flushed to storage urecord->rd->write_to_device = true; // Before committing to storage set the rec_type_bits .. cf_detail(AS_RW, "TO INDEX Digest=%"PRIx64" bits %d %p", *(uint64_t *)&urecord->tr->keyd.digest[8], urecord->ldt_rectype_bits, urecord); as_index_set_flags(rd->r, urecord->ldt_rectype_bits); // Clean up cache and start from 0 update again. All the changes // made here will if flush from write buffer to storage goes // then will never be backed out. udf_record_cache_free(urecord); return rc; Rollback: cf_debug(AS_UDF, "Rollback Called: FailIndex(%d)", failindex); for(int i = 0; i <= failindex; i++) { if (urecord->updates[i].dirty) { char * k = urecord->updates[i].name; // Pick the oldvalue for rollback as_val * v = urecord->updates[i].oldvalue; bool h = urecord->updates[i].washidden; if ( k != NULL ) { if ( v == NULL || v->type == AS_NIL ) { // if the value is NIL, then do a delete cf_detail(AS_UDF, "execute rollback: position %d deletes bin %s", i, k); rc = udf_aerospike_delbin(urecord, k); } else { // otherwise, it is a set cf_detail(AS_UDF, "execute rollback: position %d sets bin %s", i, k); rc = udf_aerospike_setbin(urecord, k, v, h); if (rc) { cf_warning(AS_UDF, "Rollback failed .. not good ... !!"); } } } if (v) { as_val_destroy(v); cf_debug(AS_UDF, "ROLLBACK as_val_destroy()"); } } } if (has_sindex) { SINDEX_GUNLOCK(); } // Do not clean up the cache in case of failure return -1; }
// Does not check bin name length. // Checks bin name quota and bin-level policy - use appropriately. as_bin * as_bin_get_or_create_from_buf(as_storage_rd *rd, byte *name, size_t namesz, bool create_only, bool replace_only, int *p_result) { if (rd->ns->single_bin) { if (! as_bin_inuse_has(rd)) { as_bin_init_nameless(rd->bins); } // Ignored bin-level policy - single-bin needs only record-level policy. return rd->bins; } uint32_t id = (uint32_t)-1; uint16_t i; as_bin *b; if (cf_vmapx_get_index_w_len(rd->ns->p_bin_name_vmap, (const char *)name, namesz, &id) == CF_VMAPX_OK) { for (i = 0; i < rd->n_bins; i++) { b = &rd->bins[i]; if (! as_bin_inuse(b)) { break; } if ((uint32_t)b->id == id) { if (as_bin_is_hidden(b)) { cf_warning(AS_BIN, "cannot manipulate hidden bin directly"); *p_result = AS_PROTO_RESULT_FAIL_INCOMPATIBLE_TYPE; return NULL; } if (create_only) { *p_result = AS_PROTO_RESULT_FAIL_BIN_EXISTS; return NULL; } return b; } } } else { if (cf_vmapx_count(rd->ns->p_bin_name_vmap) >= BIN_NAMES_QUOTA) { char zname[namesz + 1]; memcpy(zname, name, namesz); zname[namesz] = 0; cf_warning(AS_BIN, "{%s} bin-name quota full - can't add new bin-name %s", rd->ns->name, zname); *p_result = AS_PROTO_RESULT_FAIL_BIN_NAME; return NULL; } i = as_bin_inuse_count(rd); } if (replace_only) { *p_result = AS_PROTO_RESULT_FAIL_BIN_NOT_FOUND; return NULL; } if (i >= rd->n_bins) { cf_crash(AS_BIN, "ran out of allocated bins in rd"); } b = &rd->bins[i]; if (id == (uint32_t)-1) { as_bin_init_w_len(rd->ns, b, name, namesz); } else { as_bin_init_nameless(b); b->id = (uint16_t)id; } return b; }
/* * Internal function: udf_aerospike__apply_update_atomic * * Parameters: * rec -- udf_record to be updated * * Return Values: * 0 success * -1 failure * * Description: * This function applies all the updates atomically. That is, * if one of the bin update/delete/create fails, the entire function * will fail. If the nth update fails, all the n-1 updates are rolled * back to their initial values * * Special Notes: * i. The basic checks of bin name being too long or if there is enough space * on the disk for the bin values is done before allocating space for any * of the bins. * * ii. If one of the updates to be rolled back is a bin creation, * udf_aerospike_delbin is called. This will not free up the bin metadata. * So there will be a small memory mismatch b/w replica (which did not get the * record at all and hence no memory is accounted) and the master will be seen. * To avoid such cases, we are doing checks upfront. * * Callers: * udf_aerospike__execute_updates * In this function, if udf_aerospike__apply_update_atomic fails, the record * is not committed to the storage. On success, record is closed which commits to * the storage and reopened for the next set of udf updates. * The return value from udf_aerospike__apply_update_atomic is passed on to the * callers of this function. */ int udf_aerospike__apply_update_atomic(udf_record *urecord) { int rc = 0; int failmax = 0; int new_bins = 0; // How many new bins have to be created in this update as_storage_rd * rd = urecord->rd; as_namespace * ns = rd->ns; bool has_sindex = as_sindex_ns_has_sindex(ns); bool is_record_dirty = false; bool is_record_flag_dirty = false; uint8_t old_index_flags = as_index_get_flags(rd->r); uint8_t new_index_flags = 0; // This will iterate over all the updates and apply them to storage. // The items will remain, and be used as cache values. If an error // occurred during setbin(), we rollback all the operation which // is and return failure cf_detail(AS_UDF, "execute updates: %d updates", urecord->nupdates); // loop twice to make sure the updates are performed first so in case // something wrong it can be rolled back. The deletes will go through // successfully generally. // In first iteration, just calculate how many new bins need to be created for(uint32_t i = 0; i < urecord->nupdates; i++ ) { if ( urecord->updates[i].dirty ) { char * k = urecord->updates[i].name; if ( k != NULL ) { if ( !as_bin_get(rd, k) ) { new_bins++; } } } } // Free bins - total bins not in use in the record // Delta bins - new bins that need to be created int inuse_bins = as_bin_inuse_count(rd); int free_bins = rd->n_bins - inuse_bins; int delta_bins = new_bins - free_bins; cf_detail(AS_UDF, "Total bins %d, In use bins %d, Free bins %d , New bins %d, Delta bins %d", rd->n_bins, as_bin_inuse_count(urecord->rd), free_bins, new_bins, delta_bins); // Check bin usage limit. if ((inuse_bins + new_bins > UDF_RECORD_BIN_ULIMIT) || (urecord->flag & UDF_RECORD_FLAG_TOO_MANY_BINS)) { cf_warning(AS_UDF, "bin limit of %d for UDF exceeded: %d bins in use, %d bins free, %s%d new bins needed", (int)UDF_RECORD_BIN_ULIMIT, inuse_bins, free_bins, (urecord->flag & UDF_RECORD_FLAG_TOO_MANY_BINS) ? ">" : "", new_bins); goto Rollback; } // Allocate space for all the new bins that need to be created beforehand if (delta_bins > 0 && rd->ns->storage_data_in_memory && ! rd->ns->single_bin) { as_bin_allocate_bin_space(urecord->r_ref->r, rd, delta_bins); } if (!rd->ns->storage_data_in_memory && !urecord->particle_data) { // 256 as upper bound on the LDT control bin, we may write version below // leave it at the end for its use urecord->particle_data = cf_malloc(rd->ns->storage_write_block_size + 256); urecord->cur_particle_data = urecord->particle_data; urecord->end_particle_data = urecord->particle_data + rd->ns->storage_write_block_size; } if (has_sindex) { SINDEX_GRLOCK(); } // In second iteration apply updates. for(uint32_t i = 0; i < urecord->nupdates; i++ ) { urecord->updates[i].oldvalue = NULL; urecord->updates[i].washidden = false; if ( urecord->updates[i].dirty && rc == 0) { char * k = urecord->updates[i].name; as_val * v = urecord->updates[i].value; bool h = urecord->updates[i].ishidden; if ( k != NULL ) { if ( v == NULL || v->type == AS_NIL ) { // if the value is NIL, then do a delete cf_detail(AS_UDF, "execute update: position %d deletes bin %s", i, k); urecord->updates[i].oldvalue = udf_record_storage_get(urecord, k); urecord->updates[i].washidden = udf_record_bin_ishidden(urecord, k); // Only case delete fails if bin is not found that is // as good as delete. Ignore return code !! udf_aerospike_delbin(urecord, k); if (urecord->dirty != NULL) { xdr_fill_dirty_bins(urecord->dirty); } } else { // otherwise, it is a set cf_detail(AS_UDF, "execute update: position %d sets bin %s", i, k); urecord->updates[i].oldvalue = udf_record_storage_get(urecord, k); urecord->updates[i].washidden = udf_record_bin_ishidden(urecord, k); rc = udf_aerospike_setbin(urecord, i, k, v, h); if (rc) { if (urecord->updates[i].oldvalue) { as_val_destroy(urecord->updates[i].oldvalue); urecord->updates[i].oldvalue = NULL; } failmax = i; goto Rollback; } if (urecord->dirty != NULL) { xdr_add_dirty_bin(ns, urecord->dirty, k, strlen(k)); } } } is_record_dirty = true; } } if (urecord->ldt_rectype_bit_update) { if (urecord->ldt_rectype_bit_update < 0) { // ldt_rectype_bit_update is negative in case we want to reset the bits uint8_t rectype_bits = urecord->ldt_rectype_bit_update * -1; new_index_flags = old_index_flags & ~rectype_bits; } else { new_index_flags = old_index_flags | urecord->ldt_rectype_bit_update; } if (new_index_flags != old_index_flags) { as_index_clear_flags(rd->r, old_index_flags); as_index_set_flags(rd->r, new_index_flags); is_record_flag_dirty = true; cf_detail_digest(AS_RW, &urecord->tr->keyd, "Setting index flags from %d to %d new flag %d", old_index_flags, new_index_flags, as_index_get_flags(rd->r)); } } { // This is _NOT_ for writing to the storage but for simply performing sizing // calculation. If we know the upper bounds of size of rec_props.. we could // avoid this work and check with that much correction ... // // See // - udf_rw_post_processing for building rec_props for replication // - udf_record_close for building rec_props for writing it to storage size_t rec_props_data_size = as_storage_record_rec_props_size(rd); uint8_t rec_props_data[rec_props_data_size]; if (rec_props_data_size > 0) { as_storage_record_set_rec_props(rd, rec_props_data); } // Version is set in the end after record size check. Setting version won't change the size of // the record. And if it were before size check then this setting of version as well needs to // be backed out. // TODO: Add backout logic would work till very first create call of LDT end up crossing over // record boundary if (rd->ns->ldt_enabled && as_ldt_record_is_parent(rd->r)) { int rv = as_ldt_parent_storage_set_version(rd, urecord->lrecord->version, urecord->end_particle_data, __FILE__, __LINE__); if (rv < 0) { cf_warning(AS_LDT, "udf_aerospike__apply_update_atomic: Internal Error " " [Failed to set the version on storage rv=%d]... Fail",rv); goto Rollback; } // TODO - if size check below fails, won't write to device - // different behavior than write_to_device flag - OK? is_record_dirty = true; } if (! as_storage_record_size_and_check(rd)) { cf_warning(AS_UDF, "record failed storage size check, will not be updated"); failmax = (int)urecord->nupdates; goto Rollback; } if (cf_atomic32_get(rd->ns->stop_writes) == 1) { cf_warning(AS_UDF, "UDF failed by stop-writes, record will not be updated"); failmax = (int)urecord->nupdates; goto Rollback; } if (! as_storage_has_space(rd->ns)) { cf_warning(AS_UDF, "drives full, record will not be updated"); failmax = (int)urecord->nupdates; goto Rollback; } if (! is_valid_ttl(rd->ns, urecord->tr->msgp->msg.record_ttl)) { cf_warning(AS_UDF, "invalid ttl %u", urecord->tr->msgp->msg.record_ttl); failmax = (int)urecord->nupdates; goto Rollback; } } if (has_sindex) { SINDEX_GUNLOCK(); } // If there were updates do miscellaneous successful commit // tasks if (is_record_dirty || is_record_flag_dirty || (urecord->flag & UDF_RECORD_FLAG_METADATA_UPDATED)) { urecord->flag |= UDF_RECORD_FLAG_HAS_UPDATES; // will write to storage } urecord->ldt_rectype_bit_update = 0; // Clean up oldvalue cache and reset dirty. All the changes made // here has made to the particle buffer. Nothing will now be backed out. for (uint32_t i = 0; i < urecord->nupdates; i++) { udf_record_bin * bin = &urecord->updates[i]; if (bin->oldvalue != NULL ) { as_val_destroy(bin->oldvalue); bin->oldvalue = NULL; } bin->dirty = false; } return rc; Rollback: cf_debug(AS_UDF, "Rollback Called: failmax %d", failmax); for (int i = 0; i < failmax; i++) { if (urecord->updates[i].dirty) { char * k = urecord->updates[i].name; // Pick the oldvalue for rollback as_val * v = urecord->updates[i].oldvalue; bool h = urecord->updates[i].washidden; if ( k != NULL ) { if ( v == NULL || v->type == AS_NIL ) { // if the value is NIL, then do a delete cf_detail(AS_UDF, "execute rollback: position %d deletes bin %s", i, k); rc = udf_aerospike_delbin(urecord, k); } else { // otherwise, it is a set cf_detail(AS_UDF, "execute rollback: position %d sets bin %s", i, k); rc = udf_aerospike_setbin(urecord, i, k, v, h); if (rc) { cf_warning(AS_UDF, "Rollback failed .. not good ... !!"); } } } if (v) { as_val_destroy(v); cf_debug(AS_UDF, "ROLLBACK as_val_destroy()"); } } } if (is_record_dirty && urecord->dirty != NULL) { xdr_clear_dirty_bins(urecord->dirty); } if (is_record_flag_dirty) { as_index_clear_flags(rd->r, new_index_flags); as_index_set_flags(rd->r, old_index_flags); is_record_flag_dirty = false; } urecord->ldt_rectype_bit_update = 0; if (has_sindex) { SINDEX_GUNLOCK(); } // Reset the flat size in case the stuff is backedout !!! it should not // fail in the backout code ... if (! as_storage_record_size_and_check(rd)) { cf_warning(AS_LDT, "Does not fit even after rollback... it is trouble"); } // Do not clean up the cache in case of failure return -1; }
int as_msg_make_response_bufbuilder(as_record *r, as_storage_rd *rd, cf_buf_builder **bb_r, bool nobindata, char *nsname, bool include_ldt_data, bool include_key, bool skip_empty_records, cf_vector *binlist) { // Sanity checks. Either rd should be there or nobindata and nsname should be present. if (!(rd || (nobindata && nsname))) { cf_detail(AS_PROTO, "Neither storage record nor nobindata is set. Skipping the record."); return 0; } // figure out the size of the entire buffer int set_name_len = 0; const char *set_name = NULL; int ns_len = rd ? strlen(rd->ns->name) : strlen(nsname); if (as_index_get_set_id(r) != INVALID_SET_ID) { as_namespace *ns = NULL; if (rd) { ns = rd->ns; } else if (nsname) { ns = as_namespace_get_byname(nsname); } if (!ns) { cf_info(AS_PROTO, "Cannot get namespace, needed to get set information. Skipping record."); return -1; } set_name = as_index_get_set_name(r, ns); if (set_name) { set_name_len = strlen(set_name); } } uint8_t* key = NULL; uint32_t key_size = 0; if (include_key && as_index_is_flag_set(r, AS_INDEX_FLAG_KEY_STORED)) { if (! as_storage_record_get_key(rd)) { cf_info(AS_PROTO, "can't get key - skipping record"); return -1; } key = rd->key; key_size = rd->key_size; } uint16_t n_fields = 2; int msg_sz = sizeof(as_msg); msg_sz += sizeof(as_msg_field) + sizeof(cf_digest); msg_sz += sizeof(as_msg_field) + ns_len; if (set_name) { n_fields++; msg_sz += sizeof(as_msg_field) + set_name_len; } if (key) { n_fields++; msg_sz += sizeof(as_msg_field) + key_size; } int list_bins = 0; int in_use_bins = rd ? (int)as_bin_inuse_count(rd) : 0; as_val *ldt_bin_vals[in_use_bins]; if (! nobindata) { if (binlist) { int binlist_sz = cf_vector_size(binlist); for (uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); as_bin *p_bin = as_bin_get(rd, binname); if (! p_bin) { continue; } msg_sz += sizeof(as_msg_op); msg_sz += rd->ns->single_bin ? 0 : strlen(binname); if (as_bin_is_hidden(p_bin)) { if (include_ldt_data) { msg_sz += (int)as_ldt_particle_client_value_size(rd, p_bin, &ldt_bin_vals[list_bins]); } else { ldt_bin_vals[list_bins] = NULL; } } else { msg_sz += (int)as_bin_particle_client_value_size(p_bin); } list_bins++; } // Don't return an empty record. if (skip_empty_records && list_bins == 0) { return 0; } } else { msg_sz += sizeof(as_msg_op) * in_use_bins; for (uint16_t i = 0; i < in_use_bins; i++) { as_bin *p_bin = &rd->bins[i]; msg_sz += rd->ns->single_bin ? 0 : strlen(as_bin_get_name_from_id(rd->ns, p_bin->id)); if (as_bin_is_hidden(p_bin)) { if (include_ldt_data) { msg_sz += (int)as_ldt_particle_client_value_size(rd, p_bin, &ldt_bin_vals[i]); } else { ldt_bin_vals[i] = NULL; } } else { msg_sz += (int)as_bin_particle_client_value_size(p_bin); } } } } uint8_t *b; cf_buf_builder_reserve(bb_r, msg_sz, &b); // set up the header uint8_t *buf = b; as_msg *msgp = (as_msg *) buf; msgp->header_sz = sizeof(as_msg); msgp->info1 = (nobindata ? AS_MSG_INFO1_GET_NOBINDATA : 0); msgp->info2 = 0; msgp->info3 = 0; msgp->unused = 0; msgp->result_code = 0; msgp->generation = r->generation; msgp->record_ttl = r->void_time; msgp->transaction_ttl = 0; msgp->n_fields = n_fields; if (rd) { if (binlist) msgp->n_ops = list_bins; else msgp->n_ops = in_use_bins; } else { msgp->n_ops = 0; } as_msg_swap_header(msgp); buf += sizeof(as_msg); as_msg_field *mf = (as_msg_field *) buf; mf->field_sz = sizeof(cf_digest) + 1; mf->type = AS_MSG_FIELD_TYPE_DIGEST_RIPE; if (rd) { memcpy(mf->data, &rd->keyd, sizeof(cf_digest)); } else { memcpy(mf->data, &r->key, sizeof(cf_digest)); } as_msg_swap_field(mf); buf += sizeof(as_msg_field) + sizeof(cf_digest); mf = (as_msg_field *) buf; mf->field_sz = ns_len + 1; mf->type = AS_MSG_FIELD_TYPE_NAMESPACE; if (rd) { memcpy(mf->data, rd->ns->name, ns_len); } else { memcpy(mf->data, nsname, ns_len); } as_msg_swap_field(mf); buf += sizeof(as_msg_field) + ns_len; if (set_name) { mf = (as_msg_field *) buf; mf->field_sz = set_name_len + 1; mf->type = AS_MSG_FIELD_TYPE_SET; memcpy(mf->data, set_name, set_name_len); as_msg_swap_field(mf); buf += sizeof(as_msg_field) + set_name_len; } if (key) { mf = (as_msg_field *) buf; mf->field_sz = key_size + 1; mf->type = AS_MSG_FIELD_TYPE_KEY; memcpy(mf->data, key, key_size); as_msg_swap_field(mf); buf += sizeof(as_msg_field) + key_size; } if (nobindata) { return 0; } if (binlist) { list_bins = 0; int binlist_sz = cf_vector_size(binlist); for (uint16_t i = 0; i < binlist_sz; i++) { char binname[AS_ID_BIN_SZ]; cf_vector_get(binlist, i, (void*)&binname); as_bin *p_bin = as_bin_get(rd, binname); if (! p_bin) { continue; } as_msg_op *op = (as_msg_op *)buf; op->op = AS_MSG_OP_READ; op->version = 0; op->name_sz = as_bin_memcpy_name(rd->ns, op->name, p_bin); op->op_sz = 4 + op->name_sz; buf += sizeof(as_msg_op) + op->name_sz; if (as_bin_is_hidden(p_bin)) { buf += as_ldt_particle_to_client(ldt_bin_vals[list_bins], op); } else { buf += as_bin_particle_to_client(p_bin, op); } list_bins++; as_msg_swap_op(op); } } else { for (uint16_t i = 0; i < in_use_bins; i++) { as_msg_op *op = (as_msg_op *)buf; op->op = AS_MSG_OP_READ; op->version = 0; op->name_sz = as_bin_memcpy_name(rd->ns, op->name, &rd->bins[i]); op->op_sz = 4 + op->name_sz; buf += sizeof(as_msg_op) + op->name_sz; if (as_bin_is_hidden(&rd->bins[i])) { buf += as_ldt_particle_to_client(ldt_bin_vals[i], op); } else { buf += as_bin_particle_to_client(&rd->bins[i], op); } as_msg_swap_op(op); } } return 0; }
// Build response to batch request. static void batch_build_response(batch_transaction* btr, cf_buf_builder** bb_r) { as_namespace* ns = btr->ns; batch_digests *bmds = btr->digests; bool get_data = btr->get_data; uint32_t yield_count = 0; for (int i = 0; i < bmds->n_digests; i++) { batch_digest *bmd = &bmds->digest[i]; if (bmd->done == false) { // try to get the key as_partition_reservation rsv; AS_PARTITION_RESERVATION_INIT(rsv); cf_node other_node = 0; uint64_t cluster_key; if (! *bb_r) { *bb_r = cf_buf_builder_create_size(1024 * 4); } int rv = as_partition_reserve_read(ns, as_partition_getid(bmd->keyd), &rsv, &other_node, &cluster_key); if (rv == 0) { cf_atomic_int_incr(&g_config.batch_tree_count); as_index_ref r_ref; r_ref.skip_lock = false; int rec_rv = as_record_get(rsv.tree, &bmd->keyd, &r_ref, ns); if (rec_rv == 0) { as_index *r = r_ref.r; // Check to see this isn't an expired record waiting to die. if (r->void_time && r->void_time < as_record_void_time_get()) { as_msg_make_error_response_bufbuilder(&bmd->keyd, AS_PROTO_RESULT_FAIL_NOTFOUND, bb_r, ns->name); } else { // Make sure it's brought in from storage if necessary. as_storage_rd rd; if (get_data) { as_storage_record_open(ns, r, &rd, &r->key); rd.n_bins = as_bin_get_n_bins(r, &rd); } // Note: this array must stay in scope until the // response for this record has been built, since in the // get data w/ record on device case, it's copied by // reference directly into the record descriptor. as_bin stack_bins[!get_data || rd.ns->storage_data_in_memory ? 0 : rd.n_bins]; if (get_data) { // Figure out which bins you want - for now, all. rd.bins = as_bin_get_all(r, &rd, stack_bins); rd.n_bins = as_bin_inuse_count(&rd); } as_msg_make_response_bufbuilder(r, (get_data ? &rd : NULL), bb_r, !get_data, (get_data ? NULL : ns->name), true, false, btr->binlist); if (get_data) { as_storage_record_close(r, &rd); } } as_record_done(&r_ref, ns); } else { // TODO - what about empty records? cf_debug(AS_BATCH, "batch_build_response: as_record_get returned %d : key %"PRIx64, rec_rv, *(uint64_t *)&bmd->keyd); as_msg_make_error_response_bufbuilder(&bmd->keyd, AS_PROTO_RESULT_FAIL_NOTFOUND, bb_r, ns->name); } bmd->done = true; as_partition_release(&rsv); cf_atomic_int_decr(&g_config.batch_tree_count); } else { cf_debug(AS_BATCH, "batch_build_response: partition reserve read failed: rv %d", rv); as_msg_make_error_response_bufbuilder(&bmd->keyd, AS_PROTO_RESULT_FAIL_NOTFOUND, bb_r, ns->name); if (other_node != 0) { bmd->node = other_node; cf_debug(AS_BATCH, "other_node is: %p.", other_node); } else { cf_debug(AS_BATCH, "other_node is NULL."); } } yield_count++; if (yield_count % g_config.batch_priority == 0) { usleep(1); } } } }