/** * Send failure notification of general UDF execution, but check for special * LDT errors and return specific Wire Protocol error codes for these cases: * (1) Record not found (2) * (2) LDT Collection item not found (125) * * All other errors get the generic 100 (UDF FAIL) code. */ static inline int send_udf_failure(udf_call *call, const as_string *s) { char *val = as_string_tostring(s); size_t vlen = as_string_len((as_string *)s); // TODO - make as_string_len() take const long error_code = ldt_get_error_code(val, vlen); if (error_code) { if (error_code == AS_PROTO_RESULT_FAIL_NOTFOUND || error_code == AS_PROTO_RESULT_FAIL_COLLECTION_ITEM_NOT_FOUND) { call->tr->result_code = error_code; // Send an "empty" response, with no failure bin. as_transaction * tr = call->tr; single_transaction_response(tr, tr->rsv.ns, NULL/*ops*/, NULL /*bin*/, 0 /*nbins*/, 0, 0, NULL, NULL); return 0; } } cf_debug(AS_UDF, "Non-special LDT or General UDF Error(%s)", (char *) val); call->tr->result_code = AS_PROTO_RESULT_FAIL_UDF_EXECUTION; return send_failure(call, as_string_toval(s)); }
/* Internal Function: Workhorse function to send response back to the client * after UDF execution. * * caller: * send_success * send_failure * * Assumption: The call should be setup properly pointing to the tr. * * Special Handling: If it is background scan udf job do not sent any * response to client * If it is scan job ...do not cleanup the fd it will * be done by the scan thread after scan is finished */ static int send_response(udf_call *call, const char *key, size_t klen, int vtype, void *val, size_t vlen) { as_transaction * tr = call->transaction; as_namespace * ns = tr->rsv.ns; uint32_t generation = tr->generation; uint sp_sz = 1024 * 16; uint32_t void_time = 0; uint written_sz = 0; bool keep_fd = false; as_bin stack_bin; as_bin * bin = &stack_bin; // space for the stack particles uint8_t stack_particle_buf[sp_sz]; uint8_t * sp_p = stack_particle_buf; if (call->udf_type == AS_SCAN_UDF_OP_BACKGROUND) { // If we are doing a background UDF scan, do not send any result back cf_detail(AS_UDF, "UDF: Background transaction, send no result back. " "Parent job id [%"PRIu64"]", ((tscan_job*)(tr->udata.req_udata))->tid); if(strncmp(key, "FAILURE", 8) == 0) { cf_atomic_int_incr(&((tscan_job*)(tr->udata.req_udata))->n_obj_udf_failed); } else if(strncmp(key, "SUCCESS", 8) == 0) { cf_atomic_int_incr(&((tscan_job*)(tr->udata.req_udata))->n_obj_udf_success); } return 0; } else if(call->udf_type == AS_SCAN_UDF_OP_UDF) { // Do not release fd now, scan will do it at the end of all internal // udf transactions cf_detail(AS_UDF, "UDF: Internal udf transaction, do not release fd"); keep_fd = true; } if (0 != make_send_bin(ns, bin, &sp_p, sp_sz, key, klen, vtype, val, vlen)) { return(-1); } // this is going to release the file descriptor if (keep_fd && tr->proto_fd_h) cf_rc_reserve(tr->proto_fd_h); single_transaction_response( tr, ns, NULL/*ops*/, &bin, 1, generation, void_time, &written_sz, NULL); // clean up. // TODO: check: is bin_inuse valid only when data_in_memory? // There must be another way to determine if the particle is used? if ( as_bin_inuse(bin) ) { as_particle_destroy(&stack_bin, ns->storage_data_in_memory); } if (sp_p != stack_particle_buf) { cf_free(sp_p); } return 0; } // end send_response()
/** * Send failure notification of general UDF execution, but check for special * LDT errors and return specific Wire Protocol error codes for these cases: * (1) Record not found (2) * (2) LDT Collection item not found (125) * * All other errors get the generic 100 (UDF FAIL) code. */ static inline int process_udf_failure(udf_call *call, const as_string *s, cf_dyn_buf *db) { char *val = as_string_tostring(s); size_t vlen = as_string_len((as_string *)s); // TODO - make as_string_len() take const long error_code = ldt_get_error_code(val, vlen); if (error_code) { if (error_code == AS_PROTO_RESULT_FAIL_NOTFOUND || error_code == AS_PROTO_RESULT_FAIL_COLLECTION_ITEM_NOT_FOUND) { call->tr->result_code = (uint8_t)error_code; // Send an "empty" response, with no failure bin. as_transaction * tr = call->tr; if (db) { size_t msg_sz = 0; uint8_t *msgp = (uint8_t *)as_msg_make_response_msg( tr->result_code, 0, 0, NULL, NULL, 0, tr->rsv.ns, NULL, &msg_sz, as_transaction_trid(tr), NULL); if (! msgp) { cf_warning_digest(AS_RW, &tr->keyd, "{%s} LDT UDF failed to make response msg ", tr->rsv.ns->name); 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, tr->rsv.ns, NULL/*ops*/, NULL /*bin*/, 0 /*nbins*/, 0, 0, NULL, NULL); } return 0; } } cf_debug(AS_UDF, "Non-special LDT or General UDF Error(%s)", (char *) val); call->tr->result_code = AS_PROTO_RESULT_FAIL_UDF_EXECUTION; return process_failure(call, as_string_toval(s), db); }
/* 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()
/** * Send failure notification of general UDF execution, but check for special * LDT errors and return specific Wire Protocol error codes for these cases: * (1) Record not found (2) * (2) LDR Collection item not found (125) * * All other errors get the generic 100 (UDF FAIL) code. * * Parse (Actually, probe) the error string, and if we see this pattern: * FileName:Line# 4digits:LDT-<Error String> * For example: * .../aerospike-lua-core/src/ldt/lib_llist.lua:982: 0002:LDT-Top Record Not Found * All UDF errors (LDT included), have the "filename:line# " prefix, and then * LDT errors follow that with a known pattern: * (4 digits, colon, LDT-<Error String>). * We will check the error string by looking for specific markers after the * the space that follows the filename:line#. If we see the markers, we will * parse the LDT error code and use that as the wire protocol error if it is * one of the special ones: * (1) "0002:LDT-Top Record Not Found" * (2) "0125:LDT-Item Not Found" */ static inline int send_udf_failure(udf_call *call, int vtype, void *val, size_t vlen) { // We need to do a quick look to see if this is an LDT error string. If it // is, then we'll look deeper. We start looking after the first space. char * charptr; char * valptr = (char *) val; long error_code; // Start with the space, if it exists, as the marker for where we start // looking for the LDT error contents. if ((charptr = strchr((const char *) val, ' ')) != NULL) { // We must be at least 10 chars from the end, so if we're less than that // we are obviously not looking at an LDT error. if (&charptr[9] < &valptr[vlen]) { if (memcmp(&charptr[5], ":LDT-", 5) == 0) { error_code = strtol(&charptr[1], NULL, 10); if (error_code == AS_PROTO_RESULT_FAIL_NOTFOUND || error_code == AS_PROTO_RESULT_FAIL_COLLECTION_ITEM_NOT_FOUND) { call->transaction->result_code = error_code; cf_debug(AS_UDF, "LDT Error: Code(%ld) String(%s)", error_code, (char *) val); // Send an "empty" response, with no failure bin. as_transaction * tr = call->transaction; single_transaction_response(tr, tr->rsv.ns, NULL/*ops*/, NULL /*bin*/, 0 /*nbins*/, 0, 0, NULL, NULL); return 0; } } } } cf_debug(AS_UDF, "Non-special LDT or General UDF Error(%s)", (char *) val); call->transaction->result_code = AS_PROTO_RESULT_FAIL_UDF_EXECUTION; return send_response(call, "FAILURE", 7, vtype, val, vlen); }
/* 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; }