Exemple #1
0
/**
 * Initialize udf_def data structure from the transaction over the wire.
 */
udf_def *
udf_def_init_from_msg(udf_def * def, const as_transaction * tr)
{
	as_msg *m = &tr->msgp->msg;
	as_msg_field *filename = as_msg_field_get(m, AS_MSG_FIELD_TYPE_UDF_FILENAME);

	if (! filename) {
		return NULL;
	}

	as_msg_field *function = as_msg_field_get(m, AS_MSG_FIELD_TYPE_UDF_FUNCTION);

	if (! function) {
		return NULL;
	}

	as_msg_field *arglist = as_msg_field_get(m, AS_MSG_FIELD_TYPE_UDF_ARGLIST);

	if (! arglist) {
		return NULL;
	}

	as_msg_field_get_strncpy(filename, def->filename, sizeof(def->filename));
	as_msg_field_get_strncpy(function, def->function, sizeof(def->function));
	def->arglist = arglist;

	as_msg_field *op = as_transaction_has_udf_op(tr) ?
			as_msg_field_get(m, AS_MSG_FIELD_TYPE_UDF_OP) : NULL;

	def->type = op ? *op->data : AS_UDF_OP_KVS;

	return def;
}
int
as_transaction_digest_validate(as_transaction *tr)
{
	cl_msg *msgp = tr->msgp;
	as_msg *m = &msgp->msg;

	cf_info(AS_PROTO, "digest compare succeeded");

	// Can only validate if we have two things to compare.
	as_msg_field *dfp = as_msg_field_get(m, AS_MSG_FIELD_TYPE_DIGEST_RIPE);
	if (dfp == 0) {
		cf_info(AS_PROTO, "no incoming protocol digest to validate");
		return(0);
	}
	if (as_msg_field_get_value_sz(dfp) != sizeof(cf_digest)) {
		cf_info(AS_PROTO, "sent bad digest size %d, can't validate",as_msg_field_get_value_sz(dfp));
		return(-1);
	}

	// Pull out the key and do the computation the same way as above.
	cf_digest computed;
	memset(&computed, 0, sizeof(cf_digest) );

	as_msg_field *kfp = as_msg_field_get(m, AS_MSG_FIELD_TYPE_KEY);
	if (!kfp) {
		cf_info(AS_PROTO, "received request with no key and no digest, validation failed");
		return(-1);
	}

	as_msg_field *sfp = as_msg_field_get(m, AS_MSG_FIELD_TYPE_SET);
	if (sfp == 0 || as_msg_field_get_value_sz(sfp) == 0) {
		cf_digest_compute(kfp->data, as_msg_field_get_value_sz(kfp), &tr->keyd);
	}
	else {
		cf_digest_compute2(sfp->data, as_msg_field_get_value_sz(sfp),
					kfp->data, as_msg_field_get_value_sz(kfp),
					&computed);
	}

	if (0 == memcmp(&computed, &tr->keyd, sizeof(computed))) {
		cf_info(AS_PROTO, "digest compare failed. wire: %"PRIx64" computed: %"PRIx64,
			*(uint64_t *)&tr->keyd, *(uint64_t *) &computed );
		return(-1);
	}

	cf_info(AS_PROTO, "digest compare succeeded");

	return(0);
}
bool
get_msg_key(as_transaction* tr, as_storage_rd* rd)
{
	if (! as_transaction_has_key(tr)) {
		return true;
	}

	if (rd->ns->single_bin && rd->ns->storage_data_in_memory) {
		cf_warning(AS_RW, "{%s} can't store key if data-in-memory & single-bin",
				tr->rsv.ns->name);
		return false;
	}

	as_msg_field* f = as_msg_field_get(&tr->msgp->msg, AS_MSG_FIELD_TYPE_KEY);

	if ((rd->key_size = as_msg_field_get_value_sz(f)) == 0) {
		cf_warning(AS_RW, "msg flat key size is 0");
		return false;
	}

	rd->key = f->data;

	if (*rd->key == AS_PARTICLE_TYPE_INTEGER &&
			rd->key_size != 1 + sizeof(uint64_t)) {
		cf_warning(AS_RW, "bad msg integer key flat size %u", rd->key_size);
		return false;
	}

	return true;
}
int
set_set_from_msg(as_record* r, as_namespace* ns, as_msg* m)
{
	as_msg_field* f = as_msg_field_get(m, AS_MSG_FIELD_TYPE_SET);
	size_t name_len = (size_t)as_msg_field_get_value_sz(f);

	if (name_len == 0) {
		return 0;
	}

	// Given the name, find/assign the set-ID and write it in the as_index.
	return as_index_set_set_w_len(r, ns, (const char*)f->data, name_len, true);
}
// Caller must have checked that key is present in message.
bool
check_msg_key(as_msg* m, as_storage_rd* rd)
{
	as_msg_field* f = as_msg_field_get(m, AS_MSG_FIELD_TYPE_KEY);
	uint32_t key_size = as_msg_field_get_value_sz(f);
	uint8_t* key = f->data;

	if (key_size != rd->key_size || memcmp(key, rd->key, key_size) != 0) {
		cf_warning(AS_RW, "key mismatch - end of universe?");
		return false;
	}

	return true;
}
/**
 * Initialize udf_call data structure from the msg over the wire
 *
 * Returns:
 * 		0 on success
 * 		-1 on failure
 */
