# # # patch "NEWS" # from [a9412ab044bd25db67edc1625131737f1450e45a] # to [b883957ce622f6c3054feff841488eb971a9e79d] # # patch "cmd_db.cc" # from [ddc1958a87b2e73ffb96f2d1774340d1d42810b1] # to [1ed3e071783afad66d6bb81ee3b8b30604f15c8f] # # patch "database.cc" # from [d0da4979961345a3f6f4b68769910f3ba3943f81] # to [72d5286b3568dce70e0e1b281eadf8eda3f787d4] # # patch "database.hh" # from [40e06a31c4d18cf50a9a3f3f46bb2d14df06fe68] # to [31ba75343ca21813eb814b814d6576440f4f0cfc] # ============================================================ --- NEWS a9412ab044bd25db67edc1625131737f1450e45a +++ NEWS b883957ce622f6c3054feff841488eb971a9e79d @@ -1,6 +1,11 @@ conflicts, move after landing on mainlin New stuff from nvm.dates* (written on top so as to avoid tedious merge conflicts, move after landing on mainline): + New features + + - Additional '--full' option for 'mtn db info' to display some + statistic analysis of the date certs in the database. + Internal - Using 64 bit integer values to represent dates internally. This ============================================================ --- cmd_db.cc ddc1958a87b2e73ffb96f2d1774340d1d42810b1 +++ cmd_db.cc 1ed3e071783afad66d6bb81ee3b8b30604f15c8f @@ -50,13 +50,13 @@ CMD(db_info, "info", "", CMD_REF(db), "" CMD(db_info, "info", "", CMD_REF(db), "", N_("Shows information about the database"), "", - options::opts::none) + options::opts::full) { N(args.size() == 0, F("no arguments needed")); database db(app); - db.info(cout); + db.info(cout, app.opts.full); } CMD(db_version, "version", "", CMD_REF(db), "", ============================================================ --- database.cc d0da4979961345a3f6f4b68769910f3ba3943f81 +++ database.cc 72d5286b3568dce70e0e1b281eadf8eda3f787d4 @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include "vector.hh" @@ -79,6 +80,7 @@ using std::vector; using std::set; using std::string; using std::vector; +using std::accumulate; using boost::shared_ptr; using boost::shared_dynamic_cast; @@ -840,7 +842,7 @@ void void -database::info(ostream & out) +database::info(ostream & out, bool analyze) { // don't check the schema ensure_open_for_maintenance(); @@ -971,6 +973,157 @@ database::info(ostream & out) form = form % imp->cache_size(); out << form.str() << '\n'; // final newline is kept out of the translation + + // the following analyzation is only done for --full info + if (!analyze) + return; + + + typedef map rev_date; + rev_date rd; + vector certs; + + L(FL("fetching revision dates")); + imp->get_certs(date_cert_name, certs, "revision_certs"); + + L(FL("analyzing revision dates")); + rev_date::iterator d; + for (vector::iterator i = certs.begin(); i != certs.end(); ++i) + { + date_t cert_date; + try + { + cert_date = date_t::from_string(i->value()); + } + catch (informative_failure & e) + { + // simply skip dates we cannot parse + W(F("invalid date: %s for revision %s, skipped") + % i->value() % i->ident); + } + + if (cert_date.valid()) + { + if ((d = rd.find(i->ident)) == rd.end()) + rd.insert(make_pair(i->ident, cert_date)); + else + { + if (d->second > cert_date) + d->second = cert_date; + } + } + } + + L(FL("fetching ancestry map")); + typedef multimap::const_iterator gi; + rev_ancestry_map graph; + get_revision_ancestry(graph); + + L(FL("checking timestamps differences of related revisions")); + int correct = 0, + equal = 0, + incorrect = 0, + root_anc = 0, + missing = 0; + + vector diffs; + u64 diff_sum = 0; + + for (gi i = graph.begin(); i != graph.end(); ++i) + { + revision_id anc_rid = i->first, + desc_rid = i->second; + + if (null_id(anc_rid)) + { + root_anc++; + continue; + } + I(!null_id(desc_rid)); + + date_t anc_date, + desc_date; + + map::iterator j; + if ((j = rd.find(anc_rid)) != rd.end()) + anc_date = j->second; + + if ((j = rd.find(desc_rid)) != rd.end()) + desc_date = j->second; + + if (anc_date.valid() && desc_date.valid()) + { + // we only need seconds precision here + s64 diff = (desc_date - anc_date) / 1000; + diffs.push_back(diff); + + if (anc_date < desc_date) + correct++; + else if (anc_date == desc_date) + equal++; + else + { + L(FL(" rev %s -> rev %s") % anc_rid % desc_rid); + L(FL(" but date %s ! -> %s") + % anc_date.as_iso_8601_extended() + % desc_date.as_iso_8601_extended()); + L(FL(" (difference: %d seconds)") + % (anc_date.as_unix_epoch() - desc_date.as_unix_epoch())); + diff_sum += anc_date.as_unix_epoch() - + desc_date.as_unix_epoch(); + incorrect++; + } + } + else + missing++; + } + + form = + F("timestamp correctness between revisions\n" + " correct dates : %s edges\n" + " equal dates : %s edges\n" + " incorrect dates : %s edges\n" + " based on root : %s edges\n" + " missing date(s) : %s edges\n" + "\n" + "timestamp differences between revisions:\n" + " mean : %d sec\n" + " min : %d sec\n" + " max : %d sec\n" + "\n" + " 1st percentile : %s sec\n" + " 5th percentile : %s sec\n" + " 10th percentile : %s sec\n" + " 25th percentile : %s sec\n" + " 50th percentile : %s sec\n" + " 75th percentile : %s sec\n" + " 90th percentile : %s sec\n" + " 95th percentile : %s sec\n" + " 99th percentile : %s sec\n" + ); + + form = form % correct % equal % incorrect % root_anc % missing; + + // sort, so that we can get percentile values + sort(diffs.begin(), diffs.end()); + + // calculate mean time difference, output that, min and max + s64 mean = accumulate(diffs.begin(), diffs.end(), 0); + mean /= diffs.size(); + s64 median = *(diffs.begin() + diffs.size()/2); + form = form % mean % *diffs.begin() % *diffs.rbegin() + % *(diffs.begin() + int(diffs.size() * 0.01)) + % *(diffs.begin() + int(diffs.size() * 0.05)) + % *(diffs.begin() + int(diffs.size() * 0.10)) + % *(diffs.begin() + int(diffs.size() * 0.25)) + % *(diffs.begin() + int(diffs.size() * 0.50)) + % *(diffs.begin() + int(diffs.size() * 0.75)) + % *(diffs.begin() + int(diffs.size() * 0.90)) + % *(diffs.begin() + int(diffs.size() * 0.95)) + % *(diffs.begin() + int(diffs.size() * 0.99)); + + // output the string, with some newlines out of translation + out << '\n' << '\n' << form.str() << '\n'; } void ============================================================ --- database.hh 40e06a31c4d18cf50a9a3f3f46bb2d14df06fe68 +++ database.hh 31ba75343ca21813eb814b814d6576440f4f0cfc @@ -380,7 +380,7 @@ public: void debug(std::string const & sql, std::ostream & out); void dump(std::ostream &); void load(std::istream &); - void info(std::ostream &); + void info(std::ostream &, bool analyze); void version(std::ostream &); void migrate(key_store &); void test_migration_step(key_store &, std::string const &);