20#include <test/jtx/Env.h>
21#include <test/jtx/TestHelpers.h>
23#include <xrpld/perflog/PerfLog.h>
24#include <xrpld/rpc/detail/Handler.h>
26#include <xrpl/basics/random.h>
27#include <xrpl/beast/unit_test.h>
28#include <xrpl/beast/utility/Journal.h>
29#include <xrpl/json/json_reader.h>
30#include <xrpl/protocol/jss.h>
46 using path = boost::filesystem::path;
69 using namespace boost::filesystem;
76 if (!exists(dir) || !is_directory(dir) || !is_empty(dir))
92 using namespace boost::filesystem;
93 return temp_directory_path() /
"perf_log_test_dir";
99 return logDir() /
"perf_log.txt";
123 using namespace boost::filesystem;
136 secondSize = file_size(
path);
137 }
while (firstSize >= secondSize);
142 }
while (secondSize >= file_size(
path));
178 cur.isMember(jss::job) ? cur[jss::job].asString()
179 : cur[jss::method].asString());
187 [](
Cur const& lhs,
Cur const& rhs) {
188 if (lhs.dur != rhs.dur)
189 return (rhs.dur < lhs.dur);
190 return (lhs.name < rhs.name);
199 using namespace boost::filesystem;
204 BEAST_EXPECT(!exists(fixture.logFile()));
208 BEAST_EXPECT(fixture.stopSignaled ==
false);
209 BEAST_EXPECT(exists(fixture.logFile()));
216 if (!BEAST_EXPECT(!exists(fixture.logDir())))
223 fixture.logDir().c_str(), std::ios::out | std::ios::app);
224 if (!BEAST_EXPECT(nastyFile))
231 BEAST_EXPECT(fixture.stopSignaled ==
false);
233 BEAST_EXPECT(fixture.stopSignaled ==
true);
243 remove(fixture.logDir());
250 if (!BEAST_EXPECT(!exists(fixture.logDir())))
255 boost::system::error_code ec;
256 boost::filesystem::create_directories(fixture.logDir(), ec);
257 if (!BEAST_EXPECT(!ec))
260 auto fileWriteable = [](boost::filesystem::path
const& p) ->
bool {
261 return std::ofstream{p.c_str(), std::ios::out | std::ios::app}
265 if (!BEAST_EXPECT(fileWriteable(fixture.logFile())))
268 boost::filesystem::permissions(
270 perms::remove_perms | perms::owner_write | perms::others_write |
275 if (fileWriteable(fixture.logFile()))
277 log <<
"Unable to write protect file. Test skipped."
284 BEAST_EXPECT(fixture.stopSignaled ==
false);
286 BEAST_EXPECT(fixture.stopSignaled ==
true);
296 boost::filesystem::permissions(
298 perms::add_perms | perms::owner_write | perms::others_write |
309 auto perfLog{fixture.perfLog(withFile)};
339 for (
int labelIndex = 0; labelIndex < labels.
size(); ++labelIndex)
341 for (
int idIndex = 0; idIndex < 2; ++idIndex)
345 labels[labelIndex], ids[(labelIndex * 2) + idIndex]);
350 Json::Value const countersJson{perfLog->countersJson()[jss::rpc]};
351 BEAST_EXPECT(countersJson.size() == labels.
size() + 1);
352 for (
auto& label : labels)
356 BEAST_EXPECT(counter[jss::duration_us] ==
"0");
357 BEAST_EXPECT(counter[jss::errored] ==
"0");
358 BEAST_EXPECT(counter[jss::finished] ==
"0");
359 BEAST_EXPECT(counter[jss::started] ==
"2");
362 Json::Value const& total{countersJson[jss::total]};
363 BEAST_EXPECT(total[jss::duration_us] ==
"0");
364 BEAST_EXPECT(total[jss::errored] ==
"0");
365 BEAST_EXPECT(total[jss::finished] ==
"0");
374 BEAST_EXPECT(currents.size() == labels.
size() * 2);
377 for (
int i = 0; i < currents.size(); ++i)
379 BEAST_EXPECT(currents[i].name == labels[i / 2]);
380 BEAST_EXPECT(prevDur > currents[i].dur);
381 prevDur = currents[i].dur;
388 for (
int labelIndex = labels.
size() - 1; labelIndex > 0; --labelIndex)
391 perfLog->rpcFinish(labels[labelIndex], ids[(labelIndex * 2) + 1]);
393 perfLog->rpcError(labels[labelIndex], ids[(labelIndex * 2) + 0]);
395 perfLog->rpcFinish(labels[0], ids[0 + 1]);
398 auto validateFinalCounters = [
this, &labels](
401 Json::Value const& jobQueue = countersJson[jss::job_queue];
403 BEAST_EXPECT(jobQueue.
size() == 0);
407 BEAST_EXPECT(
rpc.size() == labels.
size() + 1);
415 BEAST_EXPECT(first[jss::duration_us] !=
"0");
416 BEAST_EXPECT(first[jss::errored] ==
"0");
417 BEAST_EXPECT(first[jss::finished] ==
"1");
418 BEAST_EXPECT(first[jss::started] ==
"2");
423 for (
int i = 1; i < labels.
size(); ++i)
428 BEAST_EXPECT(dur != 0 && dur < prevDur);
430 BEAST_EXPECT(counter[jss::errored] ==
"1");
431 BEAST_EXPECT(counter[jss::finished] ==
"1");
432 BEAST_EXPECT(counter[jss::started] ==
"2");
437 BEAST_EXPECT(total[jss::duration_us] !=
"0");
445 auto validateFinalCurrent = [
this,
448 Json::Value const& job_queue = currentJson[jss::jobs];
449 BEAST_EXPECT(job_queue.
isArray());
450 BEAST_EXPECT(job_queue.
size() == 0);
453 Json::Value const& methods = currentJson[jss::methods];
454 BEAST_EXPECT(methods.
size() == 1);
455 BEAST_EXPECT(methods.
isArray());
458 BEAST_EXPECT(only.
size() == 2);
460 BEAST_EXPECT(only[jss::duration_us] !=
"0");
461 BEAST_EXPECT(only[jss::method] == labels[0]);
465 validateFinalCounters(perfLog->countersJson());
466 validateFinalCurrent(perfLog->currentJson());
474 auto const fullPath = fixture.logFile();
478 BEAST_EXPECT(!exists(fullPath));
492 lastLine = std::move(line);
502 validateFinalCounters(parsedLastLine[jss::counters]);
503 validateFinalCurrent(parsedLastLine[jss::current_activities]);
515 auto perfLog{fixture.perfLog(withFile)};
526 : type(t), typeName(std::move(name))
535 for (
auto const& job : jobTypes)
544 for (
int i = 0; i < jobs.
size(); ++i)
546 perfLog->jobQueue(jobs[i].type);
548 perfLog->countersJson()[jss::job_queue]};
550 BEAST_EXPECT(jq_counters.size() == i + 2);
551 for (
int j = 0; j <= i; ++j)
555 Json::Value const& counter{jq_counters[jobs[j].typeName]};
556 BEAST_EXPECT(counter.size() == 5);
557 BEAST_EXPECT(counter[jss::queued] ==
"1");
558 BEAST_EXPECT(counter[jss::started] ==
"0");
559 BEAST_EXPECT(counter[jss::finished] ==
"0");
560 BEAST_EXPECT(counter[jss::queued_duration_us] ==
"0");
561 BEAST_EXPECT(counter[jss::running_duration_us] ==
"0");
566 BEAST_EXPECT(total.size() == 5);
567 BEAST_EXPECT(
jsonToUint64(total[jss::queued]) == i + 1);
568 BEAST_EXPECT(total[jss::started] ==
"0");
569 BEAST_EXPECT(total[jss::finished] ==
"0");
570 BEAST_EXPECT(total[jss::queued_duration_us] ==
"0");
571 BEAST_EXPECT(total[jss::running_duration_us] ==
"0");
577 BEAST_EXPECT(
current.size() == 2);
578 BEAST_EXPECT(
current.isMember(jss::jobs));
579 BEAST_EXPECT(
current[jss::jobs].size() == 0);
580 BEAST_EXPECT(
current.isMember(jss::methods));
581 BEAST_EXPECT(
current[jss::methods].size() == 0);
587 perfLog->resizeJobs(jobs.
size() * 2);
593 for (
int i = 0; i < jobs.
size(); ++i)
596 jobs[i].type,
microseconds{i + 1}, steady_clock::now(), i * 2);
601 perfLog->countersJson()[jss::job_queue]};
602 for (
int j = 0; j < jobs.
size(); ++j)
604 Json::Value const& counter{jq_counters[jobs[j].typeName]};
609 BEAST_EXPECT(counter[jss::started] ==
"2");
610 BEAST_EXPECT(queued_dur_us == j + 1);
614 BEAST_EXPECT(counter[jss::started] ==
"1");
615 BEAST_EXPECT(queued_dur_us == j + 1);
619 BEAST_EXPECT(counter[jss::started] ==
"0");
620 BEAST_EXPECT(queued_dur_us == 0);
623 BEAST_EXPECT(counter[jss::queued] ==
"1");
624 BEAST_EXPECT(counter[jss::finished] ==
"0");
625 BEAST_EXPECT(counter[jss::running_duration_us] ==
"0");
631 BEAST_EXPECT(
jsonToUint64(total[jss::started]) == (i * 2) + 1);
632 BEAST_EXPECT(total[jss::finished] ==
"0");
637 (((i * i) + 3 * i + 2) / 2));
638 BEAST_EXPECT(total[jss::running_duration_us] ==
"0");
653 BEAST_EXPECT(currents.size() == (i + 1) * 2);
656 for (
int j = 0; j <= i; ++j)
658 BEAST_EXPECT(currents[j * 2].name == jobs[j].typeName);
659 BEAST_EXPECT(prevDur > currents[j * 2].dur);
660 prevDur = currents[j * 2].dur;
662 BEAST_EXPECT(currents[(j * 2) + 1].name == jobs[j].typeName);
663 BEAST_EXPECT(prevDur > currents[(j * 2) + 1].dur);
664 prevDur = currents[(j * 2) + 1].dur;
669 for (
int i = jobs.
size() - 1; i >= 0; --i)
673 int const finished = ((jobs.
size() - i) * 2) - 1;
679 perfLog->countersJson()[jss::job_queue]};
680 for (
int j = 0; j < jobs.
size(); ++j)
682 Json::Value const& counter{jq_counters[jobs[j].typeName]};
687 BEAST_EXPECT(counter[jss::finished] ==
"0");
688 BEAST_EXPECT(running_dur_us == 0);
692 BEAST_EXPECT(counter[jss::finished] ==
"1");
693 BEAST_EXPECT(running_dur_us == ((jobs.
size() - j) * 2) - 1);
697 BEAST_EXPECT(counter[jss::finished] ==
"2");
698 BEAST_EXPECT(running_dur_us == ((jobs.
size() - j) * 4) - 1);
703 BEAST_EXPECT(queued_dur_us == j + 1);
704 BEAST_EXPECT(counter[jss::queued] ==
"1");
705 BEAST_EXPECT(counter[jss::started] ==
"2");
713 BEAST_EXPECT(
jsonToUint64(total[jss::finished]) == finished);
717 int const queuedDur = ((jobs.
size() * (jobs.
size() + 1)) / 2);
719 jsonToUint64(total[jss::queued_duration_us]) == queuedDur);
722 int const runningDur = ((finished * (finished + 1)) / 2);
736 BEAST_EXPECT(currents.size() == i * 2);
739 for (
int j = 0; j < i; ++j)
741 BEAST_EXPECT(currents[j * 2].name == jobs[j].typeName);
742 BEAST_EXPECT(prevDur > currents[j * 2].dur);
743 prevDur = currents[j * 2].dur;
745 BEAST_EXPECT(currents[(j * 2) + 1].name == jobs[j].typeName);
746 BEAST_EXPECT(prevDur > currents[(j * 2) + 1].dur);
747 prevDur = currents[(j * 2) + 1].dur;
752 auto validateFinalCounters = [
this,
756 BEAST_EXPECT(
rpc.isObject());
757 BEAST_EXPECT(
rpc.size() == 0);
760 Json::Value const& jobQueue = countersJson[jss::job_queue];
761 for (
int i = jobs.
size() - 1; i >= 0; --i)
763 Json::Value const& counter{jobQueue[jobs[i].typeName]};
766 BEAST_EXPECT(running_dur_us == ((jobs.
size() - i) * 4) - 1);
770 BEAST_EXPECT(queued_dur_us == i + 1);
772 BEAST_EXPECT(counter[jss::queued] ==
"1");
773 BEAST_EXPECT(counter[jss::started] ==
"2");
774 BEAST_EXPECT(counter[jss::finished] ==
"2");
779 int const finished = jobs.
size() * 2;
781 BEAST_EXPECT(
jsonToUint64(total[jss::started]) == finished);
782 BEAST_EXPECT(
jsonToUint64(total[jss::finished]) == finished);
786 int const queuedDur = ((jobs.
size() * (jobs.
size() + 1)) / 2);
788 jsonToUint64(total[jss::queued_duration_us]) == queuedDur);
791 int const runningDur = ((finished * (finished + 1)) / 2);
793 jsonToUint64(total[jss::running_duration_us]) == runningDur);
796 auto validateFinalCurrent = [
this](
Json::Value const& currentJson) {
800 BEAST_EXPECT(j.
size() == 0);
803 Json::Value const& methods = currentJson[jss::methods];
804 BEAST_EXPECT(methods.
size() == 0);
805 BEAST_EXPECT(methods.
isArray());
809 validateFinalCounters(perfLog->countersJson());
810 validateFinalCurrent(perfLog->currentJson());
819 auto const fullPath = fixture.logFile();
823 BEAST_EXPECT(!exists(fullPath));
837 lastLine = std::move(line);
847 validateFinalCounters(parsedLastLine[jss::counters]);
848 validateFinalCurrent(parsedLastLine[jss::current_activities]);
862 auto perfLog{fixture.perfLog(withFile)};
872 auto iter{jobTypes.begin()};
875 jobType = iter->second.type();
876 jobTypeName = iter->second.name();
880 perfLog->resizeJobs(1);
883 auto verifyCounters = [
this, jobTypeName](
889 BEAST_EXPECT(countersJson.
isObject());
890 BEAST_EXPECT(countersJson.
size() == 2);
892 BEAST_EXPECT(countersJson.
isMember(jss::rpc));
893 BEAST_EXPECT(countersJson[jss::rpc].isObject());
894 BEAST_EXPECT(countersJson[jss::rpc].size() == 0);
896 BEAST_EXPECT(countersJson.
isMember(jss::job_queue));
897 BEAST_EXPECT(countersJson[jss::job_queue].isObject());
898 BEAST_EXPECT(countersJson[jss::job_queue].size() == 1);
901 countersJson[jss::job_queue][jobTypeName]};
903 BEAST_EXPECT(job.isObject());
905 BEAST_EXPECT(
jsonToUint64(job[jss::started]) == started);
906 BEAST_EXPECT(
jsonToUint64(job[jss::finished]) == finished);
909 jsonToUint64(job[jss::queued_duration_us]) == queued_us);
911 jsonToUint64(job[jss::running_duration_us]) == running_us);
916 auto verifyEmptyCurrent = [
this](
Json::Value const& currentJson) {
917 BEAST_EXPECT(currentJson.isObject());
918 BEAST_EXPECT(currentJson.size() == 2);
920 BEAST_EXPECT(currentJson.isMember(jss::jobs));
921 BEAST_EXPECT(currentJson[jss::jobs].isArray());
922 BEAST_EXPECT(currentJson[jss::jobs].size() == 0);
924 BEAST_EXPECT(currentJson.isMember(jss::methods));
925 BEAST_EXPECT(currentJson[jss::methods].isArray());
926 BEAST_EXPECT(currentJson[jss::methods].size() == 0);
930 perfLog->jobStart(jobType,
microseconds{11}, steady_clock::now(), 2);
932 verifyCounters(perfLog->countersJson(), 1, 0, 11, 0);
933 verifyEmptyCurrent(perfLog->currentJson());
936 perfLog->jobStart(jobType,
microseconds{13}, steady_clock::now(), -1);
938 verifyCounters(perfLog->countersJson(), 2, 0, 24, 0);
939 verifyEmptyCurrent(perfLog->currentJson());
944 verifyCounters(perfLog->countersJson(), 2, 1, 24, 17);
945 verifyEmptyCurrent(perfLog->currentJson());
950 verifyCounters(perfLog->countersJson(), 2, 2, 24, 36);
951 verifyEmptyCurrent(perfLog->currentJson());
960 auto const fullPath = fixture.logFile();
964 BEAST_EXPECT(!exists(fullPath));
978 lastLine = std::move(line);
988 verifyCounters(parsedLastLine[jss::counters], 2, 2, 24, 36);
989 verifyEmptyCurrent(parsedLastLine[jss::current_activities]);
999 using namespace boost::filesystem;
1002 BEAST_EXPECT(!exists(fixture.logDir()));
1004 auto perfLog{fixture.perfLog(withFile)};
1006 BEAST_EXPECT(fixture.stopSignaled ==
false);
1009 BEAST_EXPECT(!exists(fixture.logDir()));
1013 BEAST_EXPECT(exists(fixture.logFile()));
1014 BEAST_EXPECT(file_size(fixture.logFile()) == 0);
1022 decltype(file_size(fixture.logFile())) firstFileSize{0};
1025 BEAST_EXPECT(!exists(fixture.logDir()));
1029 firstFileSize = file_size(fixture.logFile());
1030 BEAST_EXPECT(firstFileSize > 0);
1041 BEAST_EXPECT(!exists(fixture.logDir()));
1045 BEAST_EXPECT(file_size(fixture.logFile()) > firstFileSize);
1064BEAST_DEFINE_TESTSUITE(PerfLog, basics,
ripple);
T back_inserter(T... args)
Unserialize a JSON document into a Value.
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
UInt size() const
Number of values in array or object.
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
log_os< char > log
Logging output stream.
virtual beast::Journal journal(std::string const &name)=0
static JobTypes const & instance()
void testRotate(WithFile withFile)
static std::uint64_t jsonToUint64(Json::Value const &jsonUintAsString)
void testJobs(WithFile withFile)
void testInvalidID(WithFile withFile)
void run() override
Runs the suite.
void testRPC(WithFile withFile)
static std::vector< Cur > getSortedCurrent(Json::Value const ¤tJson)
boost::filesystem::path path
A transaction testing environment.
Set the expected result code for a JTx The test will fail if the code doesn't match.
T emplace_back(T... args)
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
std::set< char const * > getHandlerNames()
Return names of all methods.
std::unique_ptr< PerfLog > make_PerfLog(PerfLog::Setup const &setup, Application &app, beast::Journal journal, std::function< void()> &&signalStop)
auto make_vector(Input const &input)
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
@ current
This was a new validation and was added.
beast::xor_shift_engine & default_prng()
Return the default random engine.
Cur(std::uint64_t d, std::string n)
Fixture(Application &app, beast::Journal j)
std::unique_ptr< perf::PerfLog > perfLog(WithFile withFile)
std::chrono::milliseconds logInterval() const
Configuration from [perf] section of rippled.cfg.