int
udf_rw_call_init_from_msg(udf_call * call, as_msg *msg)
{
	call->def.type = AS_UDF_OP_KVS;
	as_msg_field *  filename = NULL;
	as_msg_field *  function = NULL;
	as_msg_field *  arglist =  NULL;

	// Check the type of udf
	as_msg_field *  op = NULL;
	op = as_msg_field_get(msg, AS_MSG_FIELD_TYPE_UDF_OP);
	if ( op ) {
		memcpy(&call->def.type, (byte *)op->data, sizeof(as_udf_op));
	}

	filename = as_msg_field_get(msg, AS_MSG_FIELD_TYPE_UDF_FILENAME);
	if ( filename ) {
		function = as_msg_field_get(msg, AS_MSG_FIELD_TYPE_UDF_FUNCTION);
		if ( function ) {
			arglist = as_msg_field_get(msg, AS_MSG_FIELD_TYPE_UDF_ARGLIST);
			if ( arglist ) {
				as_msg_field_get_strncpy(filename, &call->def.filename[0], sizeof(call->def.filename));
				as_msg_field_get_strncpy(function, &call->def.function[0], sizeof(call->def.function));
				call->def.arglist = arglist;
				return 0;
			}
		}
	}

	call->tr = NULL;
	call->def.filename[0] = 0;
	call->def.function[0] = 0;
	call->def.arglist = NULL;

	return -1;
}
Exemple #7
0
/*
 *  Peek into the message to determine the JEMalloc arena to be used for the namespace.
 *
 *  Return the arena number, or else -1 if any error occurs.
 *
 *  Note:  This function works on unswapped data.
 */
