mirror of
https://git.wownero.com/wownero/onion-wownero-blockchain-explorer.git
synced 2024-08-15 00:33:12 +00:00
Merge pull request #194 from moneroexamples/remove_caches
block and tx caches removed
This commit is contained in:
commit
69df5ef4b1
11 changed files with 87 additions and 880 deletions
|
@ -1,138 +0,0 @@
|
|||
#ifndef CACHE_HPP
|
||||
#define CACHE_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include "cache_policy.hpp"
|
||||
|
||||
namespace caches
|
||||
{
|
||||
|
||||
// Base class for caching algorithms
|
||||
template <typename Key, typename Value, typename Policy = NoCachePolicy<Key>>
|
||||
class fixed_sized_cache
|
||||
{
|
||||
public:
|
||||
|
||||
using iterator = typename std::unordered_map<Key, Value>::iterator;
|
||||
|
||||
using const_iterator =
|
||||
typename std::unordered_map<Key, Value>::const_iterator;
|
||||
|
||||
using operation_guard = typename std::lock_guard<std::mutex>;
|
||||
|
||||
fixed_sized_cache(
|
||||
size_t max_size,
|
||||
const Policy& policy = Policy())
|
||||
: max_cache_size{max_size},
|
||||
cache_policy(policy)
|
||||
{
|
||||
if (max_cache_size == 0)
|
||||
{
|
||||
max_cache_size = std::numeric_limits<size_t>::max();
|
||||
}
|
||||
}
|
||||
|
||||
void Put(const Key& key, const Value& value)
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
auto elem_it = FindElem(key);
|
||||
|
||||
if (elem_it == cache_items_map.end())
|
||||
{
|
||||
// add new element to the cache
|
||||
if (Size() + 1 > max_cache_size)
|
||||
{
|
||||
auto disp_candidate_key = cache_policy.ReplCandidate();
|
||||
|
||||
Erase(disp_candidate_key);
|
||||
}
|
||||
|
||||
Insert(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// update previous value
|
||||
Update(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool Contains(const Key& key)
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
auto elem_it = FindElem(key);
|
||||
return elem_it != cache_items_map.end();
|
||||
}
|
||||
|
||||
const Value& Get(const Key& key) const
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
auto elem_it = FindElem(key);
|
||||
|
||||
if (elem_it == cache_items_map.end())
|
||||
{
|
||||
throw std::range_error{"No such element in the cache"};
|
||||
}
|
||||
cache_policy.Touch(key);
|
||||
|
||||
return elem_it->second;
|
||||
}
|
||||
|
||||
const size_t Size() const
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
|
||||
return cache_items_map.size();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear()
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
|
||||
cache_policy.Clear();
|
||||
cache_items_map.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void Insert(const Key& key, const Value& value)
|
||||
{
|
||||
cache_policy.Insert(key);
|
||||
cache_items_map.emplace(std::make_pair(key, value));
|
||||
}
|
||||
|
||||
void Erase(const Key& key)
|
||||
{
|
||||
cache_policy.Erase(key);
|
||||
cache_items_map.erase(key);
|
||||
}
|
||||
|
||||
void Update(const Key& key, const Value& value)
|
||||
{
|
||||
cache_policy.Touch(key);
|
||||
cache_items_map[key] = value;
|
||||
}
|
||||
|
||||
const_iterator FindElem(const Key& key) const
|
||||
{
|
||||
return cache_items_map.find(key);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
std::unordered_map<Key, Value> cache_items_map;
|
||||
|
||||
mutable Policy cache_policy;
|
||||
mutable std::mutex safe_op;
|
||||
|
||||
size_t max_cache_size;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CACHE_HPP
|
|
@ -1,77 +0,0 @@
|
|||
#ifndef CACHE_POLICY_HPP
|
||||
#define CACHE_POLICY_HPP
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace caches
|
||||
{
|
||||
|
||||
template <typename Key>
|
||||
|
||||
class ICachePolicy
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~ICachePolicy() {}
|
||||
|
||||
// handle element insertion in a cache
|
||||
virtual void Insert(const Key& key) = 0;
|
||||
|
||||
// handle request to the key-element in a cache
|
||||
virtual void Touch(const Key& key) = 0;
|
||||
|
||||
// handle element deletion from a cache
|
||||
virtual void Erase(const Key& key) = 0;
|
||||
|
||||
// return a key of a replacement candidate
|
||||
virtual const Key& ReplCandidate() const = 0;
|
||||
|
||||
// clear the cache
|
||||
virtual void Clear() = 0;
|
||||
|
||||
};
|
||||
|
||||
template <typename Key>
|
||||
class NoCachePolicy : public ICachePolicy<Key>
|
||||
{
|
||||
public:
|
||||
|
||||
NoCachePolicy() = default;
|
||||
|
||||
~NoCachePolicy() override = default;
|
||||
|
||||
void Insert(const Key& key) override
|
||||
{
|
||||
key_storage.emplace(key);
|
||||
}
|
||||
|
||||
void Touch(const Key& key) override
|
||||
{
|
||||
// do not do anything
|
||||
}
|
||||
|
||||
void Erase(const Key& key) override
|
||||
{
|
||||
key_storage.erase(key);
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
const Key& ReplCandidate() const override
|
||||
{
|
||||
return *key_storage.crbegin();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear() override
|
||||
{
|
||||
key_storage.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::unordered_set<Key> key_storage;
|
||||
};
|
||||
|
||||
} // namespace caches
|
||||
|
||||
#endif // CACHE_POLICY_HPP
|
|
@ -1,53 +0,0 @@
|
|||
#ifndef FIFO_CACHE_POLICY_HPP
|
||||
#define FIFO_CACHE_POLICY_HPP
|
||||
|
||||
#include <list>
|
||||
#include "cache_policy.hpp"
|
||||
|
||||
namespace caches
|
||||
{
|
||||
|
||||
template <typename Key>
|
||||
class FIFOCachePolicy : public ICachePolicy<Key>
|
||||
{
|
||||
public:
|
||||
|
||||
FIFOCachePolicy() = default;
|
||||
~FIFOCachePolicy() = default;
|
||||
|
||||
void Insert(const Key& key) override
|
||||
{
|
||||
fifo_queue.emplace_front(key);
|
||||
}
|
||||
|
||||
// handle request to the key-element in a cache
|
||||
void Touch(const Key& key) override
|
||||
{
|
||||
// nothing to do here in the FIFO strategy
|
||||
}
|
||||
|
||||
// handle element deletion from a cache
|
||||
void Erase(const Key& key) override
|
||||
{
|
||||
fifo_queue.pop_back();
|
||||
}
|
||||
|
||||
// return a key of a replacement candidate
|
||||
const Key& ReplCandidate() const override
|
||||
{
|
||||
return fifo_queue.back();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear() override
|
||||
{
|
||||
fifo_queue.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::list<Key> fifo_queue;
|
||||
};
|
||||
} // namespace caches
|
||||
|
||||
#endif // FIFO_CACHE_POLICY_HPP
|
|
@ -1,76 +0,0 @@
|
|||
#ifndef LFU_CACHE_POLICY_HPP
|
||||
#define LFU_CACHE_POLICY_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include "cache_policy.hpp"
|
||||
|
||||
namespace caches
|
||||
{
|
||||
template <typename Key>
|
||||
class LFUCachePolicy : public ICachePolicy<Key>
|
||||
{
|
||||
public:
|
||||
|
||||
using lfu_iterator = typename std::multimap<std::size_t, Key>::iterator;
|
||||
|
||||
LFUCachePolicy() = default;
|
||||
|
||||
~LFUCachePolicy() override = default;
|
||||
|
||||
void Insert(const Key& key) override
|
||||
{
|
||||
constexpr std::size_t INIT_VAL = 1;
|
||||
|
||||
// all new value initialized with the frequency 1
|
||||
lfu_storage[key] = frequency_storage.emplace_hint(
|
||||
frequency_storage.cbegin(), INIT_VAL, key);
|
||||
}
|
||||
|
||||
void Touch(const Key& key) override
|
||||
{
|
||||
// get the previous frequency value of a key
|
||||
auto elem_for_update = lfu_storage[key];
|
||||
|
||||
auto updated_elem = std::make_pair(
|
||||
elem_for_update->first + 1, elem_for_update->second);
|
||||
|
||||
// update the previous value
|
||||
frequency_storage.erase(elem_for_update);
|
||||
|
||||
lfu_storage[key] = frequency_storage.emplace_hint(
|
||||
frequency_storage.cend(), std::move(updated_elem));
|
||||
}
|
||||
|
||||
void Erase(const Key& key) override
|
||||
{
|
||||
frequency_storage.erase(lfu_storage[key]);
|
||||
lfu_storage.erase(key);
|
||||
}
|
||||
|
||||
const Key& ReplCandidate() const override
|
||||
{
|
||||
// at the beginning of the frequency_storage we have the
|
||||
// least frequency used value
|
||||
return frequency_storage.cbegin()->second;
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear() override
|
||||
{
|
||||
frequency_storage.clear();
|
||||
lfu_storage.clear();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
std::multimap<std::size_t, Key> frequency_storage;
|
||||
|
||||
std::unordered_map<Key, lfu_iterator> lfu_storage;
|
||||
};
|
||||
} // namespace caches
|
||||
|
||||
#endif // LFU_CACHE_POLICY_HPP
|
|
@ -1,63 +0,0 @@
|
|||
#ifndef LRU_CACHE_POLICY_HPP
|
||||
#define LRU_CACHE_POLICY_HPP
|
||||
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include "cache_policy.hpp"
|
||||
|
||||
namespace caches
|
||||
{
|
||||
template <typename Key>
|
||||
class LRUCachePolicy : public ICachePolicy<Key>
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
using lru_iterator = typename std::list<Key>::const_iterator;
|
||||
|
||||
LRUCachePolicy() = default;
|
||||
~LRUCachePolicy() = default;
|
||||
|
||||
void Insert(const Key& key) override
|
||||
{
|
||||
lru_queue.emplace_front(key);
|
||||
key_finder[key] = lru_queue.cbegin();
|
||||
}
|
||||
|
||||
void Touch(const Key& key) override
|
||||
{
|
||||
// move the touched element at the beginning of the lru_queue
|
||||
lru_queue.splice(lru_queue.cbegin(), lru_queue, key_finder[key]);
|
||||
}
|
||||
|
||||
void Erase(const Key& key) override
|
||||
{
|
||||
// remove the least recently used element
|
||||
key_finder.erase(lru_queue.back());
|
||||
|
||||
lru_queue.pop_back();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
const Key& ReplCandidate() const override
|
||||
{
|
||||
return lru_queue.back();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear() override
|
||||
{
|
||||
lru_queue.clear();
|
||||
key_finder.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::list<Key> lru_queue;
|
||||
|
||||
std::unordered_map<Key, lru_iterator> key_finder;
|
||||
};
|
||||
|
||||
} // namespace caches
|
||||
|
||||
#endif // LRU_CACHE_POLICY_HPP
|
10
main.cpp
10
main.cpp
|
@ -41,6 +41,7 @@ struct jsonresponse: public crow::response
|
|||
int
|
||||
main(int ac, const char* av[])
|
||||
{
|
||||
|
||||
// get command line options
|
||||
xmreg::CmdLineOptions opts {ac, av};
|
||||
|
||||
|
@ -73,10 +74,7 @@ main(int ac, const char* av[])
|
|||
auto enable_mixin_details_opt = opts.get_option<bool>("enable-mixin-details");
|
||||
auto enable_json_api_opt = opts.get_option<bool>("enable-json-api");
|
||||
auto enable_as_hex_opt = opts.get_option<bool>("enable-as-hex");
|
||||
auto enable_tx_cache_opt = opts.get_option<bool>("enable-tx-cache");
|
||||
auto concurrency_opt = opts.get_option<size_t>("concurrency");
|
||||
auto enable_block_cache_opt = opts.get_option<bool>("enable-block-cache");
|
||||
auto show_cache_times_opt = opts.get_option<bool>("show-cache-times");
|
||||
auto enable_emission_monitor_opt = opts.get_option<bool>("enable-emission-monitor");
|
||||
|
||||
|
||||
|
@ -101,10 +99,7 @@ main(int ac, const char* av[])
|
|||
bool enable_mixin_details {*enable_mixin_details_opt};
|
||||
bool enable_json_api {*enable_json_api_opt};
|
||||
bool enable_as_hex {*enable_as_hex_opt};
|
||||
bool enable_tx_cache {*enable_tx_cache_opt};
|
||||
bool enable_block_cache {*enable_block_cache_opt};
|
||||
bool enable_emission_monitor {*enable_emission_monitor_opt};
|
||||
bool show_cache_times {*show_cache_times_opt};
|
||||
|
||||
|
||||
// set monero log output level
|
||||
|
@ -279,9 +274,6 @@ main(int ac, const char* av[])
|
|||
enable_output_key_checker,
|
||||
enable_autorefresh_option,
|
||||
enable_mixin_details,
|
||||
enable_tx_cache,
|
||||
enable_block_cache,
|
||||
show_cache_times,
|
||||
no_blocks_on_index,
|
||||
mempool_info_timeout,
|
||||
*testnet_url,
|
||||
|
|
|
@ -37,12 +37,6 @@ namespace xmreg
|
|||
"enable outputs key file checker")
|
||||
("enable-json-api", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable JSON REST api")
|
||||
("enable-tx-cache", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable caching of transaction details")
|
||||
("show-cache-times", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"show times of getting data from cache vs no cache")
|
||||
("enable-block-cache", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable caching of block details")
|
||||
("enable-as-hex", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable links to provide hex represtations of a tx and a block")
|
||||
("enable-autorefresh-option", value<bool>()->default_value(false)->implicit_value(true),
|
||||
|
|
|
@ -137,13 +137,31 @@ MicroCore::get_tx(const crypto::hash& tx_hash, transaction& tx)
|
|||
if (m_blockchain_storage.have_tx(tx_hash))
|
||||
{
|
||||
// get transaction with given hash
|
||||
tx = m_blockchain_storage.get_db().get_tx(tx_hash);
|
||||
try
|
||||
{
|
||||
tx = m_blockchain_storage.get_db().get_tx(tx_hash);
|
||||
}
|
||||
catch (TX_DNE const& e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// coinbase txs are not considered pruned
|
||||
tx = m_blockchain_storage.get_db().get_pruned_tx(tx_hash);
|
||||
return true;
|
||||
}
|
||||
catch (TX_DNE const& e)
|
||||
{
|
||||
cerr << "MicroCore::get_tx: " << e.what() << endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "MicroCore::get_tx tx does not exist in blockchain: " << tx_hash << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
|
|
497
src/page.h
497
src/page.h
|
@ -31,9 +31,6 @@
|
|||
|
||||
#include "../ext/json.hpp"
|
||||
|
||||
#include "../ext/vpetrigocaches/cache.hpp"
|
||||
#include "../ext/vpetrigocaches/lru_cache_policy.hpp"
|
||||
#include "../ext/vpetrigocaches/fifo_cache_policy.hpp"
|
||||
#include "../ext/mstch/src/visitor/render_node.hpp"
|
||||
|
||||
extern "C" uint64_t me_rx_seedheight(const uint64_t height);
|
||||
|
@ -85,31 +82,6 @@ extern __thread randomx_vm *rx_vm;
|
|||
MAKE_ONIONEXPLORER_RPC_VERSION(ONIONEXPLORER_RPC_VERSION_MAJOR, ONIONEXPLORER_RPC_VERSION_MINOR)
|
||||
|
||||
|
||||
// basic info about tx to be stored in cashe.
|
||||
// we need to store block_no and timestamp,
|
||||
// as this time and number of confirmation needs
|
||||
// to be updated between requests. Just cant
|
||||
// get it from cash, as it will be old very soon
|
||||
struct tx_info_cache
|
||||
{
|
||||
uint64_t block_no;
|
||||
uint64_t timestamp;
|
||||
mstch::map tx_map;
|
||||
|
||||
// custom key for use in cache.
|
||||
// cache uses unordeded map for keys
|
||||
struct key
|
||||
{
|
||||
crypto::hash tx_hash;
|
||||
bool detailed;
|
||||
|
||||
bool operator==(const key &other) const
|
||||
{
|
||||
return (tx_hash == other.tx_hash && detailed == other.detailed);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// helper to ignore any number of template parametrs
|
||||
template<typename...> using VoidT = void;
|
||||
|
@ -147,22 +119,6 @@ struct OutputIndicesReturnVectOfVectT<
|
|||
>>: std::true_type
|
||||
{};
|
||||
|
||||
// indect overload of hash for tx_info_cache::key
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<tx_info_cache::key>
|
||||
{
|
||||
size_t operator()(const tx_info_cache::key& k) const
|
||||
{
|
||||
size_t const h1 ( std::hash<crypto::hash>{}(k.tx_hash) );
|
||||
size_t const h2 ( std::hash<bool>{}(k.detailed) );
|
||||
return h1 ^ (h2 << 1);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* visitor to produce json representations of
|
||||
|
@ -521,15 +477,10 @@ bool enable_pusher;
|
|||
bool enable_key_image_checker;
|
||||
bool enable_output_key_checker;
|
||||
bool enable_mixins_details;
|
||||
bool enable_tx_cache;
|
||||
bool enable_block_cache;
|
||||
bool enable_as_hex;
|
||||
bool show_cache_times;
|
||||
|
||||
|
||||
bool enable_autorefresh_option;
|
||||
|
||||
|
||||
uint64_t no_of_mempool_tx_of_frontpage;
|
||||
uint64_t no_blocks_on_index;
|
||||
uint64_t mempool_info_timeout;
|
||||
|
@ -548,24 +499,6 @@ string js_html_files_all_in_one;
|
|||
// read operation in OS
|
||||
map<string, string> template_file;
|
||||
|
||||
|
||||
// alias for easy class typing
|
||||
template <typename Key, typename Value>
|
||||
using lru_cache_t = caches::fixed_sized_cache<Key, Value, caches::LRUCachePolicy<Key>>;
|
||||
|
||||
|
||||
// alias for easy class typing
|
||||
template <typename Key, typename Value>
|
||||
using fifo_cache_t = caches::fixed_sized_cache<Key, Value, caches::FIFOCachePolicy<Key>>;
|
||||
|
||||
// cache of txs_map of txs in blocks. this is useful for
|
||||
// index2 page, so that we dont parse txs in each block
|
||||
// for each request.
|
||||
fifo_cache_t<uint64_t, vector<pair<crypto::hash, mstch::node>>> block_tx_cache;
|
||||
|
||||
lru_cache_t<tx_info_cache::key, tx_info_cache> tx_context_cache;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
page(MicroCore* _mcore,
|
||||
|
@ -578,9 +511,6 @@ page(MicroCore* _mcore,
|
|||
bool _enable_output_key_checker,
|
||||
bool _enable_autorefresh_option,
|
||||
bool _enable_mixins_details,
|
||||
bool _enable_tx_cache,
|
||||
bool _enable_block_cache,
|
||||
bool _show_cache_times,
|
||||
uint64_t _no_blocks_on_index,
|
||||
uint64_t _mempool_info_timeout,
|
||||
string _testnet_url,
|
||||
|
@ -597,16 +527,11 @@ page(MicroCore* _mcore,
|
|||
enable_output_key_checker {_enable_output_key_checker},
|
||||
enable_autorefresh_option {_enable_autorefresh_option},
|
||||
enable_mixins_details {_enable_mixins_details},
|
||||
enable_tx_cache {_enable_tx_cache},
|
||||
enable_block_cache {_enable_block_cache},
|
||||
show_cache_times {_show_cache_times},
|
||||
no_blocks_on_index {_no_blocks_on_index},
|
||||
mempool_info_timeout {_mempool_info_timeout},
|
||||
testnet_url {_testnet_url},
|
||||
stagenet_url {_stagenet_url},
|
||||
mainnet_url {_mainnet_url},
|
||||
block_tx_cache(200),
|
||||
tx_context_cache(1000)
|
||||
mainnet_url {_mainnet_url}
|
||||
{
|
||||
mainnet = nettype == cryptonote::network_type::MAINNET;
|
||||
testnet = nettype == cryptonote::network_type::TESTNET;
|
||||
|
@ -705,8 +630,7 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|||
{"enable_pusher" , enable_pusher},
|
||||
{"enable_key_image_checker" , enable_key_image_checker},
|
||||
{"enable_output_key_checker", enable_output_key_checker},
|
||||
{"enable_autorefresh_option", enable_autorefresh_option},
|
||||
{"show_cache_times" , show_cache_times}
|
||||
{"enable_autorefresh_option", enable_autorefresh_option}
|
||||
};
|
||||
|
||||
context.emplace("txs", mstch::array()); // will keep tx to show
|
||||
|
@ -724,12 +648,6 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|||
|
||||
vector<double> blk_sizes;
|
||||
|
||||
// measure time of cache based execution, and non-cached execution
|
||||
double duration_cached {0.0};
|
||||
double duration_non_cached {0.0};
|
||||
uint64_t cache_hits {0};
|
||||
uint64_t cache_misses {0};
|
||||
|
||||
// loop index
|
||||
int64_t i = end_height;
|
||||
|
||||
|
@ -764,193 +682,67 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|||
|
||||
context["age_format"] = age.second;
|
||||
|
||||
// start measure time here
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
if (enable_block_cache && block_tx_cache.Contains(i))
|
||||
// get all transactions in the block found
|
||||
// initialize the first list with transaction for solving
|
||||
// the block i.e. coinbase.
|
||||
vector<cryptonote::transaction> blk_txs {blk.miner_tx};
|
||||
vector<crypto::hash> missed_txs;
|
||||
|
||||
if (!core_storage->get_transactions(blk.tx_hashes, blk_txs, missed_txs))
|
||||
{
|
||||
// get txs info in the ith block from
|
||||
// our cache
|
||||
|
||||
// start measure time here
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
const vector<pair<crypto::hash, mstch::node>>& txd_pairs
|
||||
= block_tx_cache.Get(i);
|
||||
|
||||
// copy tx maps from txs_maps_tmp into txs array,
|
||||
// that will go to templates
|
||||
for (const pair<crypto::hash, mstch::node>& txd_pair: txd_pairs)
|
||||
{
|
||||
// we need to check if the given transaction is still
|
||||
// in the same block as when it was cached. it is possible
|
||||
// the block got orphaned, and this tx is in mempool
|
||||
// or different block, and what we have in cache
|
||||
// is thus wrong
|
||||
|
||||
// but we do this only for first top blocks. no sense
|
||||
// doing it for all blocks
|
||||
|
||||
bool is_tx_still_in_block_as_expected {true};
|
||||
|
||||
if (i + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > height)
|
||||
{
|
||||
const crypto::hash& tx_hash = txd_pair.first;
|
||||
|
||||
if (core_storage->have_tx(tx_hash))
|
||||
{
|
||||
try
|
||||
{
|
||||
uint64_t tx_height_in_blockchain =
|
||||
core_storage->get_db().get_tx_block_height(tx_hash);
|
||||
|
||||
// check if height of the given tx that we have in cache,
|
||||
// denoted by i, is same as what is acctually stored
|
||||
// in blockchain
|
||||
if (tx_height_in_blockchain == i)
|
||||
{
|
||||
is_tx_still_in_block_as_expected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if no tx in the given block, just stop
|
||||
// any futher search. no need. we are going
|
||||
// to ditch the cache, in a monent
|
||||
is_tx_still_in_block_as_expected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const TX_DNE& e)
|
||||
{
|
||||
cerr << "Tx from cache" << pod_to_hex(tx_hash)
|
||||
<< " is no longer in the blockchain "
|
||||
<< endl;
|
||||
|
||||
is_tx_still_in_block_as_expected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
is_tx_still_in_block_as_expected = false;
|
||||
break;
|
||||
}
|
||||
|
||||
} // if (i + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE > height)
|
||||
|
||||
|
||||
if (!is_tx_still_in_block_as_expected)
|
||||
{
|
||||
// if some tx in cache is not in blockchain
|
||||
// where it should be, its probably better to
|
||||
// ditch entire cache, as redo it below.
|
||||
|
||||
block_tx_cache.Clear();
|
||||
txs.clear();
|
||||
i = end_height;
|
||||
continue; // reado the main loop
|
||||
}
|
||||
|
||||
// if we got to here, it means that everything went fine
|
||||
// and no unexpeced things happended.
|
||||
mstch::map txd_map = boost::get<mstch::map>(txd_pair.second);
|
||||
|
||||
// now we need to update age of txs from cashe
|
||||
if (!boost::get<string>(txd_map["age"]).empty())
|
||||
{
|
||||
txd_map["age"] = age.first;
|
||||
}
|
||||
|
||||
txs.push_back(txd_map);
|
||||
|
||||
} // for (const pair<crypto::hash, mstch::map>& txd_pair: txd_pairs)
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
||||
(std::chrono::steady_clock::now() - start);
|
||||
|
||||
// cout << "block_tx_json_cache from cache" << endl;
|
||||
|
||||
duration_cached += duration.count();
|
||||
|
||||
++cache_hits;
|
||||
cerr << "Cant get transactions in block: " << i << endl;
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
||||
uint64_t tx_i {0};
|
||||
|
||||
// tx_hash , txd_map
|
||||
vector<pair<crypto::hash, mstch::node>> txd_pairs;
|
||||
|
||||
for(auto it = blk_txs.begin(); it != blk_txs.end(); ++it)
|
||||
{
|
||||
// this is new block. not in cashe.
|
||||
// need to process its txs and add to cache
|
||||
const cryptonote::transaction& tx = *it;
|
||||
|
||||
// start measure time here
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
const tx_details& txd = get_tx_details(tx, false, i, height);
|
||||
|
||||
// get all transactions in the block found
|
||||
// initialize the first list with transaction for solving
|
||||
// the block i.e. coinbase.
|
||||
vector<cryptonote::transaction> blk_txs {blk.miner_tx};
|
||||
vector<crypto::hash> missed_txs;
|
||||
mstch::map txd_map = txd.get_mstch_map();
|
||||
|
||||
if (!core_storage->get_transactions(blk.tx_hashes, blk_txs, missed_txs))
|
||||
//add age to the txd mstch map
|
||||
txd_map.insert({"height" , i});
|
||||
txd_map.insert({"blk_hash" , blk_hash_str});
|
||||
txd_map.insert({"age" , age.first});
|
||||
txd_map.insert({"is_ringct" , (tx.version > 1)});
|
||||
txd_map.insert({"rct_type" , tx.rct_signatures.type});
|
||||
txd_map.insert({"blk_size" , blk_size_str});
|
||||
|
||||
|
||||
// do not show block info for other than first tx in a block
|
||||
if (tx_i > 0)
|
||||
{
|
||||
cerr << "Cant get transactions in block: " << i << endl;
|
||||
--i;
|
||||
continue;
|
||||
txd_map["height"] = string("");
|
||||
txd_map["age"] = string("");
|
||||
txd_map["blk_size"] = string("");
|
||||
}
|
||||
|
||||
uint64_t tx_i {0};
|
||||
txd_pairs.emplace_back(txd.hash, txd_map);
|
||||
|
||||
// this vector will go into block_tx cache
|
||||
// tx_hash , txd_map
|
||||
vector<pair<crypto::hash, mstch::node>> txd_pairs;
|
||||
++tx_i;
|
||||
|
||||
for(auto it = blk_txs.begin(); it != blk_txs.end(); ++it)
|
||||
{
|
||||
const cryptonote::transaction& tx = *it;
|
||||
} // for(list<cryptonote::transaction>::reverse_iterator rit = blk_txs.rbegin();
|
||||
|
||||
const tx_details& txd = get_tx_details(tx, false, i, height);
|
||||
// copy tx maps from txs_maps_tmp into txs array,
|
||||
// that will go to templates
|
||||
for (const pair<crypto::hash, mstch::node>& txd_pair: txd_pairs)
|
||||
{
|
||||
txs.push_back(boost::get<mstch::map>(txd_pair.second));
|
||||
}
|
||||
|
||||
mstch::map txd_map = txd.get_mstch_map();
|
||||
|
||||
//add age to the txd mstch map
|
||||
txd_map.insert({"height" , i});
|
||||
txd_map.insert({"blk_hash" , blk_hash_str});
|
||||
txd_map.insert({"age" , age.first});
|
||||
txd_map.insert({"is_ringct" , (tx.version > 1)});
|
||||
txd_map.insert({"rct_type" , tx.rct_signatures.type});
|
||||
txd_map.insert({"blk_size" , blk_size_str});
|
||||
|
||||
|
||||
// do not show block info for other than first tx in a block
|
||||
if (tx_i > 0)
|
||||
{
|
||||
txd_map["height"] = string("");
|
||||
txd_map["age"] = string("");
|
||||
txd_map["blk_size"] = string("");
|
||||
}
|
||||
|
||||
txd_pairs.emplace_back(txd.hash, txd_map);
|
||||
|
||||
++tx_i;
|
||||
|
||||
} // for(list<cryptonote::transaction>::reverse_iterator rit = blk_txs.rbegin();
|
||||
|
||||
// copy tx maps from txs_maps_tmp into txs array,
|
||||
// that will go to templates
|
||||
for (const pair<crypto::hash, mstch::node>& txd_pair: txd_pairs)
|
||||
{
|
||||
txs.push_back(boost::get<mstch::map>(txd_pair.second));
|
||||
}
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
||||
(std::chrono::steady_clock::now() - start);
|
||||
|
||||
duration_non_cached += duration.count();
|
||||
|
||||
++cache_misses;
|
||||
|
||||
if (enable_block_cache)
|
||||
{
|
||||
// save in block_tx cache
|
||||
block_tx_cache.Put(i, txd_pairs);
|
||||
}
|
||||
|
||||
} // else if (block_tx_json_cache.Contains(i))
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
||||
(std::chrono::steady_clock::now() - start);
|
||||
|
||||
--i; // go to next block number
|
||||
|
||||
|
@ -959,30 +751,20 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|||
// calculate median size of the blocks shown
|
||||
//double blk_size_median = xmreg::calc_median(blk_sizes.begin(), blk_sizes.end());
|
||||
|
||||
// save computational times for disply in the frontend
|
||||
|
||||
context["construction_time_cached"] = fmt::format(
|
||||
"{:0.4f}", duration_cached/1.0e6);
|
||||
|
||||
context["construction_time_non_cached"] = fmt::format(
|
||||
"{:0.4f}", duration_non_cached/1.0e6);
|
||||
|
||||
context["construction_time_total"] = fmt::format(
|
||||
"{:0.4f}", (duration_non_cached+duration_cached)/1.0e6);
|
||||
|
||||
context["cache_hits"] = cache_hits;
|
||||
context["cache_misses"] = cache_misses;
|
||||
|
||||
|
||||
// get current network info from MemoryStatus thread.
|
||||
MempoolStatus::network_info current_network_info
|
||||
= MempoolStatus::current_network_info;
|
||||
|
||||
// perapre network info mstch::map for the front page
|
||||
string hash_rate;
|
||||
|
||||
double hr_d;
|
||||
char metric_prefix;
|
||||
cryptonote::difficulty_type hr = make_difficulty(current_network_info.hash_rate, current_network_info.hash_rate_top64);
|
||||
|
||||
cryptonote::difficulty_type hr = make_difficulty(
|
||||
current_network_info.hash_rate,
|
||||
current_network_info.hash_rate_top64);
|
||||
|
||||
get_metric_prefix(hr, hr_d, metric_prefix);
|
||||
|
||||
if (metric_prefix != 0)
|
||||
|
@ -1001,7 +783,7 @@ index2(uint64_t page_no = 0, bool refresh_page = false)
|
|||
}
|
||||
|
||||
context["network_info"] = mstch::map {
|
||||
{"difficulty" , make_difficulty(current_network_info.difficulty, current_network_info.difficulty_top64).str()},
|
||||
{"difficulty" , current_network_info.difficulty},
|
||||
{"hash_rate" , hash_rate},
|
||||
{"fee_per_kb" , print_money(current_network_info.fee_per_kb)},
|
||||
{"alt_blocks_no" , current_network_info.alt_blocks_count},
|
||||
|
@ -1101,7 +883,6 @@ mempool(bool add_header_and_footer = false, uint64_t no_of_mempool_tx = 25)
|
|||
// initalise page tempate map with basic info about mempool
|
||||
mstch::map context {
|
||||
{"mempool_size" , static_cast<uint64_t>(total_no_of_mempool_tx)}, // total no of mempool txs
|
||||
{"show_cache_times" , show_cache_times},
|
||||
{"mempool_refresh_time" , MempoolStatus::mempool_refresh_time}
|
||||
};
|
||||
|
||||
|
@ -1110,11 +891,6 @@ mempool(bool add_header_and_footer = false, uint64_t no_of_mempool_tx = 25)
|
|||
// get reference to blocks template map to be field below
|
||||
mstch::array& txs = boost::get<mstch::array>(context["mempooltxs"]);
|
||||
|
||||
double duration_cached {0.0};
|
||||
double duration_non_cached {0.0};
|
||||
uint64_t cache_hits {0};
|
||||
uint64_t cache_misses {0};
|
||||
|
||||
uint64_t local_copy_server_timestamp = server_timestamp;
|
||||
|
||||
// for each transaction in the memory pool
|
||||
|
@ -1145,7 +921,6 @@ mempool(bool add_header_and_footer = false, uint64_t no_of_mempool_tx = 25)
|
|||
delta_time[3], delta_time[4]);
|
||||
}
|
||||
|
||||
// cout << "block_tx_json_cache from cache" << endl;
|
||||
|
||||
// set output page template map
|
||||
txs.push_back(mstch::map {
|
||||
|
@ -1583,149 +1358,8 @@ show_tx(string tx_hash_str, uint16_t with_ring_signatures = 0, bool refresh_page
|
|||
|
||||
mstch::map tx_context;
|
||||
|
||||
if (enable_tx_cache && tx_context_cache.Contains({tx_hash, static_cast<bool>(with_ring_signatures)}))
|
||||
{
|
||||
// with_ring_signatures == 0 means that cache is not used
|
||||
// when obtaining detailed information about tx is requested.
|
||||
|
||||
// we are going to measure time for the construction of the
|
||||
// tx context from cashe. just for fun, to see if cache is any faster.
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
const tx_info_cache& tx_info_cashed
|
||||
= tx_context_cache.Get({tx_hash, static_cast<bool>(with_ring_signatures)});
|
||||
|
||||
tx_context = tx_info_cashed.tx_map;
|
||||
|
||||
//cout << "get tx from cash: " << tx_hash_str <<endl;
|
||||
//cout << " - tx_blk_height: " << boost::get<uint64_t>(tx_context["tx_blk_height"]) <<endl;
|
||||
//cout << " - blk_timestamp_uint: " << boost::get<uint64_t>(tx_context["blk_timestamp_uint"]) <<endl;
|
||||
|
||||
// now have to update age and confirmation numbers of the tx.
|
||||
|
||||
uint64_t tx_blk_height = boost::get<uint64_t>(tx_context["tx_blk_height"]);
|
||||
uint64_t blk_timestamp_uint = boost::get<uint64_t>(tx_context["blk_timestamp_uint"]);
|
||||
|
||||
if (tx_blk_height > 0)
|
||||
{
|
||||
// seems to be in blockchain. off course it could have been orphaned
|
||||
// so double check if its for sure in blockchain
|
||||
|
||||
if (core_storage->have_tx(tx_hash))
|
||||
{
|
||||
// ok, it is still in blockchain
|
||||
// update its age and number of confirmations
|
||||
|
||||
pair<string, string> age
|
||||
= get_age(std::time(nullptr),
|
||||
blk_timestamp_uint,
|
||||
FULL_AGE_FORMAT);
|
||||
|
||||
tx_context["delta_time"] = age.first;
|
||||
|
||||
uint64_t bc_height = core_storage->get_current_blockchain_height();
|
||||
|
||||
tx_context["confirmations"] = bc_height - (tx_blk_height - 1);
|
||||
|
||||
// marke it as from cashe. useful if we want to show
|
||||
// info about cashed/not cashed in frontend.
|
||||
tx_context["from_cache"] = true;
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
||||
(std::chrono::steady_clock::now() - start);
|
||||
|
||||
tx_context["construction_time"] = fmt::format(
|
||||
"{:0.4f}", static_cast<double>(duration.count())/1.0e6);
|
||||
|
||||
// normally we should update this into in the cache.
|
||||
// but since we make this check all the time,
|
||||
// we can skip updating cashed version
|
||||
|
||||
} // if (core_storage->have_tx(tx_hash))
|
||||
else
|
||||
{
|
||||
// its not in blockchain, but it was there when we cashed it.
|
||||
// so we update it in cash, as it should be back in mempool
|
||||
|
||||
tx_context = construct_tx_context(tx, static_cast<bool>(with_ring_signatures));
|
||||
|
||||
tx_context_cache.Put(
|
||||
{tx_hash, static_cast<bool>(with_ring_signatures)},
|
||||
tx_info_cache {
|
||||
boost::get<uint64_t>(tx_context["tx_blk_height"]),
|
||||
boost::get<uint64_t>(tx_context["blk_timestamp_uint"]),
|
||||
tx_context}
|
||||
);
|
||||
}
|
||||
} // if (tx_blk_height > 0)
|
||||
else
|
||||
{
|
||||
// the tx was cashed when in mempool.
|
||||
// since then, it might have been included in some block.
|
||||
// so we check it.
|
||||
|
||||
if (core_storage->have_tx(tx_hash))
|
||||
{
|
||||
// checking if in blockchain already
|
||||
// it was before in mempool, but now maybe already in blockchain
|
||||
|
||||
tx_context = construct_tx_context(tx, static_cast<bool>(with_ring_signatures));
|
||||
|
||||
tx_context_cache.Put(
|
||||
{tx_hash, static_cast<bool>(with_ring_signatures)},
|
||||
tx_info_cache {
|
||||
boost::get<uint64_t>(tx_context["tx_blk_height"]),
|
||||
boost::get<uint64_t>(tx_context["blk_timestamp_uint"]),
|
||||
tx_context});
|
||||
|
||||
|
||||
} // if (core_storage->have_tx(tx_hash))
|
||||
else
|
||||
{
|
||||
// still seems to be in mempool only.
|
||||
// so just get its time duration, as its read only
|
||||
// from cache
|
||||
|
||||
tx_context["from_cache"] = true;
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
||||
(std::chrono::steady_clock::now() - start);
|
||||
|
||||
tx_context["construction_time"] = fmt::format(
|
||||
"{:0.4f}", static_cast<double>(duration.count())/1.0e6);
|
||||
|
||||
}
|
||||
|
||||
} // else if (tx_blk_height > 0)
|
||||
|
||||
} // if (tx_context_cache.Contains(tx_hash))
|
||||
else
|
||||
{
|
||||
|
||||
// we are going to measure time for the construction of the
|
||||
// tx context. just for fun, to see if cache is any faster.
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
tx_context = construct_tx_context(tx, static_cast<bool>(with_ring_signatures));
|
||||
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>
|
||||
(std::chrono::steady_clock::now() - start);
|
||||
|
||||
if (enable_tx_cache)
|
||||
{
|
||||
tx_context_cache.Put(
|
||||
{tx_hash, static_cast<bool>(with_ring_signatures)},
|
||||
tx_info_cache {
|
||||
boost::get<uint64_t>(tx_context["tx_blk_height"]),
|
||||
boost::get<uint64_t>(tx_context["blk_timestamp_uint"]),
|
||||
tx_context});
|
||||
}
|
||||
|
||||
tx_context["construction_time"] = fmt::format(
|
||||
"{:0.4f}", static_cast<double>(duration.count())/1.0e6);
|
||||
|
||||
} // else if (tx_context_cache.Contains(tx_hash))
|
||||
|
||||
tx_context = construct_tx_context(tx, static_cast<bool>(with_ring_signatures));
|
||||
|
||||
tx_context["show_more_details_link"] = show_more_details_link;
|
||||
|
||||
|
@ -1737,7 +1371,6 @@ show_tx(string tx_hash_str, uint16_t with_ring_signatures = 0, bool refresh_page
|
|||
mstch::map context {
|
||||
{"testnet" , this->testnet},
|
||||
{"stagenet" , this->stagenet},
|
||||
{"show_cache_times" , show_cache_times},
|
||||
{"txs" , mstch::array{}},
|
||||
{"refresh" , refresh_page},
|
||||
{"tx_hash" , tx_hash_str}
|
||||
|
@ -4878,11 +4511,6 @@ json_transaction(string tx_hash_str)
|
|||
no_confirmations = txd.no_confirmations;
|
||||
}
|
||||
|
||||
// get tx from tx fetched. can be use to double check
|
||||
// if what we return in the json response agrees with
|
||||
// what tx_hash was requested
|
||||
string tx_hash_str_again = pod_to_hex(get_transaction_hash(tx));
|
||||
|
||||
// get basic tx info
|
||||
j_data = get_tx_json(tx, txd);
|
||||
|
||||
|
@ -6447,7 +6075,6 @@ construct_tx_context(transaction tx, uint16_t with_ring_signatures = 0)
|
|||
{"error_msg" , string("")},
|
||||
{"have_raw_tx" , false},
|
||||
{"show_more_details_link", true},
|
||||
{"from_cache" , false},
|
||||
{"construction_time" , string {}},
|
||||
};
|
||||
|
||||
|
@ -6863,7 +6490,15 @@ get_tx_details(const transaction& tx,
|
|||
tx_details txd;
|
||||
|
||||
// get tx hash
|
||||
txd.hash = get_transaction_hash(tx);
|
||||
|
||||
if (!tx.pruned)
|
||||
{
|
||||
txd.hash = get_transaction_hash(tx);
|
||||
}
|
||||
else
|
||||
{
|
||||
txd.hash = get_pruned_transaction_hash(tx, tx.prunable_hash);
|
||||
}
|
||||
|
||||
// get tx public key from extra
|
||||
// this check if there are two public keys
|
||||
|
|
|
@ -111,14 +111,3 @@
|
|||
|
||||
</div>
|
||||
|
||||
|
||||
{{#show_cache_times}}
|
||||
<div class="center">
|
||||
<h6 style="margin-top: 1px;color:#949490">
|
||||
Tx details construction time: {{construction_time_total}} s
|
||||
<br/>
|
||||
includes {{construction_time_cached}} s from block cache ({{cache_hits}} hits)
|
||||
and {{construction_time_non_cached}} s from non cache ({{cache_misses}} misses)
|
||||
</h6>
|
||||
</div>
|
||||
{{/show_cache_times}}
|
||||
|
|
|
@ -300,18 +300,4 @@
|
|||
{{/with_ring_signatures}}
|
||||
{{/have_raw_tx}}
|
||||
|
||||
|
||||
{{#show_cache_times}}
|
||||
<div class="center">
|
||||
{{#construction_time}}
|
||||
<h6 style="margin-top: 1px;color:#949490">
|
||||
Tx details construction time: {{construction_time}} s
|
||||
{{#from_cache}}
|
||||
<br/>Tx read from the tx cache
|
||||
{{/from_cache}}
|
||||
</h6>
|
||||
{{/construction_time}}
|
||||
</div>
|
||||
{{/show_cache_times}}
|
||||
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue