// Looked up the message in the store. Time to send the response value back to // the requester. The CF_BYTEARRAY is handed off in this case. If you want to // keep a reference, then keep the reference yourself. int as_proxy_send_response(cf_node dst, msg *m, uint32_t result_code, uint32_t generation, uint32_t void_time, as_msg_op **ops, as_bin **bins, uint16_t bin_count, as_namespace *ns, uint64_t trid, const char *setname) { uint32_t tid; msg_get_uint32(m, PROXY_FIELD_TID, &tid); #ifdef DEBUG cf_debug(AS_PROXY, "proxy send response: message %p bytearray %p tid %d", m, result_code, tid); #endif msg_reset(m); msg_set_uint32(m, PROXY_FIELD_OP, PROXY_OP_RESPONSE); msg_set_uint32(m, PROXY_FIELD_TID, tid); size_t msg_sz = 0; cl_msg * msgp = as_msg_make_response_msg(result_code, generation, void_time, ops, bins, bin_count, ns, 0, &msg_sz, trid, setname); msg_set_buf(m, PROXY_FIELD_AS_PROTO, (byte *) msgp, msg_sz, MSG_SET_HANDOFF_MALLOC); int rv = as_fabric_send(dst, m, AS_FABRIC_PRIORITY_MEDIUM); if (rv != 0) { cf_debug(AS_PROXY, "sending proxy response: fabric send err %d, catch you on the retry", rv); as_fabric_msg_put(m); } return 0; } // end as_proxy_send_response()
int as_msg_send_reply(as_file_handle *fd_h, uint32_t result_code, uint32_t generation, uint32_t void_time, as_msg_op **ops, as_bin **bins, uint16_t bin_count, as_namespace *ns, uint *written_sz, uint64_t trid, const char *setname) { int rv = 0; // most cases are small messages - try to stack alloc if we can byte fb[MSG_STACK_BUFFER_SZ]; size_t msg_sz = sizeof(fb); // memset(fb,0xff,msg_sz); // helpful to see what you might not be setting uint8_t *msgp = (uint8_t *) as_msg_make_response_msg( result_code, generation, void_time, ops, bins, bin_count, ns, (cl_msg *)fb, &msg_sz, trid, setname); if (!msgp) return(-1); if (fd_h->fd == 0) { cf_warning(AS_PROTO, "write to fd 0 internal error"); cf_crash(AS_PROTO, "send reply: can't write to fd 0"); } // cf_detail(AS_PROTO, "write fd %d",fd); size_t pos = 0; while (pos < msg_sz) { int rv = send(fd_h->fd, msgp + pos, msg_sz - pos, MSG_NOSIGNAL); if (rv > 0) { pos += rv; } else if (rv < 0) { if (errno != EWOULDBLOCK) { // common message when a client aborts cf_debug(AS_PROTO, "protocol write fail: fd %d sz %zd pos %zd rv %d errno %d", fd_h->fd, msg_sz, pos, rv, errno); as_end_of_transaction_force_close(fd_h); rv = -1; goto Exit; } usleep(1); // Yield } else { cf_info(AS_PROTO, "protocol write fail zero return: fd %d sz %d pos %d ", fd_h->fd, msg_sz, pos); as_end_of_transaction_force_close(fd_h); rv = -1; goto Exit; } } // good for stats as a higher layer if (written_sz) *written_sz = msg_sz; as_end_of_transaction_ok(fd_h); Exit: if ((uint8_t *)msgp != fb) cf_free(msgp); return(rv); }
/** * 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); }
transaction_status read_local(as_transaction* tr) { as_msg* m = &tr->msgp->msg; as_namespace* ns = tr->rsv.ns; as_index_ref r_ref; if (as_record_get(tr->rsv.tree, &tr->keyd, &r_ref) != 0) { read_local_done(tr, NULL, NULL, AS_ERR_NOT_FOUND); return TRANS_DONE_ERROR; } as_record* r = r_ref.r; // Check if it's an expired or truncated record. if (as_record_is_doomed(r, ns)) { read_local_done(tr, &r_ref, NULL, AS_ERR_NOT_FOUND); return TRANS_DONE_ERROR; } int result = repl_state_check(r, tr); if (result != 0) { if (result == -3) { read_local_done(tr, &r_ref, NULL, AS_ERR_UNAVAILABLE); return TRANS_DONE_ERROR; } // No response sent to origin. as_record_done(&r_ref, ns); return result == 1 ? TRANS_IN_PROGRESS : TRANS_WAITING; } // Check if it's a tombstone. if (! as_record_is_live(r)) { read_local_done(tr, &r_ref, NULL, AS_ERR_NOT_FOUND); return TRANS_DONE_ERROR; } as_storage_rd rd; as_storage_record_open(ns, r, &rd); // If configuration permits, allow reads to use page cache. rd.read_page_cache = ns->storage_read_page_cache; // Check the key if required. // Note - for data-not-in-memory "exists" ops, key check is expensive! if (as_transaction_has_key(tr) && as_storage_record_get_key(&rd) && ! check_msg_key(m, &rd)) { read_local_done(tr, &r_ref, &rd, AS_ERR_KEY_MISMATCH); return TRANS_DONE_ERROR; } if ((m->info1 & AS_MSG_INFO1_GET_NO_BINS) != 0) { tr->generation = r->generation; tr->void_time = r->void_time; tr->last_update_time = r->last_update_time; read_local_done(tr, &r_ref, &rd, AS_OK); return TRANS_DONE_SUCCESS; } if ((result = as_storage_rd_load_n_bins(&rd)) < 0) { cf_warning_digest(AS_RW, &tr->keyd, "{%s} read_local: failed as_storage_rd_load_n_bins() ", ns->name); read_local_done(tr, &r_ref, &rd, -result); return TRANS_DONE_ERROR; } as_bin stack_bins[ns->storage_data_in_memory ? 0 : rd.n_bins]; if ((result = as_storage_rd_load_bins(&rd, stack_bins)) < 0) { cf_warning_digest(AS_RW, &tr->keyd, "{%s} read_local: failed as_storage_rd_load_bins() ", ns->name); read_local_done(tr, &r_ref, &rd, -result); return TRANS_DONE_ERROR; } if (! as_bin_inuse_has(&rd)) { cf_warning_digest(AS_RW, &tr->keyd, "{%s} read_local: found record with no bins ", ns->name); read_local_done(tr, &r_ref, &rd, AS_ERR_UNKNOWN); return TRANS_DONE_ERROR; } uint32_t bin_count = (m->info1 & AS_MSG_INFO1_GET_ALL) != 0 ? rd.n_bins : m->n_ops; as_msg_op* ops[bin_count]; as_msg_op** p_ops = ops; as_bin* response_bins[bin_count]; uint16_t n_bins = 0; as_bin result_bins[bin_count]; uint32_t n_result_bins = 0; if ((m->info1 & AS_MSG_INFO1_GET_ALL) != 0) { p_ops = NULL; n_bins = rd.n_bins; as_bin_get_all_p(&rd, response_bins); } else { if (m->n_ops == 0) { cf_warning_digest(AS_RW, &tr->keyd, "{%s} read_local: bin op(s) expected, none present ", ns->name); read_local_done(tr, &r_ref, &rd, AS_ERR_PARAMETER); return TRANS_DONE_ERROR; } bool respond_all_ops = (m->info2 & AS_MSG_INFO2_RESPOND_ALL_OPS) != 0; as_msg_op* op = 0; int n = 0; while ((op = as_msg_op_iterate(m, op, &n)) != NULL) { if (op->op == AS_MSG_OP_READ) { as_bin* b = as_bin_get_from_buf(&rd, op->name, op->name_sz); if (b || respond_all_ops) { ops[n_bins] = op; response_bins[n_bins++] = b; } } else if (op->op == AS_MSG_OP_CDT_READ) { as_bin* b = as_bin_get_from_buf(&rd, op->name, op->name_sz); if (b) { as_bin* rb = &result_bins[n_result_bins]; as_bin_set_empty(rb); if ((result = as_bin_cdt_read_from_client(b, op, rb)) < 0) { cf_warning_digest(AS_RW, &tr->keyd, "{%s} read_local: failed as_bin_cdt_read_from_client() ", ns->name); destroy_stack_bins(result_bins, n_result_bins); read_local_done(tr, &r_ref, &rd, -result); return TRANS_DONE_ERROR; } if (as_bin_inuse(rb)) { n_result_bins++; ops[n_bins] = op; response_bins[n_bins++] = rb; } else if (respond_all_ops) { ops[n_bins] = op; response_bins[n_bins++] = NULL; } } else if (respond_all_ops) { ops[n_bins] = op; response_bins[n_bins++] = NULL; } } else { cf_warning_digest(AS_RW, &tr->keyd, "{%s} read_local: unexpected bin op %u ", ns->name, op->op); destroy_stack_bins(result_bins, n_result_bins); read_local_done(tr, &r_ref, &rd, AS_ERR_PARAMETER); return TRANS_DONE_ERROR; } } } cf_dyn_buf_define_size(db, 16 * 1024); if (tr->origin != FROM_BATCH) { db.used_sz = db.alloc_sz; db.buf = (uint8_t*)as_msg_make_response_msg(tr->result_code, r->generation, r->void_time, p_ops, response_bins, n_bins, ns, (cl_msg*)dyn_bufdb, &db.used_sz, as_transaction_trid(tr)); db.is_stack = db.buf == dyn_bufdb; // Note - not bothering to correct alloc_sz if buf was allocated. } else { tr->generation = r->generation; tr->void_time = r->void_time; tr->last_update_time = r->last_update_time; // Since as_batch_add_result() constructs response directly in shared // buffer to avoid extra copies, can't use db. send_read_response(tr, p_ops, response_bins, n_bins, NULL); } destroy_stack_bins(result_bins, n_result_bins); as_storage_record_close(&rd); as_record_done(&r_ref, ns); // Now that we're not under the record lock, send the message we just built. if (db.used_sz != 0) { send_read_response(tr, NULL, NULL, 0, &db); cf_dyn_buf_free(&db); tr->from.proto_fd_h = NULL; } return TRANS_DONE_SUCCESS; }
/* 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; }