Transaction::pointer Transaction::load (uint256 const& id) { std::string sql = "SELECT LedgerSeq,Status,RawTxn " "FROM Transactions WHERE TransID='"; sql.append (to_string (id)); sql.append ("';"); boost::optional<std::uint64_t> ledgerSeq; boost::optional<std::string> status; Blob rawTxn; { auto db = getApp().getTxnDB ().checkoutDb (); soci::blob sociRawTxnBlob (*db); soci::indicator rti; *db << sql, soci::into (ledgerSeq), soci::into (status), soci::into (sociRawTxnBlob, rti); if (!db->got_data () || rti != soci::i_ok) return {}; convert(sociRawTxnBlob, rawTxn); } return Transaction::transactionFromSQL ( ledgerSeq, status, rawTxn, Validate::YES); }
/// Retrieve the data for a given namespace and key. Store::Status MemcachedStore::get_data(const std::string& table, const std::string& key, std::string& data, uint64_t& cas, SAS::TrailId trail) { Store::Status status = Store::Status::OK; // Construct the fully qualified key. std::string fqkey = table + "\\\\" + key; const char* key_ptr = fqkey.data(); const size_t key_len = fqkey.length(); const std::vector<memcached_st*>& replicas = get_replicas(fqkey, Op::READ); if (trail != 0) { SAS::Event start(trail, SASEvent::MEMCACHED_GET_START, 0); start.add_var_param(fqkey); SAS::report_event(start); } LOG_DEBUG("%d read replicas for key %s", replicas.size(), fqkey.c_str()); // Read from all replicas until we get a positive result. memcached_return_t rc = MEMCACHED_ERROR; bool active_not_found = false; size_t failed_replicas = 0; size_t ii; // If we only have one replica, we should try it twice - // libmemcached won't notice a dropped TCP connection until it tries // to make a request on it, and will fail the request then // reconnect, so the second attempt could still work. size_t attempts = (replicas.size() == 1) ? 2 : replicas.size(); for (ii = 0; ii < attempts; ++ii) { size_t replica_idx; if ((replicas.size() == 1) && (ii == 1)) { if (rc != MEMCACHED_CONNECTION_FAILURE) { // This is a legitimate error, not a server failure, so we // shouldn't retry. break; } replica_idx = 0; LOG_WARNING("Failed to read from sole memcached replica: retrying once"); } else { replica_idx = ii; } // We must use memcached_mget because memcached_get does not retrieve CAS // values. LOG_DEBUG("Attempt to read from replica %d (connection %p)", replica_idx, replicas[replica_idx]); rc = memcached_mget(replicas[replica_idx], &key_ptr, &key_len, 1); if (memcached_success(rc)) { // memcached_mget command was successful, so retrieve the result. LOG_DEBUG("Fetch result"); memcached_result_st result; memcached_result_create(replicas[replica_idx], &result); memcached_fetch_result(replicas[replica_idx], &result, &rc); if (memcached_success(rc)) { // Found a record, so exit the read loop. LOG_DEBUG("Found record on replica %d", replica_idx); data.assign(memcached_result_value(&result), memcached_result_length(&result)); cas = (active_not_found) ? 0 : memcached_result_cas(&result); // std::string::assign copies its arguments when used with a // char*, so this is safe. memcached_result_free(&result); break; } else { // Free the result and continue the read loop. memcached_result_free(&result); } } if (rc == MEMCACHED_NOTFOUND) { // Failed to find a record on an active replica. Flag this so if we do // find data on a later replica we can reset the cas value returned to // zero to ensure a subsequent write will succeed. LOG_DEBUG("Read for %s on replica %d returned NOTFOUND", fqkey.c_str(), replica_idx); active_not_found = true; } else { // Error from this node, so consider it inactive. LOG_DEBUG("Read for %s on replica %d returned error %d (%s)", fqkey.c_str(), replica_idx, rc, memcached_strerror(replicas[replica_idx], rc)); ++failed_replicas; } } if (memcached_success(rc)) { if (trail != 0) { SAS::Event got_data(trail, SASEvent::MEMCACHED_GET_SUCCESS, 0); got_data.add_var_param(fqkey); got_data.add_var_param(data); got_data.add_static_param(cas); SAS::report_event(got_data); } // Return the data and CAS value. The CAS value is either set to the CAS // value from the result, or zero if an earlier active replica returned // NOT_FOUND. This ensures that a subsequent set operation will succeed // on the earlier active replica. LOG_DEBUG("Read %d bytes from table %s key %s, CAS = %ld", data.length(), table.c_str(), key.c_str(), cas); } else if (failed_replicas < replicas.size()) { // At least one replica returned NOT_FOUND. if (trail != 0) { SAS::Event not_found(trail, SASEvent::MEMCACHED_GET_NOT_FOUND, 0); not_found.add_var_param(fqkey); SAS::report_event(not_found); } LOG_DEBUG("At least one replica returned not found, so return NOT_FOUND"); status = Store::Status::NOT_FOUND; } else { // All replicas returned an error, so log the error and return the // failure. if (trail != 0) { SAS::Event err(trail, SASEvent::MEMCACHED_GET_ERROR, 0); err.add_var_param(fqkey); SAS::report_event(err); } LOG_ERROR("Failed to read data for %s from %d replicas", fqkey.c_str(), replicas.size()); status = Store::Status::ERROR; } return status; }
/// Retrieve the data for a given namespace and key. Store::Status BaseMemcachedStore::get_data(const std::string& table, const std::string& key, std::string& data, uint64_t& cas, SAS::TrailId trail) { Store::Status status; // Construct the fully qualified key. std::string fqkey = table + "\\\\" + key; const char* key_ptr = fqkey.data(); const size_t key_len = fqkey.length(); int vbucket = vbucket_for_key(fqkey); const std::vector<memcached_st*>& replicas = get_replicas(vbucket, Op::READ); if (trail != 0) { SAS::Event start(trail, SASEvent::MEMCACHED_GET_START, 0); start.add_var_param(fqkey); SAS::report_event(start); } TRC_DEBUG("%d read replicas for key %s", replicas.size(), fqkey.c_str()); // Read from all replicas until we get a positive result. memcached_return_t rc = MEMCACHED_ERROR; bool active_not_found = false; size_t failed_replicas = 0; size_t ii; // If we only have one replica, we should try it twice - // libmemcached won't notice a dropped TCP connection until it tries // to make a request on it, and will fail the request then // reconnect, so the second attempt could still work. size_t attempts = (replicas.size() == 1) ? 2 : replicas.size(); for (ii = 0; ii < attempts; ++ii) { size_t replica_idx; if ((replicas.size() == 1) && (ii == 1)) { if (rc != MEMCACHED_CONNECTION_FAILURE) { // This is a legitimate error, not a server failure, so we // shouldn't retry. break; } replica_idx = 0; TRC_WARNING("Failed to read from sole memcached replica: retrying once"); } else { replica_idx = ii; } TRC_DEBUG("Attempt to read from replica %d (connection %p)", replica_idx, replicas[replica_idx]); rc = get_from_replica(replicas[replica_idx], key_ptr, key_len, data, cas); if (memcached_success(rc)) { // Got data back from this replica. Don't try any more. TRC_DEBUG("Read for %s on replica %d returned SUCCESS", fqkey.c_str(), replica_idx); break; } else if (rc == MEMCACHED_NOTFOUND) { // Failed to find a record on an active replica. Flag this so if we do // find data on a later replica we can reset the cas value returned to // zero to ensure a subsequent write will succeed. TRC_DEBUG("Read for %s on replica %d returned NOTFOUND", fqkey.c_str(), replica_idx); active_not_found = true; } else { // Error from this node, so consider it inactive. TRC_DEBUG("Read for %s on replica %d returned error %d (%s)", fqkey.c_str(), replica_idx, rc, memcached_strerror(replicas[replica_idx], rc)); ++failed_replicas; } } if (memcached_success(rc)) { if (data != TOMBSTONE) { if (trail != 0) { SAS::Event got_data(trail, SASEvent::MEMCACHED_GET_SUCCESS, 0); got_data.add_var_param(fqkey); got_data.add_var_param(data); got_data.add_static_param(cas); SAS::report_event(got_data); } // Return the data and CAS value. The CAS value is either set to the CAS // value from the result, or zero if an earlier active replica returned // NOT_FOUND. This ensures that a subsequent set operation will succeed // on the earlier active replica. if (active_not_found) { cas = 0; } TRC_DEBUG("Read %d bytes from table %s key %s, CAS = %ld", data.length(), table.c_str(), key.c_str(), cas); status = Store::OK; } else { if (trail != 0) { SAS::Event got_tombstone(trail, SASEvent::MEMCACHED_GET_TOMBSTONE, 0); got_tombstone.add_var_param(fqkey); got_tombstone.add_static_param(cas); SAS::report_event(got_tombstone); } // We have read a tombstone. Return NOT_FOUND to the caller, and also // zero out the CAS (returning a zero CAS makes the interface cleaner). TRC_DEBUG("Read tombstone from table %s key %s, CAS = %ld", table.c_str(), key.c_str(), cas); cas = 0; status = Store::NOT_FOUND; } // Regardless of whether we got a tombstone, the vbucket is alive. update_vbucket_comm_state(vbucket, OK); if (_comm_monitor) { _comm_monitor->inform_success(); } } else if (failed_replicas < replicas.size()) { // At least one replica returned NOT_FOUND. if (trail != 0) { SAS::Event not_found(trail, SASEvent::MEMCACHED_GET_NOT_FOUND, 0); not_found.add_var_param(fqkey); SAS::report_event(not_found); } TRC_DEBUG("At least one replica returned not found, so return NOT_FOUND"); status = Store::Status::NOT_FOUND; update_vbucket_comm_state(vbucket, OK); if (_comm_monitor) { _comm_monitor->inform_success(); } } else { // All replicas returned an error, so log the error and return the // failure. if (trail != 0) { SAS::Event err(trail, SASEvent::MEMCACHED_GET_ERROR, 0); err.add_var_param(fqkey); SAS::report_event(err); } TRC_ERROR("Failed to read data for %s from %d replicas", fqkey.c_str(), replicas.size()); status = Store::Status::ERROR; update_vbucket_comm_state(vbucket, FAILED); if (_comm_monitor) { _comm_monitor->inform_failure(); } } return status; }