/* * Function based on the UDF result and the result of UDF call along * with the optype information update the UDF stats and LDT stats. * * Parameter: * op: execute optype * is_success : In case the UDF operation was successful * ret : return value of UDF execution * * Returns: nothing */ void udf_rw_update_stats(as_namespace *ns, udf_optype op, int ret, bool is_success) { if (UDF_OP_IS_LDT(op)) { if (UDF_OP_IS_READ(op)) cf_atomic_int_incr(&ns->ldt_read_reqs); else if (UDF_OP_IS_DELETE(op)) cf_atomic_int_incr(&ns->ldt_delete_reqs); else if (UDF_OP_IS_WRITE (op)) cf_atomic_int_incr(&ns->ldt_write_reqs); if (ret == 0) { if (is_success) { if (UDF_OP_IS_READ(op)) cf_atomic_int_incr(&ns->ldt_read_success); else if (UDF_OP_IS_DELETE(op)) cf_atomic_int_incr(&ns->ldt_delete_success); else if (UDF_OP_IS_WRITE (op)) cf_atomic_int_incr(&ns->ldt_write_success); } else { cf_atomic_int_incr(&ns->ldt_errs); } } else { cf_atomic_int_incr(&g_config.udf_lua_errs); } } else { if (UDF_OP_IS_READ(op)) cf_atomic_int_incr(&g_config.udf_read_reqs); else if (UDF_OP_IS_DELETE(op)) cf_atomic_int_incr(&g_config.udf_delete_reqs); else if (UDF_OP_IS_WRITE (op)) cf_atomic_int_incr(&g_config.udf_write_reqs); if (ret == 0) { if (is_success) { if (UDF_OP_IS_READ(op)) cf_atomic_int_incr(&g_config.udf_read_success); else if (UDF_OP_IS_DELETE(op)) cf_atomic_int_incr(&g_config.udf_delete_success); else if (UDF_OP_IS_WRITE (op)) cf_atomic_int_incr(&g_config.udf_write_success); } else { if (UDF_OP_IS_READ(op)) cf_atomic_int_incr(&g_config.udf_read_errs_other); else if (UDF_OP_IS_DELETE(op)) cf_atomic_int_incr(&g_config.udf_delete_errs_other); else if (UDF_OP_IS_WRITE (op)) cf_atomic_int_incr(&g_config.udf_write_errs_other); } } else { cf_info(AS_UDF,"lua error, ret:%d",ret); cf_atomic_int_incr(&g_config.udf_lua_errs); } } }
/* * Write the record to the storage in case there are write and closes the * record and frees up the stuff. With the pickled buf for each udf_record * it create single pickled buf for the entire LDT to be sent to the remote * for replica. * * Parameter: * lrecord : LDT record to operate on * pickled_* (out) to be populated is null if there was delete * lrecord_op (out) is set properly for the entire ldt * set_id : Set id for record. Passed for delete operation. * * Returns: 0 on success * otherwise on failure */ static int rw_finish(ldt_record *lrecord, write_request *wr, udf_optype * lrecord_op, uint16_t set_id) { int subrec_count = 0; udf_optype h_urecord_op = UDF_OPTYPE_READ; *lrecord_op = UDF_OPTYPE_READ; udf_record *h_urecord = as_rec_source(lrecord->h_urec); bool is_ldt = false; int ret = 0; getop(h_urecord, &h_urecord_op); if (h_urecord_op == UDF_OPTYPE_DELETE) { post_processing(h_urecord, &h_urecord_op, set_id); wr->pickled_buf = NULL; wr->pickled_sz = 0; as_rec_props_clear(&wr->pickled_rec_props); *lrecord_op = UDF_OPTYPE_DELETE; } else { if (h_urecord_op == UDF_OPTYPE_WRITE) { *lrecord_op = UDF_OPTYPE_WRITE; } FOR_EACH_SUBRECORD(i, j, lrecord) { udf_optype c_urecord_op = UDF_OPTYPE_READ; udf_record *c_urecord = &lrecord->chunk[i].slots[j].c_urecord; getop(c_urecord, &c_urecord_op); if (UDF_OP_IS_WRITE(c_urecord_op)) { is_ldt = true; subrec_count++; } post_processing(c_urecord, &c_urecord_op, set_id); } // Process the parent record in the end .. this is to make sure // the lock is held till the end. post_processing(h_urecord, &h_urecord_op, set_id); if (is_ldt) { // Create the multiop pickled buf for thr_rw.c ret = as_ldt_record_pickle(lrecord, &wr->pickled_buf, &wr->pickled_sz); FOR_EACH_SUBRECORD(i, j, lrecord) { udf_record *c_urecord = &lrecord->chunk[i].slots[j].c_urecord; // Cleanup in case pickle code bailed out // 1. either because this single node run no replica // 2. failed to pack stuff up. udf_record_cleanup(c_urecord, true); } } else {
/* Internal Function: Does the post processing for the UDF record after the * UDF execution. Does the following: * 1. Record is closed * 2. urecord_op is updated to delete in case there is no bin left in it. * 3. record->pickled_buf is populated before the record is close in case * it was write operation * 4. UDF updates cache is cleared * * Returns: Nothing * * Parameters: urecord - UDF record to operate on * urecord_op (out) - Populated with the optype */ void udf_rw_post_processing(udf_record *urecord, udf_optype *urecord_op, uint16_t set_id) { as_storage_rd *rd = urecord->rd; as_transaction *tr = urecord->tr; as_index_ref *r_ref = urecord->r_ref; // INIT urecord->pickled_buf = NULL; urecord->pickled_sz = 0; urecord->pickled_void_time = 0; as_rec_props_clear(&urecord->pickled_rec_props); bool udf_xdr_ship_op = false; // TODO: optimize not to allocate buffer if it is single // node cluster. No remote to send data to // Check if UDF has updates. if (urecord->flag & UDF_RECORD_FLAG_HAS_UPDATES) { // Check if the record is not deleted after an update if ( urecord->flag & UDF_RECORD_FLAG_OPEN) { *urecord_op = UDF_OPTYPE_WRITE; udf_xdr_ship_op = true; } else { // If the record has updates and it is not open, // and if it pre-existed it's an update followed by a delete. if ( urecord->flag & UDF_RECORD_FLAG_PREEXISTS) { *urecord_op = UDF_OPTYPE_DELETE; udf_xdr_ship_op = true; } // If the record did not pre-exist and is updated // and it is not open, then it is create followed by // delete essentially no_op. else { *urecord_op = UDF_OPTYPE_NONE; } } } else if ((urecord->flag & UDF_RECORD_FLAG_PREEXISTS) && !(urecord->flag & UDF_RECORD_FLAG_OPEN)) { *urecord_op = UDF_OPTYPE_DELETE; udf_xdr_ship_op = true; } else { *urecord_op = UDF_OPTYPE_READ; } cf_detail(AS_UDF, "FINISH working with LDT Record %p %p %p %p %d", &urecord, urecord->tr, urecord->r_ref, urecord->rd, (urecord->flag & UDF_RECORD_FLAG_STORAGE_OPEN)); // If there exists a record reference but no bin of the record is in use, // delete the record. remove from the tree. Only LDT_RECORD here not needed // for LDT_SUBRECORD (only do it if requested by UDF). All the SUBRECORD of // removed LDT_RECORD will be lazily cleaned up by defrag. if (!(urecord->flag & UDF_RECORD_FLAG_IS_SUBRECORD) && urecord->flag & UDF_RECORD_FLAG_OPEN && !as_bin_inuse_has(rd)) { as_index_delete(tr->rsv.tree, &tr->keyd); urecord->starting_memory_bytes = 0; *urecord_op = UDF_OPTYPE_DELETE; udf_xdr_ship_op = true; } else if (*urecord_op == UDF_OPTYPE_WRITE) { cf_detail(AS_UDF, "Committing Changes %"PRIx64" n_bins %d", rd->keyd, as_bin_get_n_bins(r_ref->r, rd)); 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); } write_local_post_processing(tr, tr->rsv.ns, NULL, &urecord->pickled_buf, &urecord->pickled_sz, &urecord->pickled_void_time, &urecord->pickled_rec_props, true/*increment_generation*/, NULL, r_ref->r, rd, urecord->starting_memory_bytes); // Now ok to accommodate a new stored key... if (! as_index_is_flag_set(r_ref->r, AS_INDEX_FLAG_KEY_STORED) && rd->key) { if (rd->ns->storage_data_in_memory) { as_record_allocate_key(r_ref->r, rd->key, rd->key_size); } as_index_set_flags(r_ref->r, AS_INDEX_FLAG_KEY_STORED); } // ... or drop a stored key. else if (as_index_is_flag_set(r_ref->r, AS_INDEX_FLAG_KEY_STORED) && ! rd->key) { if (rd->ns->storage_data_in_memory) { as_record_remove_key(r_ref->r); } as_index_clear_flags(r_ref->r, AS_INDEX_FLAG_KEY_STORED); } } // Collect the record information (for XDR) before closing the record as_generation generation = 0; if (urecord->flag & UDF_RECORD_FLAG_OPEN) { generation = r_ref->r->generation; set_id = as_index_get_set_id(r_ref->r); } // Close the record for all the cases udf_record_close(urecord, false); // Write to XDR pipe after closing the record, in order to release the record lock as // early as possible. if (udf_xdr_ship_op == true) { if (UDF_OP_IS_WRITE(*urecord_op)) { cf_detail(AS_UDF, "UDF write shipping for key %" PRIx64, tr->keyd); xdr_write(tr->rsv.ns, tr->keyd, generation, 0, false, set_id); } else if (UDF_OP_IS_DELETE(*urecord_op)) { cf_detail(AS_UDF, "UDF delete shipping for key %" PRIx64, tr->keyd); xdr_write(tr->rsv.ns, tr->keyd, generation, 0, true, set_id); } } // Replication happens when the main record replicates if (urecord->particle_data) { cf_free(urecord->particle_data); urecord->particle_data = 0; } udf_record_cache_free(urecord); }
/* Internal Function: Does the post processing for the UDF record after the * UDF execution. Does the following: * 1. Record is closed * 2. urecord_op is updated to delete in case there is no bin left in it. * 3. record->pickled_buf is populated before the record is close in case * it was write operation * 4. UDF updates cache is cleared * * Returns: Nothing * * Parameters: urecord - UDF record to operate on * urecord_op (out) - Populated with the optype */ static void post_processing(udf_record *urecord, udf_optype *urecord_op, uint16_t set_id) { as_storage_rd *rd = urecord->rd; as_transaction *tr = urecord->tr; as_index_ref *r_ref = urecord->r_ref; // INIT urecord->pickled_buf = NULL; urecord->pickled_sz = 0; as_rec_props_clear(&urecord->pickled_rec_props); bool udf_xdr_ship_op = false; getop(urecord, urecord_op); if (UDF_OP_IS_DELETE(*urecord_op) || UDF_OP_IS_WRITE(*urecord_op)) { udf_xdr_ship_op = true; } cf_detail(AS_UDF, "FINISH working with LDT Record %p %p %p %p %d", &urecord, urecord->tr, urecord->r_ref, urecord->rd, (urecord->flag & UDF_RECORD_FLAG_STORAGE_OPEN)); // If there exists a record reference but no bin of the record is in use, // delete the record. remove from the tree. Only LDT_RECORD here not needed // for LDT_SUBRECORD (only do it if requested by UDF). All the SUBRECORD of // removed LDT_RECORD will be lazily cleaned up by defrag. if (udf_zero_bins_left(urecord)) { as_transaction *tr = urecord->tr; as_index_delete(tr->rsv.tree, &tr->keyd); urecord->starting_memory_bytes = 0; *urecord_op = UDF_OPTYPE_DELETE; } else if (*urecord_op == UDF_OPTYPE_WRITE) { cf_detail_digest(AS_UDF, &rd->keyd, "Committing Changes n_bins %d", as_bin_get_n_bins(r_ref->r, rd)); 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); } write_udf_post_processing(tr, rd, &urecord->pickled_buf, &urecord->pickled_sz, &urecord->pickled_rec_props, urecord->starting_memory_bytes); // Now ok to accommodate a new stored key... if (! as_index_is_flag_set(r_ref->r, AS_INDEX_FLAG_KEY_STORED) && rd->key) { if (rd->ns->storage_data_in_memory) { as_record_allocate_key(r_ref->r, rd->key, rd->key_size); } as_index_set_flags(r_ref->r, AS_INDEX_FLAG_KEY_STORED); } // ... or drop a stored key. else if (as_index_is_flag_set(r_ref->r, AS_INDEX_FLAG_KEY_STORED) && ! rd->key) { if (rd->ns->storage_data_in_memory) { as_record_remove_key(r_ref->r); } as_index_clear_flags(r_ref->r, AS_INDEX_FLAG_KEY_STORED); } } // Collect the record information (for XDR) before closing the record as_generation generation = 0; if (urecord->flag & UDF_RECORD_FLAG_OPEN) { generation = r_ref->r->generation; set_id = as_index_get_set_id(r_ref->r); } urecord->op = *urecord_op; // Close the record for all the cases udf_record_close(urecord); // Write to XDR pipe after closing the record, in order to release the record lock as // early as possible. if (udf_xdr_ship_op == true) { if (UDF_OP_IS_WRITE(*urecord_op)) { cf_detail(AS_UDF, "UDF write shipping for key %" PRIx64, tr->keyd); xdr_write(tr->rsv.ns, tr->keyd, generation, 0, false, set_id); } else if (UDF_OP_IS_DELETE(*urecord_op)) { cf_detail(AS_UDF, "UDF delete shipping for key %" PRIx64, tr->keyd); xdr_write(tr->rsv.ns, tr->keyd, generation, 0, true, set_id); } } }