/* Workhorse function to send response back to the client after UDF execution. * * Assumption: The call should be setup properly pointing to the tr. * * Special Handling: If it is background udf job do not send any * response to client */ int send_response(udf_call *call, const char *bin_name, const as_val *val) { // NO response if background UDF if (call->def.type == AS_UDF_OP_BACKGROUND) { return 0; } // Note - this function quietly handles a null val. The response call will // be given a bin with a name but not 'in use', and it does the right thing. as_bin stack_bin; as_bin *bin = &stack_bin; uint32_t particle_size = as_particle_size_from_asval(val); static const size_t MAX_STACK_SIZE = 32 * 1024; uint8_t stack_particle[particle_size > MAX_STACK_SIZE ? 0 : particle_size]; uint8_t *particle_buf = stack_particle; if (particle_size > MAX_STACK_SIZE) { particle_buf = (uint8_t *)cf_malloc(particle_size); if (! particle_buf) { cf_warning(AS_UDF, "failed alloc for particle size %u", particle_size); return -1; } } as_transaction *tr = call->tr; as_namespace *ns = tr->rsv.ns; as_bin_init(ns, bin, bin_name); as_bin_particle_stack_from_asval(bin, particle_buf, val); single_transaction_response(tr, ns, NULL, &bin, 1, tr->generation, tr->void_time, NULL, NULL); if (particle_buf != stack_particle) { cf_free(particle_buf); } return 0; } // end send_response()
/* * Internal function: udf_aerospike_setbin * * Parameters: * offset -- offset of udf bin in updates array * r -- udf_record to be manipulated * bname -- name of the bin to be deleted * val -- value to be updated with * * Return value: * 0 on success * -1 on failure * * Description: * The function sets the bin with the name * passed in as parameter to the value, passed as the third parameter. * Before updating the bin, it is checked if the value can fit in the 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: * udf_aerospike__apply_update_atomic * In this function, if it fails at the time of update, the record is set * to rollback all the updates till this point. The case where it fails in * rollback is not handled. * * Side Notes: * i. write_to_device will be set to true on a successful bin update. * If all the updates from udf_aerospike__apply_update_atomic (including this) are * successful, the record will be written to disk and reopened so that the rest of * sets of updates can be applied. * * ii. If put in sindex fails, we do not handle it. * * TODO make sure anything goes into setbin only if the bin value is * changed */ static int udf_aerospike_setbin(udf_record * urecord, int offset, const char * bname, const as_val * val, bool is_hidden) { if (bname == NULL || bname[0] == 0 ) { cf_warning(AS_UDF, "udf_aerospike_setbin: Invalid Parameters: [No bin name supplied]... Fail"); return -1; } if (as_particle_type_from_asval(val) == AS_PARTICLE_TYPE_NULL) { cf_warning(AS_UDF, "udf_aerospike_setbin: [%s] called with unusable as_val", bname); return -3; } uint8_t type = as_val_type(val); if (is_hidden && ((type != AS_MAP) && (type != AS_LIST))) { cf_warning(AS_UDF, "udf_aerospike_setbin: Invalid Operation [Hidden %d type Not allowed]... Fail", type); return -3; } as_storage_rd * rd = urecord->rd; as_transaction *tr = urecord->tr; as_bin * b = as_bin_get_or_create(rd, bname); if ( !b ) { cf_warning(AS_UDF, "udf_aerospike_setbin: Internal Error [Bin %s not found.. Possibly ran out of bins]... Fail", bname); return -1; } bool has_sindex = as_sindex_ns_has_sindex(rd->ns); if (has_sindex) { SINDEX_GRLOCK(); } SINDEX_BINS_SETUP(sbins, 2 * rd->ns->sindex_cnt); as_sindex * si_arr[2 * rd->ns->sindex_cnt]; int sbins_populated = 0; int si_arr_index = 0; const char * set_name = as_index_get_set_name(rd->r, rd->ns); if (has_sindex ) { si_arr_index += as_sindex_arr_lookup_by_set_binid_lockfree(rd->ns, set_name, b->id, &si_arr[si_arr_index]); sbins_populated += as_sindex_sbins_from_bin(rd->ns, set_name, b, &sbins[sbins_populated], AS_SINDEX_OP_DELETE); } // we know we are doing an update now, make sure there is particle data, // set to be 1 wblock size now @TODO! int ret = 0; cf_detail(AS_UDF, "udf_setbin: bin %s type %d ", bname, type ); if (rd->ns->storage_data_in_memory) { if (as_bin_particle_replace_from_asval(b, val) != 0) { cf_warning(AS_UDF, "udf_aerospike_setbin: [%s] failed to replace particle", bname); ret = -4; } } else { uint32_t size = as_particle_size_from_asval(val); uint8_t *particle_buf = udf__aerospike_get_particle_buf(urecord, &urecord->updates[offset], size); if (particle_buf) { as_bin_particle_stack_from_asval(b, particle_buf, val); } else { cf_warning(AS_UDF, "udf_aerospike_setbin: [%s] failed to get space for particle size %u", bname, size); ret = -4; } } if (is_hidden && ret == 0) { if (type == AS_LIST) { as_bin_particle_list_set_hidden(b); } else if (type == AS_MAP) { as_bin_particle_map_set_hidden(b); } } // Update sindex if required if (has_sindex) { if (ret) { SINDEX_GUNLOCK(); if (sbins_populated > 0) { as_sindex_sbin_freeall(sbins, sbins_populated); } as_sindex_release_arr(si_arr, si_arr_index); return ret; } si_arr_index += as_sindex_arr_lookup_by_set_binid_lockfree(rd->ns, set_name, b->id, &si_arr[si_arr_index]); sbins_populated += as_sindex_sbins_from_bin(rd->ns, set_name, b, &sbins[sbins_populated], AS_SINDEX_OP_INSERT); SINDEX_GUNLOCK(); if (sbins_populated > 0) { tr->flags |= AS_TRANSACTION_FLAG_SINDEX_TOUCHED; as_sindex_update_by_sbin(rd->ns, as_index_get_set_name(rd->r, rd->ns), sbins, sbins_populated, &rd->keyd); as_sindex_sbin_freeall(sbins, sbins_populated); } as_sindex_release_arr(si_arr, si_arr_index); } return ret; } // end udf_aerospike_setbin()
/* Workhorse function to send response back to the client after UDF execution. * * Assumption: The call should be setup properly pointing to the tr. * * Special Handling: If it is background udf job do not send any * response to client */ int process_response(udf_call *call, const char *bin_name, const as_val *val, cf_dyn_buf *db) { // NO response if background UDF if (call->def->type == AS_UDF_OP_BACKGROUND) { return 0; } // Note - this function quietly handles a null val. The response call will // be given a bin with a name but not 'in use', and it does the right thing. as_bin stack_bin; as_bin *bin = &stack_bin; uint32_t particle_size = as_particle_size_from_asval(val); static const size_t MAX_STACK_SIZE = 32 * 1024; uint8_t stack_particle[particle_size > MAX_STACK_SIZE ? 0 : particle_size]; uint8_t *particle_buf = stack_particle; if (particle_size > MAX_STACK_SIZE) { particle_buf = (uint8_t *)cf_malloc(particle_size); if (! particle_buf) { cf_warning(AS_UDF, "failed alloc for particle size %u", particle_size); return -1; } } as_transaction *tr = call->tr; as_namespace *ns = tr->rsv.ns; as_bin_init(ns, bin, bin_name); as_bin_particle_stack_from_asval(bin, particle_buf, val); if (db) { size_t msg_sz = 0; uint8_t *msgp = (uint8_t *)as_msg_make_response_msg(tr->result_code, tr->generation, tr->void_time, NULL, &bin, 1, ns, NULL, &msg_sz, as_transaction_trid(tr), NULL); if (! msgp) { cf_warning_digest(AS_RW, &tr->keyd, "{%s} UDF failed to make response msg ", ns->name); if (particle_buf != stack_particle) { cf_free(particle_buf); } return -1; } // Stash the message, to be sent later. db->buf = msgp; db->is_stack = false; db->alloc_sz = msg_sz; db->used_sz = msg_sz; } else { single_transaction_response(tr, ns, NULL, &bin, 1, tr->generation, tr->void_time, NULL, NULL); } if (particle_buf != stack_particle) { cf_free(particle_buf); } return 0; }