Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
Already on GitHub? Sign in to your account
Add mempool statistics collector #8501
Conversation
jonasschnelli
added
the
RPC/REST/ZMQ
label
Aug 12, 2016
isle2983
commented on an outdated diff
Aug 13, 2016
| + } | ||
| + } | ||
| + | ||
| + mempoolSamples_t subset(fromSample, toSample + 1); | ||
| + | ||
| + // set the fromTime and toTime pass-by-ref parameters | ||
| + fromTime = mempoolStats.startTime + (*fromSample).timeDelta; | ||
| + toTime = mempoolStats.startTime + (*toSample).timeDelta; | ||
| + | ||
| + // return subset | ||
| + return subset; | ||
| + } | ||
| + | ||
| + // return all available samples | ||
| + fromTime = mempoolStats.startTime + mempoolStats.vSamples.front().timeDelta; | ||
| + ; |
|
|
isle2983
commented on an outdated diff
Aug 14, 2016
| +// Distributed under the MIT software license, see the accompanying | ||
| +// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
| + | ||
| +#ifndef BITCOIN_STATS_H | ||
| +#define BITCOIN_STATS_H | ||
| + | ||
| +#include <sync.h> | ||
| + | ||
| +#include <atomic> | ||
| +#include <stdlib.h> | ||
| +#include <vector> | ||
| + | ||
| +#include <boost/signals2/signal.hpp> | ||
| + | ||
| +struct CStatsMempoolSample { | ||
| + uint32_t timeDelta; //use 32bit time delta to save memmory |
|
|
isle2983
commented on an outdated diff
Aug 14, 2016
| + static std::atomic<bool> statsEnabled; | ||
| + static CStats* DefaultStats(); //shared instance | ||
| + | ||
| + /* signals */ | ||
| + boost::signals2::signal<void(void)> MempoolStatsDidChange; //mempool signal | ||
| + | ||
| + /* add a mempool stats sample */ | ||
| + void addMempoolSample(int64_t txcount, int64_t dynUsage, int64_t currentMinRelayFee); | ||
| + | ||
| + /* get all mempool samples (non interpolated) */ | ||
| + mempoolSamples_t mempoolGetValuesInRange(uint64_t& fromTime, uint64_t& toTime); | ||
| + | ||
| + /* set the target for the maximum memory consuption (in bytes) */ | ||
| + void setMaxMemoryUsageTarget(size_t maxMem); | ||
| + | ||
| + /* get the statictis module help strings */ |
|
|
isle2983
commented on an outdated diff
Aug 14, 2016
| + static const size_t DEFAULT_MAX_STATS_MEMORY; //default maximum of memory to use | ||
| + static const bool DEFAULT_STATISTICS_ENABLED; //default value for enabling statistics | ||
| + | ||
| + static std::atomic<bool> statsEnabled; | ||
| + static CStats* DefaultStats(); //shared instance | ||
| + | ||
| + /* signals */ | ||
| + boost::signals2::signal<void(void)> MempoolStatsDidChange; //mempool signal | ||
| + | ||
| + /* add a mempool stats sample */ | ||
| + void addMempoolSample(int64_t txcount, int64_t dynUsage, int64_t currentMinRelayFee); | ||
| + | ||
| + /* get all mempool samples (non interpolated) */ | ||
| + mempoolSamples_t mempoolGetValuesInRange(uint64_t& fromTime, uint64_t& toTime); | ||
| + | ||
| + /* set the target for the maximum memory consuption (in bytes) */ |
|
|
isle2983
commented on an outdated diff
Aug 14, 2016
| + if (!statsEnabled) | ||
| + return; | ||
| + | ||
| + uint64_t now = GetTime(); | ||
| + { | ||
| + LOCK(cs_stats); | ||
| + | ||
| + // set the mempool stats start time if this is the first sample | ||
| + if (mempoolStats.startTime == 0) | ||
| + mempoolStats.startTime = now; | ||
| + | ||
| + // ensure the minimum time delta between samples | ||
| + if (mempoolStats.vSamples.size() && mempoolStats.vSamples.back().timeDelta + SAMPLE_MIN_DELTA_IN_SEC >= now - mempoolStats.startTime) | ||
| + return; | ||
| + | ||
| + // calculate the current time detla and add a sample |
|
|
isle2983
commented on an outdated diff
Aug 14, 2016
| + | ||
| +#include "stats/stats.h" | ||
| + | ||
| +#include "memusage.h" | ||
| +#include "utiltime.h" | ||
| + | ||
| +#include "util.h" | ||
| + | ||
| +const uint32_t CStats::SAMPLE_MIN_DELTA_IN_SEC = 2; | ||
| +const int CStats::CLEANUP_SAMPLES_THRESHOLD = 100; | ||
| +size_t CStats::maxStatsMemory = 0; | ||
| +const size_t CStats::DEFAULT_MAX_STATS_MEMORY = 10 * 1024 * 1024; //10 MB | ||
| +const bool CStats::DEFAULT_STATISTICS_ENABLED = false; | ||
| +std::atomic<bool> CStats::statsEnabled(false); //disable stats by default | ||
| + | ||
| +CStats defaultStats; |
|
|
|
Hi Jonas, I understand the concern in your description for the addMempoolSample() stat
Is the 'flat' encoding strictly needed? or is there some other concern with
Cheers, Isle |
|
@isle2983 Welcome to github. For your questions/inputs:
|
jonasschnelli
referenced this pull request
Aug 19, 2016
Open
[Qt] Add interactive mempool graph #8550
MarcoFalke
commented on an outdated diff
Aug 19, 2016
| + "\nExamples:\n" + | ||
| + HelpExampleCli("getmempoolstats", "") + HelpExampleRpc("getmempoolstats", "")); | ||
| + | ||
| + // get stats from the core stats model | ||
| + uint64_t timeFrom = 0; | ||
| + uint64_t timeTo = 0; | ||
| + mempoolSamples_t samples = CStats::DefaultStats()->mempoolGetValuesInRange(timeFrom, timeTo); | ||
| + | ||
| + // use "flat" json encoding for performance reasons | ||
| + std::string flatData; | ||
| + for (struct CStatsMempoolSample& sample : samples) { | ||
| + flatData += std::to_string(sample.timeDelta) + ","; | ||
| + flatData += std::to_string(sample.txCount) + ","; | ||
| + flatData += std::to_string(sample.dynMemUsage) + ","; | ||
| + flatData += std::to_string(sample.minFeePerK) + ","; | ||
| + } |
MarcoFalke
Member
|
|
I have been playing around making my own changes off these commits (isle2983:getmempoolstats). Mostly to just to get some hands on with the code and try to get my C++ up to par. But anyway, I made the rpc output of the samples full JSON:
The JSON 'pretty' print through bitcoin-cli is definitely unwieldy. However, the computational overhead in doing the wrangling doesn't seem so bad. The 1.7MM of stat data is from collecting just overnight. With that data, I can pull it off the node, parse and convert the JSON into CSV with a python script and plot it in gnuplot in under a second.
Not sure what the comparable is with the qt gui stuff branch that is running, but this doesn't seem too bad on the face of it. Also, if getting this info from the node to the UI quickly is a concern, perhaps a more dense, binary-like format is worth considering i.e:
One could imagine it being more efficient than even the 'flat' format, depending on the sophistication. |
|
Thanks @isle2983 for the testing, benchmarks and improvements. I also though again about copy the samples hash before filtering it. I came to the conclusion that it's not worth generating a memory peak (by a copy of the whole samples vector) in order to allow a faster release of the LOCK. The filtering should be very fast because it only compares some uint32 and does construct a new vector with a from-/to-iterators (should also preform fast). |
jonasschnelli
referenced this pull request
Aug 26, 2016
Closed
Add bloom filter usage statistics #7685
|
Needed rebase. |
|
Rebased. |
MarcoFalke
added this to the 0.14.0 milestone
Nov 10, 2016
|
Assigning "would be nice to have" for 0.14 per meeting today. |
|
Just saw @gmaxwell's comment on #8550 (which I completely agree with) and it reminded to look at that PR and this one. Sorry for not getting involved sooner, but I really like the idea. Unfortunately I can think of many many stats (dozens and dozens) that we might want to collect, both to potentially show to users in whiz-bangy gui's and also would be useful for developers and businesses trying to understand unusual behavior on the network. If we envision that there might be 1 KB of different stats data, then maybe rather than just saving sample data points and trimming when they hit memory usage, we should be smart about saving it along different time frames. For instance we could have second, minute, hour, day sampling intervals and we could save 1000 points or more on each and still have quite reasonable memory usage, but they could be auto trimmed.. so if you wanted to look at data from hours ago, you couldn't look at it on the second time frame... |
|
@morcos: thanks for the comment. Yes. I completely agree. I think this is a first start and the current design allows features like you mentioned. |
|
@jonasschnelli Well I guess what I was thinking was that one general framework might fit all stats. You log it with whatever frequency you want. And it's stored in up to 4 different histories (by second, minute, hour, day) and each of those is trimmed to some limit (say 1000 or 2000 data points each). Is there any type of stat that such a general framework might not work well with? |
|
@morcos: the original idea I was trying to follow was to not collect in a fixed frequency. A) To avoid locking a thread for just collecting stats samples. B) To not collect over and over the same value if it was unchanged. I had the idea of recording samples in the most restrained way possible. Collect lock free and only if values have changes; collect the according timestamp. But not sure if this is stupid. |
|
Rebased (main split) |
|
c0af366 has unresolved conflicts :( |
|
@luke-jr All checks have passed ;-) |
|
@paveljanik They're removed in the subsequent commit. |
|
Yes (I noticed that), this is why we should take Travis' results as a help only. |
added a commit
to bitcoinknots/bitcoin
that referenced
this pull request
Dec 21, 2016
added a commit
to bitcoinknots/bitcoin
that referenced
this pull request
Dec 21, 2016
|
@jonasschnelli whatever happened to this plan?
|
jonasschnelli
removed this from the 0.14.0 milestone
Jan 12, 2017
|
Concept ACK |
|
Needs rebase / reply to @morcos' review comment |
|
Completely rewrote this PR. Current features are ping @morcos |
jonasschnelli
added this to the 0.15.0 milestone
May 8, 2017
| @@ -1164,6 +1166,9 @@ bool AppInitSanityChecks() | ||
| { | ||
| // ********************************************************* Step 4: sanity checks | ||
| + if (!CStats::parameterInteraction()) |
ryanofsky
May 10, 2017
Contributor
This seems like it would belong in AppInitParameterInteraction more than AppInitSanityChecks
| + "\nResult:\n" | ||
| + " [\n" | ||
| + " {\n" | ||
| + " \"percision_interval\" : \"interval\", (numeric) Interval target in seconds between samples\n" |
ryanofsky
May 10, 2017
Contributor
Should s/percision/precision/ throughout PR. Also maybe this field should be called "sample_interval" to be consistent with "samples" field below.
| + std::atomic<size_t> maxStatsMemory; | ||
| + | ||
| + // mempool collector | ||
| + CStatsMempool *mempoolStatsCollector; |
| + intervalCounter = 0; | ||
| + | ||
| + // setup the samples per percision vector | ||
| + for (unsigned int i = 0; i < sizeof(precisionIntervals) / sizeof(precisionIntervals[0]); i++) { |
ryanofsky
May 10, 2017
Contributor
Should replace with for (unsigned int interval : precisionIntervals)
| + | ||
| + // setup the samples per percision vector | ||
| + for (unsigned int i = 0; i < sizeof(precisionIntervals) / sizeof(precisionIntervals[0]); i++) { | ||
| + vSamplesPerPrecision.push_back(MempoolSamplesVector()); |
| + | ||
| +std::vector<unsigned int> CStatsMempool::getPercisionGroupsAndIntervals() { | ||
| + std::vector<unsigned int> grps; | ||
| + for (unsigned int i = 0; i < sizeof(precisionIntervals) / sizeof(precisionIntervals[0]); i++) { |
ryanofsky
May 10, 2017
Contributor
I think you could drop the loop and just return {std::begin(precisionIntervals), std::end(precisionIntervals)};
| + | ||
| + /* time delta to last item as 16bit unsigned integer | ||
| + * allows a max delta of ~18h */ | ||
| + uint16_t timeDelta; |
ryanofsky
May 10, 2017
Contributor
Using uint16_t here probably doesn't save any space due to struct padding.
|
Addressed @ryanofsky points. |
| + | ||
| +void RegisterStatsRPCCommands(CRPCTable& tableRPC) | ||
| +{ | ||
| + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) |
jonasschnelli
May 12, 2017
Member
I think we better fix this together with all other Register*RPCCommands calls.
| +#include "util.h" | ||
| + | ||
| + | ||
| +const size_t DEFAULT_MAX_STATS_MEMORY = 10 * 1024 * 1024; //10 MB |
| +{ | ||
| + std::string strUsage = HelpMessageGroup(_("Statistic options:")); | ||
| + strUsage += HelpMessageOpt("-statsenable=", strprintf("Enable statistics (default: %u)", DEFAULT_STATISTICS_ENABLED)); | ||
| + strUsage += HelpMessageOpt("-statsmaxmemorytarget=<n>", strprintf(_("Set the memory limit target for statictics in bytes (default: %u)"), DEFAULT_MAX_STATS_MEMORY)); |
| +const static unsigned int MAX_SAMPLES = 5000; | ||
| + | ||
| + | ||
| +const static unsigned int fallbackMaxSamplesPerPercision = 1000; |
paveljanik
May 12, 2017
Contributor
fallbackMaxSamplesPerPercision -> fallbackMaxSamplesPerPrecision
|
Fixed @paveljanik points and nits. |
| + | ||
| +}; | ||
| + | ||
| +void RegisterStatsRPCCommands(CRPCTable& tableRPC) |
paveljanik
May 12, 2017
Contributor
Wshadow emits:
stats/rpc_stats.cpp:78:42: warning: declaration shadows a variable in the global namespace [-Wshadow]
jonasschnelli
referenced this pull request
May 16, 2017
Open
Network Traffic/Reset delayed action #10406
| + // use "flat" json encoding for performance reasons | ||
| + UniValue samplesObj(UniValue::VARR); | ||
| + for (const struct CStatsMempoolSample& sample : samples) { | ||
| + UniValue singleSample(UniValue::VARR); |
ryanofsky
Jun 13, 2017
Contributor
In commit "Add mempool statistics collector"
It seems like it would be more user friendly and extensible if this were an object instead of an array, so the elements could be named. It's a little awkard to have to remember that the first element in the array is time delta, second element is transaction count, etc.
ryanofsky
Oct 5, 2017
Contributor
Thread #8501 (comment)
Any thoughts on this? Named fields seems more natural than tuples in a JSON data structure.
| + | ||
| +const size_t DEFAULT_MAX_STATS_MEMORY = 10 * 1024 * 1024; // 10 MB | ||
| +const bool DEFAULT_STATISTICS_ENABLED = false; | ||
| +const static unsigned int STATS_COLLECT_INTERVAL = 2000; // 2 secs |
ryanofsky
Jun 13, 2017
Contributor
In commit "Add mempool statistics collector"
Most of the other settings seem to be in seconds instead of milliseconds. Seems like it would be good to switch this one to seconds for consistency, and to be able to get rid of the division by 1000 in CStatsMempool::addMempoolSamples.
ryanofsky
Oct 5, 2017
Contributor
Thread #8501 (comment)
Would be nice to use either seconds or milliseconds internally in the stats classes instead of both.
| + startTime += vSamplesPerPrecision[i][j].timeDelta; | ||
| + } | ||
| + // remove element(s) at vector front | ||
| + vSamplesPerPrecision[i].erase(vSamplesPerPrecision[i].begin(), vSamplesPerPrecision[i].begin() + (vSamplesPerPrecision[i].size() - vMaxSamplesPerPrecision[i])); |
ryanofsky
Jun 13, 2017
Contributor
In commit "Add mempool statistics collector"
Seems like it would be more efficient to switch to a circular buffer, or use std::deque instead of deleting from the beginning of the vector.
ryanofsky
Oct 5, 2017
Contributor
Thread #8501 (comment)
Still worth considering alternatives to erasing from the beginning of a vector, I think.
| + | ||
| + intervalCounter++; | ||
| + | ||
| + if (intervalCounter > biggestInterval) { |
ryanofsky
Jun 13, 2017
Contributor
In commit "Add mempool statistics collector"
I don't understand what this is doing. It seems unnecessary, and strange because it is comparing the counter against numbers of seconds without taking collectInterval into account. If you will keep this, I think it'd be good to add a comment with an example of interval and counter values that explains how this is supposed to work.
jonasschnelli
Jun 13, 2017
Member
I have added this to avoid an overflow of intervalCounter. I though resetting it when we exceeded the highest possible interval seems sane but I agree that a size_t overflow will very likely happens never... would you recommend to remove it?
ryanofsky
Jun 13, 2017
Contributor
would you recommend to remove it?
I'd say remove it unless there's a way to reset it that makes sense (I'm sure there is one but nothing immediately comes to mind).
ryanofsky
Oct 5, 2017
Contributor
Thread #8501 (comment)
Should follow up by removing this or adding comments suggested above.
| + std::atomic<uint64_t> startTime; //start time | ||
| + std::vector<MempoolSamplesVector> vSamplesPerPrecision; //!<multiple samples vector per precision level | ||
| + std::vector<size_t> vMaxSamplesPerPrecision; //!<vector with the maximum amount of samples per precision level | ||
| + std::vector<size_t> vTimeLastSample; //!<vector with the maximum amount of samples per precision level |
ryanofsky
Jun 13, 2017
Contributor
In commit "Add mempool statistics collector"
All three of these vectors have the same length (number of precision levels). Seems like it would be cleaner to have one vector containing a struct with all the information that needs to be stored for a given precision level. It would also make the c++ data structure more consistent with the json data structure returned from rpc.
jonasschnelli
Jun 13, 2017
Member
That would be possible though I think that the current approach makes the core more readable.
ryanofsky
Oct 5, 2017
Contributor
Do you think having parallel lists is more readable in c++ and having one list is more readable in json? Also, why are the first two vectors labeled perPrecision, and the third one not? I guess I think something like the following would be less ambiguous and duplicative:
struct PrecisionLevel {
std::vector<CStatsMempoolSample> samples;
size_t max_num_samples;
int64_t last_sample_time;
};
std::vector<PrecisionLevel> m_precision_levels;Could also rename cs_mempool_stats to cs_precision_levels to make it clearer what the lock is protecting.
| @@ -545,6 +546,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C | ||
| { | ||
| const CTransaction& tx = *ptx; | ||
| const uint256 hash = tx.GetHash(); | ||
| + CFeeRate poolMinFeeRate; |
ryanofsky
Jun 13, 2017
Contributor
In commit "Add mempool statistics collector"
Would be good to move declaration closer to where this is being set (at least as close as possible).
laanwj
modified the milestones:
0.16.0,
0.15.0
Jul 13, 2017
jonasschnelli
referenced this pull request
Jul 21, 2017
Open
Collect node's metrics in Prometheus format #10887
|
Rebased on my repo.
|
jonasschnelli
added some commits
Aug 12, 2016
|
Rebased (rebased @luke-jr's version). |
| + | ||
| + MempoolSamplesVector samples = CStats::DefaultStats()->mempoolCollector->getSamplesForPrecision(i, timeFrom); | ||
| + | ||
| + // use "flat" json encoding for performance reasons |
| + // use "flat" json encoding for performance reasons | ||
| + UniValue samplesObj(UniValue::VARR); | ||
| + for (const struct CStatsMempoolSample& sample : samples) { | ||
| + UniValue singleSample(UniValue::VARR); |
ryanofsky
Oct 5, 2017
Contributor
Thread #8501 (comment)
Any thoughts on this? Named fields seems more natural than tuples in a JSON data structure.
| + | ||
| +const size_t DEFAULT_MAX_STATS_MEMORY = 10 * 1024 * 1024; // 10 MB | ||
| +const bool DEFAULT_STATISTICS_ENABLED = false; | ||
| +const static unsigned int STATS_COLLECT_INTERVAL = 2000; // 2 secs |
ryanofsky
Oct 5, 2017
Contributor
Thread #8501 (comment)
Would be nice to use either seconds or milliseconds internally in the stats classes instead of both.
| +const bool DEFAULT_STATISTICS_ENABLED = false; | ||
| +const static unsigned int STATS_COLLECT_INTERVAL = 2000; // 2 secs | ||
| + | ||
| +CStats* CStats::sharedInstance = NULL; |
| +{ | ||
| +private: | ||
| + static CStats* sharedInstance; //!< singleton instance | ||
| + std::atomic<bool> statsEnabled; |
ryanofsky
Oct 5, 2017
Contributor
Why have this as a separate variable when it seems to just be true when maxStatsMemory != 0. Maybe eliminate this or else add comments documenting the intended interfaction between the two variables.
| + void startCollecting(CScheduler& scheduler); | ||
| + | ||
| + /* set the target for the maximum memory consumption (in bytes) */ | ||
| + void setMaxMemoryUsageTarget(size_t maxMem); |
ryanofsky
Oct 5, 2017
Contributor
Should this be private since it is called by parameterInteraction?
Either way, it seems like this needs a comment about when it is safe to be called. E.g. it seems like this will not always work properly if it is called after startCollecting because it won't schedule the callback. But maybe it will work to change the size if it was previously greater than 0?
| + | ||
| + /* SIGNALS | ||
| + * ======= */ | ||
| + boost::signals2::signal<void(void)> MempoolStatsDidChange; //mempool signal |
ryanofsky
Oct 5, 2017
Contributor
Nobody seems to be listening to this signal. Should add comment explaining who intended listeners are, if keeping this.
| +{ | ||
| + bool statsChanged = false; | ||
| + uint64_t now = GetTime(); | ||
| + { |
ryanofsky
Oct 5, 2017
Contributor
Indent and extra braces don't seem to accomplishing anything here.
| + bool statsChanged = false; | ||
| + uint64_t now = GetTime(); | ||
| + { | ||
| + LOCK(cs_mempool_stats); |
| + startTime += vSamplesPerPrecision[i][j].timeDelta; | ||
| + } | ||
| + // remove element(s) at vector front | ||
| + vSamplesPerPrecision[i].erase(vSamplesPerPrecision[i].begin(), vSamplesPerPrecision[i].begin() + (vSamplesPerPrecision[i].size() - vMaxSamplesPerPrecision[i])); |
ryanofsky
Oct 5, 2017
Contributor
Thread #8501 (comment)
Still worth considering alternatives to erasing from the beginning of a vector, I think.
| + | ||
| + intervalCounter++; | ||
| + | ||
| + if (intervalCounter > biggestInterval) { |
ryanofsky
Oct 5, 2017
Contributor
Thread #8501 (comment)
Should follow up by removing this or adding comments suggested above.
| + size_t maxAmountOfSamples = maxMem / sampleSize; | ||
| + | ||
| + // distribute the max samples equal between precision levels | ||
| + unsigned int samplesPerPrecision = maxAmountOfSamples / sizeof(precisionIntervals) / sizeof(precisionIntervals[0]); |
ryanofsky
Oct 5, 2017
Contributor
Should be * sizeof(precisionIntervals[0])? And maybe multiplication should precede division for less rounding error.
| + samplesPerPrecision = std::max(MIN_SAMPLES, samplesPerPrecision); | ||
| + samplesPerPrecision = std::min(MAX_SAMPLES, samplesPerPrecision); | ||
| + for (unsigned int i = 0; i < sizeof(precisionIntervals) / sizeof(precisionIntervals[0]); i++) { | ||
| + vMaxSamplesPerPrecision[i] = samplesPerPrecision; |
ryanofsky
Oct 5, 2017
Contributor
Should this acquire cs_mempool_stats before changing these values?
| + std::atomic<uint64_t> startTime; //start time | ||
| + std::vector<MempoolSamplesVector> vSamplesPerPrecision; //!<multiple samples vector per precision level | ||
| + std::vector<size_t> vMaxSamplesPerPrecision; //!<vector with the maximum amount of samples per precision level | ||
| + std::vector<size_t> vTimeLastSample; //!<vector with the maximum amount of samples per precision level |
ryanofsky
Oct 5, 2017
Contributor
Comment is wrong, and type should probably be int64_t, as returned by GetTime.
| + std::atomic<uint64_t> startTime; //start time | ||
| + std::vector<MempoolSamplesVector> vSamplesPerPrecision; //!<multiple samples vector per precision level | ||
| + std::vector<size_t> vMaxSamplesPerPrecision; //!<vector with the maximum amount of samples per precision level | ||
| + std::vector<size_t> vTimeLastSample; //!<vector with the maximum amount of samples per precision level |
ryanofsky
Oct 5, 2017
Contributor
Do you think having parallel lists is more readable in c++ and having one list is more readable in json? Also, why are the first two vectors labeled perPrecision, and the third one not? I guess I think something like the following would be less ambiguous and duplicative:
struct PrecisionLevel {
std::vector<CStatsMempoolSample> samples;
size_t max_num_samples;
int64_t last_sample_time;
};
std::vector<PrecisionLevel> m_precision_levels;Could also rename cs_mempool_stats to cs_precision_levels to make it clearer what the lock is protecting.
| + # test mempool stats | ||
| + time.sleep(15) | ||
| + mps = self.nodes[0].getmempoolstats() | ||
| + assert_equal(len(mps), 3) #fixed amount of percision levels |
jonasschnelli commentedAug 12, 2016
•
Edited 1 time
-
jonasschnelli
Aug 12, 2016
This PR adds a statistics collector class which aims to collect various types of statistics up to the configurable maximum memory target. At the moment, only mempool statistics will be collected.
Motivation
Adding more statistics and visualization to the GUI would leverage its usage. To do so, we need stats that are collected even when the visualization is not visible (example: the GUI network graph will only draw data when it's visible which is kinda unusable)
How it works
This PR adds a simple stats manager that polls stats over a repetitive
CSchedulertask.The samples are not guaranteed to be const-time. Each sample contains a time delta to the last one (uint16_t).
API
-statsenabledefault disabled-statsmaxmemorytarget10MB default maximal memory target to use for statistics.getmempoolstats==
[ { "percision_interval": 2, "time_from": 1494252401, "samples": [ [ 11, 1, 1008, 0 ], .... }... ]Features
-> CScheduler driven sample collecting (current interval 2000ms)
-> Relevant mempool data (size, memory requirement, minfee) gets written to an atomic cache (no additional locking required)
-> Multiple precision levels (currently three, 2s, 60s, 1800s)
-> Memory target that will calculate how many samples do fit in the given target
-> Sample do only have a 2byte time-delta to the last sample, allowing to save some memory
-> Flexible design, adding more data-points should be simple (bandwidth, utxo-stats, etc.).