/*
 * 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;
}
示例#2
0
// 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;
}
示例#3
0
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);
}
示例#4
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;
}
示例#6
0
// 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;
}
示例#7
0
/*
 * 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;
}
示例#8
0
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;
}
示例#9
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);
			}
		}
	}
}