int
as_msg_peek_namespace_jem_arena(cl_msg *msgp)
{
	int arena = -1;

	if (msgp && (msgp->proto.version == PROTO_VERSION) && (msgp->proto.type == PROTO_TYPE_AS_MSG)) {
		as_msg *amsg = &msgp->msg;

		as_msg_field *mf = as_msg_field_get(amsg, AS_MSG_FIELD_TYPE_NAMESPACE);

		if (mf) {
			char *ns = (char *) mf->data;
			arena = as_namespace_get_jem_arena(ns);
		}
	}

	return arena;
}
Exemple #8
0
bool
as_msg_peek_data_in_memory(cl_msg *msgp)
{
	if (! msgp ||
			msgp->proto.version != PROTO_VERSION ||
			msgp->proto.type != PROTO_TYPE_AS_MSG) {
		return false;
	}

	as_msg_field *f = as_msg_field_get(&msgp->msg, AS_MSG_FIELD_TYPE_NAMESPACE);

	if (! f) {
		return false;
	}

	as_namespace *ns = as_namespace_get_bymsgfield_unswap(f);

	return ns && ns->storage_data_in_memory;
}
/**
 * aerospike::create(record)
 * Function: udf_aerospike_rec_create
 *
 * Parameters:
 * 		as - as_aerospike
 *		rec - as_rec
 *
 * Return Values:
 * 		1 if record is being read or on a create, it already exists
 * 		o/w return value of udf_aerospike__execute_updates
 *
 * Description:
 * 		Create a new record in local storage.
 * 		The record will only be created if it does not exist.
 * 		This assumes the record has a digest that is valid for local storage.
 *
 *		Synchronization : object lock acquired by the transaction thread executing UDF.
 * 		Partition reservation takes place just before the transaction starts executing
 * 		( look for as_partition_reserve_udf in thr_tsvc.c )
 *
 * 		Callers:
 * 		lua interfacing function, mod_lua_aerospike_rec_create
 * 		The return value of udf_aerospike_rec_create is pushed on to the lua stack
 *
 * 		Notes:
 * 		The 'read' and 'exists' flag of udf_record are set to true.
*/
static int
udf_aerospike_rec_create(const as_aerospike * as, const as_rec * rec)
{
	int ret = udf_aerospike_param_check(as, rec, __FILE__, __LINE__);
	if (ret) {
		return ret;
	}

	udf_record * urecord  = (udf_record *) as_rec_source(rec);

	// make sure record isn't already successfully read
	if (urecord->flag & UDF_RECORD_FLAG_OPEN) {
		cf_detail(AS_UDF, "udf_aerospike_rec_create: Record Already Exists");
		return 1;
	}
	as_transaction *tr    = urecord->tr;
	as_index_ref   *r_ref = urecord->r_ref;
	as_storage_rd  *rd    = urecord->rd;
	as_index_tree  *tree  = tr->rsv.tree;

	if (urecord->flag & UDF_RECORD_FLAG_IS_SUBRECORD) {
		tree      = tr->rsv.sub_tree;
	}

	// make sure we got the record as a create
	int rv = as_record_get_create(tree, &tr->keyd, r_ref, tr->rsv.ns);
	cf_detail_digest(AS_UDF, &tr->keyd, "Creating %sRecord",
			(urecord->flag & UDF_RECORD_FLAG_IS_SUBRECORD) ? "Sub" : "");

	// rv 0 means record exists, 1 means create, < 0 means fail
	// TODO: Verify correct result codes.
	if (rv == 0) {
		cf_warning(AS_UDF, "udf_aerospike_rec_create: Record Already Exists 2");
		as_record_done(r_ref, tr->rsv.ns);
		bzero(r_ref, sizeof(as_index_ref));
		return 1;
	} else if (rv < 0) {
		cf_warning(AS_UDF, "udf_aerospike_rec_create: Record Open Failed with rv=%d", rv);
		return rv;
	}

	// Associates the set name with the storage rec and index
	if(tr->msgp) {
		// Set the set name to index and close record if the setting the set name
		// is not successful
		int rv_set = as_record_set_set_from_msg(r_ref->r, tr->rsv.ns, &tr->msgp->msg);
		if (rv_set != 0) {
			cf_warning(AS_UDF, "udf_aerospike_rec_create: Failed to set setname");
			as_record_done(r_ref, tr->rsv.ns);
			// TODO bzero is expensive. Switch to use flag.
			bzero(r_ref, sizeof(as_index_ref));
			return 4;
		}
	}

	urecord->flag |= UDF_RECORD_FLAG_OPEN;
	cf_detail(AS_UDF, "Open %p %x %"PRIx64"", urecord, urecord->flag, *(uint64_t *)&tr->keyd);

	as_index *r    = r_ref->r;
	// open up storage
	as_storage_record_create(urecord->tr->rsv.ns, urecord->r_ref->r,
		urecord->rd, &urecord->tr->keyd);

	cf_detail(AS_UDF, "as_storage_record_create: udf_aerospike_rec_create: r %p rd %p",
		urecord->r_ref->r, urecord->rd);

	// if multibin storage, we will use urecord->stack_bins, so set the size appropriately
	if ( ! rd->ns->storage_data_in_memory && ! rd->ns->single_bin ) {
		rd->n_bins = sizeof(urecord->stack_bins) / sizeof(as_bin);
	}

	// side effect: will set the unused bins to properly unused
	rd->bins       = as_bin_get_all(r, rd, urecord->stack_bins);
	urecord->flag |= UDF_RECORD_FLAG_STORAGE_OPEN;

	// If the message has a key, apply it to the record.
	as_msg_field* f = as_msg_field_get(&tr->msgp->msg, AS_MSG_FIELD_TYPE_KEY);
	if (f) {
		rd->key_size = as_msg_field_get_value_sz(f);
		rd->key = f->data;
	}

	cf_detail(AS_UDF, "Storage Open %p %x %"PRIx64"", urecord, urecord->flag, *(uint64_t *)&tr->keyd);
	cf_detail(AS_UDF, "udf_aerospike_rec_create: Record created %d", urecord->flag);

	int rc         = udf_aerospike__execute_updates(urecord);
	if(rc) {
		//  Creating the udf record failed, destroy the as_record
		if (!as_bin_inuse_has(urecord->rd)) {
			udf_aerospike_rec_remove(as, rec);
		}
	}
	return rc;
}
Exemple #10
0
/**
 * Initialize a new UDF. This populates the udf_call from information
 * in the current transaction. If passed in transaction has req_data it is
 * assumed to be internal and the UDF information is picked from the udata
 * associated with it. TODO: Do not overload please define flag for this.
 *
 *
 * Parameter:
 * 		tr the transaction to build a udf_call from
 *
 * Returns"
 * 		return a new udf_call (Caller needs to free it up)
 * 		NULL in case of failure
 */
int
udf_call_init(udf_call * call, as_transaction * tr)
{

	call->active   = false;
	call->udf_type = AS_SCAN_UDF_NONE;
	as_msg_field *  filename = NULL;
	as_msg_field *  function = NULL;
	as_msg_field *  arglist =  NULL;

	if (tr->udata.req_udata) {
		udf_call *ucall = NULL;
		if (tr->udata.req_type == UDF_SCAN_REQUEST) {
			ucall = &((tscan_job *)(tr->udata.req_udata))->call;
		} else if (tr->udata.req_type == UDF_QUERY_REQUEST) {
			ucall = as_query_get_udf_call(tr->udata.req_udata);
		}

		if (ucall) {
			strncpy(call->filename, ucall->filename, sizeof(ucall->filename));
			strncpy(call->function, ucall->function, sizeof(ucall->function));
			call->transaction = tr;
			call->active      = true;
			call->arglist     = ucall->arglist;
			call->udf_type    = ucall->udf_type;
			if (tr->udata.req_type == UDF_SCAN_REQUEST) {
				cf_atomic_int_incr(&g_config.udf_scan_rec_reqs);
			} else if (tr->udata.req_type == UDF_QUERY_REQUEST) {
				cf_atomic_int_incr(&g_config.udf_query_rec_reqs);
			}
		}
		// TODO: return proper macros
		return 0;
	}

	// Check the type of udf
	as_msg_field *  op = NULL;
	op = as_msg_field_get(&tr->msgp->msg, AS_MSG_FIELD_TYPE_UDF_OP);
	if (!op) {
		// Normal udf operation, no special type
		call->udf_type = 0;
	} else {
		// We got a udf type from the server
		byte optype;
		memcpy(&optype, (byte *)op->data, sizeof(optype));
		if(optype == AS_SCAN_UDF_OP_UDF) {
			cf_debug(AS_UDF, "UDF scan op received");
			call->udf_type = AS_SCAN_UDF_OP_UDF;
		} else if(optype == AS_SCAN_UDF_OP_BACKGROUND) {
			cf_debug(AS_UDF, "UDF scan background op received");
			call->udf_type = AS_SCAN_UDF_OP_BACKGROUND;
		} else {
			cf_warning(AS_UDF, "Undefined udf type received over protocol");
			goto Cleanup;
		}
	}
	filename = as_msg_field_get(&tr->msgp->msg, AS_MSG_FIELD_TYPE_UDF_FILENAME);
	if ( filename ) {
		function = as_msg_field_get(&tr->msgp->msg, AS_MSG_FIELD_TYPE_UDF_FUNCTION);
		if ( function ) {
			arglist = as_msg_field_get(&tr->msgp->msg, AS_MSG_FIELD_TYPE_UDF_ARGLIST);
			if ( arglist ) {
				call->transaction = tr;
				as_msg_field_get_strncpy(filename, &call->filename[0], sizeof(call->filename));
				as_msg_field_get_strncpy(function, &call->function[0], sizeof(call->function));
				call->arglist = arglist;
				call->active = true;
				cf_detail(AS_UDF, "UDF Request Unpacked %s %s", call->filename, call->function);
				return 0;
			}
		}
	}
Cleanup:
	call->transaction = NULL;
	call->filename[0] = 0;
	call->function[0] = 0;
	call->arglist = NULL;

	return 1;
}
// Put batch request on a separate batch queue.
int
as_batch(as_transaction* tr)
{
	as_msg* msg = &tr->msgp->msg;

	as_msg_field* nsfp = as_msg_field_get(msg, AS_MSG_FIELD_TYPE_NAMESPACE);
	if (! nsfp) {
		cf_warning(AS_BATCH, "Batch namespace is required.");
		return -1;
	}

	as_msg_field* dfp = as_msg_field_get(msg, AS_MSG_FIELD_TYPE_DIGEST_RIPE_ARRAY);
	if (! dfp) {
		cf_warning(AS_BATCH, "Batch digests are required.");
		return -1;
	}

	uint n_digests = dfp->field_sz / sizeof(cf_digest);

	if (n_digests > g_config.batch_max_requests) {
		cf_warning(AS_BATCH, "Batch request size %u exceeds max %u.", n_digests, g_config.batch_max_requests);
		return -1;
	}

	batch_transaction btr;
	btr.trid = tr->trid;
	btr.end_time = tr->end_time;
	btr.get_data = !(msg->info1 & AS_MSG_INFO1_GET_NOBINDATA);

	btr.ns = as_namespace_get_bymsgfield(nsfp);
	if (! btr.ns) {
		cf_warning(AS_BATCH, "Batch namespace is required.");
		return -1;
	}

	// Create the master digest table.
	btr.digests = (batch_digests*) cf_malloc(sizeof(batch_digests) + (sizeof(batch_digest) * n_digests));
	if (! btr.digests) {
		cf_warning(AS_BATCH, "Failed to allocate memory for batch digests.");
		return -1;
	}

	batch_digests* bmd = btr.digests;
	bmd->n_digests = n_digests;
	uint8_t* digest_field_data = dfp->data;

	for (int i = 0; i < n_digests; i++) {
		bmd->digest[i].done = false;
		bmd->digest[i].node = 0;
		memcpy(&bmd->digest[i].keyd, digest_field_data, sizeof(cf_digest));
		digest_field_data += sizeof(cf_digest);
	}

	btr.binlist = as_binlist_from_op(msg);
	btr.fd_h = tr->proto_fd_h;
	tr->proto_fd_h = 0;
	btr.fd_h->last_used = cf_getms();

	cf_atomic_int_incr(&g_config.batch_initiate);
	cf_queue_push(g_batch_queue, &btr);
	return 0;
}
// Put batch request on a separate batch queue.
int
as_batch_direct_queue_task(as_transaction* tr)
{
	cf_atomic_int_incr(&g_config.batch_initiate);

	if (g_config.n_batch_threads <= 0) {
		cf_warning(AS_BATCH, "batch-threads has been disabled.");
		return AS_PROTO_RESULT_FAIL_BATCH_DISABLED;
	}

	as_msg* msg = &tr->msgp->msg;

	as_msg_field* nsfp = as_msg_field_get(msg, AS_MSG_FIELD_TYPE_NAMESPACE);
	if (! nsfp) {
		cf_warning(AS_BATCH, "Batch namespace is required.");
		return AS_PROTO_RESULT_FAIL_NAMESPACE;
	}

	as_msg_field* dfp = as_msg_field_get(msg, AS_MSG_FIELD_TYPE_DIGEST_RIPE_ARRAY);
	if (! dfp) {
		cf_warning(AS_BATCH, "Batch digests are required.");
		return AS_PROTO_RESULT_FAIL_PARAMETER;
	}

	uint n_digests = dfp->field_sz / sizeof(cf_digest);

	if (n_digests > g_config.batch_max_requests) {
		cf_warning(AS_BATCH, "Batch request size %u exceeds max %u.", n_digests, g_config.batch_max_requests);
		return AS_PROTO_RESULT_FAIL_BATCH_MAX_REQUESTS;
	}

	batch_transaction btr;
	btr.trid = tr->trid;
	btr.end_time = tr->end_time;
	btr.get_data = !(msg->info1 & AS_MSG_INFO1_GET_NOBINDATA);
	btr.complete = false;

	btr.ns = as_namespace_get_bymsgfield(nsfp);
	if (! btr.ns) {
		cf_warning(AS_BATCH, "Batch namespace is required.");
		return AS_PROTO_RESULT_FAIL_NAMESPACE;
	}

	// Create the master digest table.
	btr.digests = (batch_digests*) cf_malloc(sizeof(batch_digests) + (sizeof(batch_digest) * n_digests));
	if (! btr.digests) {
		cf_warning(AS_BATCH, "Failed to allocate memory for batch digests.");
		return AS_PROTO_RESULT_FAIL_UNKNOWN;
	}

	batch_digests* bmd = btr.digests;
	bmd->n_digests = n_digests;
	uint8_t* digest_field_data = dfp->data;

	for (int i = 0; i < n_digests; i++) {
		bmd->digest[i].done = false;
		bmd->digest[i].node = 0;
		memcpy(&bmd->digest[i].keyd, digest_field_data, sizeof(cf_digest));
		digest_field_data += sizeof(cf_digest);
	}

	btr.binlist = as_binlist_from_op(msg);
	btr.fd_h = tr->proto_fd_h;
	tr->proto_fd_h = 0;
	btr.fd_h->last_used = cf_getms();

	int status = as_thread_pool_queue_task_fixed(&batch_direct_thread_pool, &btr);
	
	if (status) {
		cf_warning(AS_BATCH, "Batch enqueue failed");
		return AS_PROTO_RESULT_FAIL_UNKNOWN;		
	}
	return 0;
}
/*
  The transaction prepare function fills out the fields of the tr structure,
  using the information in the msg structure. It also swaps the fields in the
  message header (which are originally in network byte order).

  Once the prepare function has been called on the transaction, the
  'preprocessed' flag is set and the transaction cannot be prepared again.
  Returns:
  0:  OK
  -1: General Error
  -2: Request received with no key
  -3: Request received with digest array
*/
int as_transaction_prepare(as_transaction *tr) {
	cl_msg *msgp = tr->msgp;
	as_msg *m = &msgp->msg;

#if defined(USE_SYSTEMTAP)
	uint64_t nodeid = g_config.self_node;
#endif

//	cf_assert(tr, AS_PROTO, CF_CRITICAL, "invalid transaction");

	void *limit = ((void *)m) + msgp->proto.sz;

	// Check processed flag.  It's a non-fatal error to call this function again.
	if( tr->preprocessed )
		return(0);

	if (0 != cf_digest_compare(&tr->keyd, &cf_digest_zero)) {
		cf_warning(AS_RW, "Internal inconsistency: transaction has keyd, but marked not swizzled");
	}

	as_msg_swap_header(m);
	if (0 != as_msg_swap_fields_and_ops(m, limit)) {
		cf_info(AS_PROTO, "msg swap field and ops returned error");
		return(-1);
	}

	if (m->n_fields>PROTO_NFIELDS_MAX_WARNING) {
		cf_info(AS_PROTO, "received too many n_fields! %d",m->n_fields);
    }

	// Set the transaction end time if available.
	if (m->transaction_ttl) {
//		cf_debug(AS_PROTO, "received non-zero transaction ttl: %d",m->transaction_ttl);
		tr->end_time = tr->start_time + ((uint64_t)m->transaction_ttl * 1000000);
	}

	// Set the preprocessed flag. All exits from here on out are considered
	// processed since the msg header has been swapped and the transaction
	// end time has been set.
	tr->preprocessed = true;
	tr->flag         = 0;
	UREQ_DATA_INIT(&tr->udata);
	as_msg_field *sfp = as_msg_field_get(m, AS_MSG_FIELD_TYPE_DIGEST_RIPE_ARRAY);
	if (sfp) {
		cf_debug(AS_PROTO, "received request with digest array, batch");
		return(-3);
	}

	// It's tempting to move this to the final return, but queries
	// actually escape in the if clause below ...
	ASD_TRANS_PREPARE(nodeid, (uint64_t) msgp, tr->trid);

	// Sent digest? Use!
	as_msg_field *dfp = as_msg_field_get(m, AS_MSG_FIELD_TYPE_DIGEST_RIPE);
	if (dfp) {
//		cf_detail(AS_PROTO, "transaction prepare: received sent digest\n");
		if (as_msg_field_get_value_sz(dfp) != sizeof(cf_digest)) {
			cf_info(AS_PROTO, "sent bad digest size %d, recomputing digest",as_msg_field_get_value_sz(dfp));
			goto Compute;
		}
		memcpy(&tr->keyd, dfp->data, sizeof(cf_digest));
	}
	// Not sent, so compute.
	else {
Compute:		;
		as_msg_field *kfp = as_msg_field_get(m, AS_MSG_FIELD_TYPE_KEY);
		if (!kfp) {
			cf_detail(AS_PROTO, "received request with no key, scan");
			return(-2);
		}
        if (as_msg_field_get_value_sz(kfp)> PROTO_FIELD_LENGTH_MAX) {
	        cf_info(AS_PROTO, "key field too big %d. Is it for real?",as_msg_field_get_value_sz(kfp));
        }

		as_msg_field *sfp = as_msg_field_get(m, AS_MSG_FIELD_TYPE_SET);
		if (sfp == 0 || as_msg_field_get_value_sz(sfp) == 0) {
			cf_digest_compute(kfp->data, as_msg_field_get_value_sz(kfp), &tr->keyd);
			// cf_info(AS_PROTO, "computing key for sz %d %"PRIx64" bytes %d %d %d %d",as_msg_field_get_value_sz(kfp),*(uint64_t *)&tr->keyd,kfp->data[0],kfp->data[1],kfp->data[2],kfp->data[3]);
		}
		else {
            if (as_msg_field_get_value_sz(sfp)> PROTO_FIELD_LENGTH_MAX) {
		        cf_info(AS_PROTO, "set field too big %d. Is this for real?",as_msg_field_get_value_sz(sfp));
            }
			cf_digest_compute2(sfp->data, as_msg_field_get_value_sz(sfp),
						kfp->data, as_msg_field_get_value_sz(kfp),
						&tr->keyd);
			// cf_info(AS_PROTO, "computing set with key for sz %d %"PRIx64" bytes %d %d",as_msg_field_get_value_sz(kfp),*(uint64_t *)&tr->keyd,kfp->data[0],kfp->data[1],kfp->data[2],kfp->data[3]);
		}
	}

	return(0);
}