polardbxengine/storage/ndb/tools/ndb_index_stat.cpp

733 lines
20 KiB
C++

/* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2.0,
as published by the Free Software Foundation.
This program is also distributed with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation. The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have included with MySQL.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License, version 2.0, for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include <ndb_global.h>
#include <ndb_opts.h>
#include <NdbOut.hpp>
#include <NdbApi.hpp>
#include <NDBT.hpp>
#include <NdbIndexStatImpl.hpp>
#include <ndb_rand.h>
// stats options
static const char* _dbname = 0;
static bool _delete = false;
static bool _update = false;
static bool _dump = false;
static int _query = 0;
static int _stats_any = 0;
// sys options
static bool _sys_drop = false;
static bool _sys_create = false;
static bool _sys_create_if_not_exist = false;
static bool _sys_create_if_not_valid = false;
static bool _sys_check = false;
static bool _sys_skip_tables = false;
static bool _sys_skip_events = false;
static int _sys_any = 0;
// other
static bool _verbose = false;
static int _loops = 1;
static Ndb_cluster_connection* g_ncc = 0;
static Ndb* g_ndb = 0;
static Ndb* g_ndb_sys = 0;
static NdbDictionary::Dictionary* g_dic = 0;
static NdbIndexStat* g_is = 0;
static const char* g_tabname = 0;
static const NdbDictionary::Table* g_tab = 0;
static int g_indcount = 0;
static const char** g_indnames = 0;
static const NdbDictionary::Index** g_indlist = 0;
// current index in loop
static const char* g_indname = 0;
static const NdbDictionary::Index* g_ind = 0;
#define CHK1(b) \
if (!(b)) { \
ret = -1; \
break; \
}
#define CHK2(b, e) \
if (!(b)) { \
g_err << "ERR: " << #b << " failed at line " << __LINE__ \
<< ": " << e << endl; \
ret = -1; \
break; \
}
static NdbError
getNdbError(Ndb_cluster_connection* ncc)
{
NdbError err;
err.code = g_ncc->get_latest_error();
err.message = g_ncc->get_latest_error_msg();
return err;
}
static int
doconnect()
{
int ret = 0;
do
{
g_ncc = new Ndb_cluster_connection(opt_ndb_connectstring);
CHK2(g_ncc->connect(opt_connect_retries - 1, opt_connect_retry_delay) == 0, getNdbError(g_ncc));
CHK2(g_ncc->wait_until_ready(30, 10) == 0, getNdbError(g_ncc));
if (!_sys_any)
{
g_ndb = new Ndb(g_ncc, _dbname);
CHK2(g_ndb->init() == 0, g_ndb->getNdbError());
CHK2(g_ndb->waitUntilReady(30) == 0, g_ndb->getNdbError());
g_dic = g_ndb->getDictionary();
}
g_ndb_sys = new Ndb(g_ncc, NDB_INDEX_STAT_DB);
CHK2(g_ndb_sys->init() == 0, g_ndb_sys->getNdbError());
CHK2(g_ndb_sys->waitUntilReady(30) == 0, g_ndb_sys->getNdbError());
g_is = new NdbIndexStat;
g_info << "connected" << endl;
}
while (0);
return ret;
}
static void
dodisconnect()
{
delete g_is;
delete g_ndb_sys;
delete g_ndb;
delete g_ncc;
g_info << "disconnected" << endl;
}
static const char*
format(Uint64 us64, char* buf)
{
Uint32 ms = (Uint32)(us64 / (Uint64)1000);
Uint32 us = (Uint32)(us64 % (Uint64)1000);
sprintf(buf, "%u.%03u", ms, us);
return buf;
}
static const char*
format(double x, char* buf)
{
sprintf(buf, "%.02f", x);
return buf;
}
static void
show_head(const NdbIndexStat::Head& head)
{
setOutputLevel(2);
g_info << "table:" << g_tabname;
g_info << " index:" << g_indname;
g_info << " fragCount:" << head.m_fragCount;
g_info << endl;
g_info << "sampleVersion:" << head.m_sampleVersion;
g_info << " loadTime:" << head.m_loadTime;
g_info << " sampleCount:" << head.m_sampleCount;
g_info << " keyBytes:" << head.m_keyBytes;
g_info << endl;
setOutputLevel(_verbose ? 2 : 0);
}
static void
show_cache_info(const char* name, const NdbIndexStat::CacheInfo& info)
{
Uint64 us64;
char buf[100];
setOutputLevel(2);
g_info << name << ":";
g_info << " valid:" << info.m_valid;
g_info << " sampleCount:" << info.m_sampleCount;
g_info << " totalBytes:" << info.m_totalBytes;
g_info << endl;
g_info << "times in ms:";
g_info << " save: " << format(info.m_save_time, buf);
g_info << " sort: " << format(info.m_sort_time, buf);
if (info.m_sampleCount != 0)
{
us64 = info.m_sort_time / (Uint64)info.m_sampleCount;
g_info << " sort per sample: " << format(us64, buf);
}
g_info << endl;
setOutputLevel(_verbose ? 2 : 0);
}
static void
show_cache_entry(const NdbIndexStatImpl::CacheIter& iter)
{
setOutputLevel(2);
const NdbPack::DataC& key = iter.m_keyData;
const NdbPack::DataC& value = iter.m_valueData;
char buf[8000];
key.print(buf, sizeof(buf));
g_info << "key:" << buf << endl;
value.print(buf, sizeof(buf));
g_info << "value:" << buf << endl;
setOutputLevel(_verbose ? 2 : 0);
}
static int
doquery()
{
int ret = 0;
char buf[100];
Uint8 b_lo_buffer[NdbIndexStat::BoundBufferBytes];
Uint8 b_hi_buffer[NdbIndexStat::BoundBufferBytes];
NdbIndexStat::Bound b_lo(g_is, b_lo_buffer);
NdbIndexStat::Bound b_hi(g_is, b_hi_buffer);
do
{
NdbIndexStat::Range r(b_lo, b_hi);
Uint8 s_buffer[NdbIndexStat::StatBufferBytes];
NdbIndexStat::Stat s(s_buffer);
for (int n = 0; n < _query; n++)
{
g_is->reset_range(r);
for (int i = 0; i <= 1; i++)
{
NdbIndexStat::Bound& b = (i == 0 ? b_lo : b_hi);
if (ndb_rand() % 3 != 0)
{
if (ndb_rand() % 3 != 0)
{
Uint32 x = ndb_rand();
CHK2(g_is->add_bound(b, &x) == 0, g_is->getNdbError());
}
else
{
CHK2(g_is->add_bound_null(b) == 0, g_is->getNdbError());
}
bool strict = (ndb_rand() % 2 == 0);
g_is->set_bound_strict(b, strict);
}
}
CHK2(ret == 0, "failed");
CHK2(g_is->finalize_range(r) == 0, g_is->getNdbError());
CHK2(g_is->query_stat(r, s) == 0, g_is->getNdbError());
double rir = -1.0;
NdbIndexStat::get_rir(s, &rir);
g_info << "rir: " << format(rir, buf) << endl;
}
CHK2(ret == 0, "failed");
}
while (0);
return ret;
}
static int
dostats(int i)
{
int ret = 0;
do
{
g_indname = g_indnames[i];
g_ind = g_indlist[i];
g_is->reset_index();
CHK2(g_is->set_index(*g_ind, *g_tab) == 0, g_is->getNdbError());
if (_delete)
{
g_info << g_indname << ": delete stats" << endl;
if (ndb_rand() % 2 == 0)
{
CHK2(g_dic->deleteIndexStat(*g_ind, *g_tab) == 0, g_dic->getNdbError());
}
else
{
CHK2(g_is->delete_stat(g_ndb_sys) == 0, g_is->getNdbError());
}
}
if (_update)
{
g_info << g_indname << ": update stats" << endl;
if (ndb_rand() % 2 == 0)
{
CHK2(g_dic->updateIndexStat(*g_ind, *g_tab) == 0, g_dic->getNdbError());
}
else
{
CHK2(g_is->update_stat(g_ndb_sys) == 0, g_is->getNdbError());
}
}
NdbIndexStat::Head head;
g_is->read_head(g_ndb_sys);
g_is->get_head(head);
CHK2(head.m_found != -1, g_is->getNdbError());
if (head.m_found == false)
{
g_info << "no stats" << endl;
break;
}
show_head(head);
g_info << "read stats" << endl;
CHK2(g_is->read_stat(g_ndb_sys) == 0, g_is->getNdbError());
g_is->move_cache();
g_is->clean_cache();
g_info << "query cache created" << endl;
NdbIndexStat::CacheInfo infoQuery;
g_is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery);
show_cache_info("query cache", infoQuery);
if (_dump)
{
NdbIndexStatImpl& impl = g_is->getImpl();
NdbIndexStatImpl::CacheIter iter(impl);
CHK2(impl.dump_cache_start(iter) == 0, g_is->getNdbError());
while (impl.dump_cache_next(iter) == true)
{
show_cache_entry(iter);
}
}
if (_query > 0)
{
CHK2(doquery() == 0, "failed");
}
}
while (0);
return ret;
}
static int
dostats()
{
int ret = 0;
do
{
for (int i = 0; i < g_indcount; i++)
{
CHK1(dostats(i) == 0);
}
CHK1(ret == 0);
}
while (0);
return ret;
}
static int
checkobjs()
{
int ret = 0;
do
{
CHK2((g_tab = g_dic->getTable(g_tabname)) != 0,
g_tabname << ": " << g_dic->getNdbError());
if (g_indcount == 0)
{
NdbDictionary::Dictionary::List list;
CHK2(g_dic->listIndexes(list, g_tabname) == 0, g_dic->getNdbError());
const int count = list.count;
g_indnames = (const char**)malloc(sizeof(char*) * count);
CHK2(g_indnames != 0, "out of memory");
for (int i = 0; i < count; i++)
{
const NdbDictionary::Dictionary::List::Element& e = list.elements[i];
if (e.type == NdbDictionary::Object::OrderedIndex)
{
g_indnames[g_indcount] = strdup(e.name);
CHK2(g_indnames[g_indcount] != 0, "out of memory");
g_indcount++;
}
}
CHK1(ret == 0);
}
g_indlist = (const NdbDictionary::Index**)malloc(sizeof(NdbDictionary::Index*) * g_indcount);
CHK2(g_indlist != 0, "out of memory");
for (int i = 0; i < g_indcount; i++)
{
CHK2((g_indlist[i] = g_dic->getIndex(g_indnames[i], g_tabname)) != 0,
g_tabname << "." << g_indnames[i] << ": " << g_dic->getNdbError());
}
}
while (0);
return ret;
}
static int
dosys()
{
int ret = 0;
do
{
if (_sys_drop)
{
if (!_sys_skip_events)
{
g_info << "dropping sys events" << endl;
CHK2(g_is->drop_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
CHK2(g_is->check_sysevents(g_ndb_sys) == -1, "unexpected success");
CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysEvents,
"unexpected error: " << g_is->getNdbError());
}
if (!_sys_skip_tables)
{
g_info << "dropping all sys tables" << endl;
CHK2(g_is->drop_systables(g_ndb_sys) == 0, g_is->getNdbError());
CHK2(g_is->check_systables(g_ndb_sys) == -1, "unexpected success");
CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysTables,
"unexpected error: " << g_is->getNdbError());
}
g_info << "drop done" << endl;
}
if (_sys_create)
{
if (!_sys_skip_tables)
{
g_info << "creating all sys tables" << endl;
CHK2(g_is->create_systables(g_ndb_sys) == 0, g_is->getNdbError());
CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
}
if (!_sys_skip_events)
{
g_info << "creating sys events" << endl;
CHK2(g_is->create_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
CHK2(g_is->check_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
g_info << "create done" << endl;
}
}
if (_sys_create_if_not_exist)
{
if (!_sys_skip_tables)
{
if (g_is->check_systables(g_ndb_sys) == -1)
{
CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysTables,
g_is->getNdbError());
g_info << "creating all sys tables" << endl;
CHK2(g_is->create_systables(g_ndb_sys) == 0, g_is->getNdbError());
CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
g_info << "create done" << endl;
}
else
{
g_info << "using existing sys tables" << endl;
}
}
if (!_sys_skip_events)
{
if (g_is->check_sysevents(g_ndb_sys) == -1)
{
CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysEvents,
g_is->getNdbError());
g_info << "creating sys events" << endl;
CHK2(g_is->create_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
g_info << "create done" << endl;
}
else
{
g_info << "using existing sys events" << endl;
}
}
}
if (_sys_create_if_not_valid)
{
if (!_sys_skip_tables)
{
if (g_is->check_systables(g_ndb_sys) == -1)
{
if (g_is->getNdbError().code != NdbIndexStat::NoSysTables)
{
CHK2(g_is->getNdbError().code == NdbIndexStat::BadSysTables,
g_is->getNdbError());
g_info << "dropping invalid sys tables" << endl;
CHK2(g_is->drop_systables(g_ndb_sys) == 0, g_is->getNdbError());
CHK2(g_is->check_systables(g_ndb_sys) == -1, "unexpected success");
CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysTables,
"unexpected error: " << g_is->getNdbError());
g_info << "drop done" << endl;
}
g_info << "creating all sys tables" << endl;
CHK2(g_is->create_systables(g_ndb_sys) == 0, g_is->getNdbError());
CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
g_info << "create done" << endl;
}
else
{
g_info << "using existing sys tables" << endl;
}
}
if (!_sys_skip_events)
{
if (g_is->check_sysevents(g_ndb_sys) == -1)
{
if (g_is->getNdbError().code != NdbIndexStat::NoSysEvents)
{
CHK2(g_is->getNdbError().code == NdbIndexStat::BadSysEvents,
g_is->getNdbError());
g_info << "dropping invalid sys events" << endl;
CHK2(g_is->drop_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
CHK2(g_is->check_sysevents(g_ndb_sys) == -1, "unexpected success");
CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysEvents,
"unexpected error: " << g_is->getNdbError());
g_info << "drop done" << endl;
}
g_info << "creating sys events" << endl;
CHK2(g_is->create_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
CHK2(g_is->check_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
g_info << "create done" << endl;
}
else
{
g_info << "using existing sys events" << endl;
}
}
}
if (_sys_check)
{
if (!_sys_skip_tables)
{
CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
g_info << "sys tables ok" << endl;
}
if (!_sys_skip_events)
{
CHK2(g_is->check_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
g_info << "sys events ok" << endl;
}
}
}
while (0);
return ret;
}
static int
doall()
{
int ret = 0;
do
{
CHK2(doconnect() == 0, "connect to NDB");
int loop = 0;
while (++loop <= _loops)
{
g_info << "loop " << loop << " of " << _loops << endl;
if (!_sys_any)
{
if (loop == 1)
{
CHK1(checkobjs() == 0);
}
CHK2(dostats() == 0, "at loop " << loop);
}
else
{
CHK2(dosys() == 0, "at loop " << loop);
}
}
CHK1(ret == 0);
}
while (0);
dodisconnect();
return ret;
}
static struct my_option
my_long_options[] =
{
NDB_STD_OPTS("ndb_index_stat"),
// stats options
{ "database", 'd',
"Name of database table is in",
(uchar**) &_dbname, (uchar**) &_dbname, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{ "delete", NDB_OPT_NOSHORT,
"Delete index stats of given table"
" and stop any configured auto update",
(uchar **)&_delete, (uchar **)&_delete, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "update", NDB_OPT_NOSHORT,
"Update index stats of given table"
" and restart any configured auto update",
(uchar **)&_update, (uchar **)&_update, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "dump", NDB_OPT_NOSHORT,
"Dump query cache",
(uchar **)&_dump, (uchar **)&_dump, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "query", NDB_OPT_NOSHORT,
"Perform random range queries on first key attr (must be int unsigned)",
(uchar **)&_query, (uchar **)&_query, 0,
GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
// sys options
{ "sys-drop", NDB_OPT_NOSHORT,
"Drop any stats tables and events in NDB kernel (all stats is lost)",
(uchar **)&_sys_drop, (uchar **)&_sys_drop, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "sys-create", NDB_OPT_NOSHORT,
"Create stats tables and events in NDB kernel (must not exist)",
(uchar **)&_sys_create, (uchar **)&_sys_create, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "sys-create-if-not-exist", NDB_OPT_NOSHORT,
"Like --sys-create but do nothing if correct objects exist",
(uchar **)&_sys_create_if_not_exist, (uchar **)&_sys_create_if_not_exist, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "sys-create-if-not-valid", NDB_OPT_NOSHORT,
"Like --sys-create-if-not-exist but first drop any invalid objects",
(uchar **)&_sys_create_if_not_valid, (uchar **)&_sys_create_if_not_valid, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "sys-check", NDB_OPT_NOSHORT,
"Check that correct stats tables and events exist in NDB kernel",
(uchar **)&_sys_check, (uchar **)&_sys_check, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "sys-skip-tables", NDB_OPT_NOSHORT,
"Do not apply sys options to tables",
(uchar **)&_sys_skip_tables, (uchar **)&_sys_skip_tables, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "sys-skip-events", NDB_OPT_NOSHORT,
"Do not apply sys options to events",
(uchar **)&_sys_skip_events, (uchar **)&_sys_skip_events, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
// other
{ "verbose", 'v',
"Verbose messages",
(uchar **)&_verbose, (uchar **)&_verbose, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
{ "loops", NDB_OPT_NOSHORT,
"Repeat same commands a number of times (for testing)",
(uchar **)&_loops, (uchar **)&_loops, 0,
GET_INT, REQUIRED_ARG, 1, 0, 0, 0, 0, 0 },
{ 0, 0,
0,
0, 0, 0,
GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
};
const char*
load_default_groups[]= { "mysql_cluster", 0 };
static void
short_usage_sub(void)
{
ndb_short_usage_sub("[table [index...]]");
}
static void
usage_extra()
{
printf("%s: ordered index stats tool and test\n", my_progname);
}
static int
checkopts(int argc, char** argv)
{
int ret = 0;
do
{
_stats_any =
(_dbname != 0) +
(_delete != 0) +
(_update != 0) +
(_dump != 0) +
(_query != 0);
_sys_any =
(_sys_create != 0) +
(_sys_create_if_not_exist != 0) +
(_sys_create_if_not_valid != 0) +
(_sys_drop != 0) +
( _sys_check != 0) +
(_sys_skip_tables != 0) +
(_sys_skip_events != 0);
if (!_sys_any)
{
if (_dbname == 0)
_dbname = "TEST_DB";
CHK2(argc >= 1, "stats options require table");
g_tabname = strdup(argv[0]);
CHK2(g_tabname != 0, "out of memory");
g_indcount = argc - 1;
if (g_indcount != 0)
{
g_indnames = (const char**)malloc(sizeof(char*) * g_indcount);
CHK2(g_indnames != 0, "out of memory");
for (int i = 0; i < g_indcount; i++)
{
g_indnames[i] = strdup(argv[1 + i]);
CHK2(g_indnames[i] != 0, "out of memory");
}
CHK1(ret == 0);
}
}
else
{
CHK2(_stats_any == 0, "cannot mix --sys options with stats options");
CHK2(argc == 0, "--sys options take no args");
}
}
while (0);
return ret;
}
int
main(int argc, char** argv)
{
NDB_INIT(argv[0]);
int ret;
Ndb_opts opts(argc, argv, my_long_options);
opts.set_usage_funcs(short_usage_sub, usage_extra);
ret = opts.handle_options();
if (ret != 0 || checkopts(argc, argv) != 0)
{
exit(NDBT_ProgramExit(NDBT_WRONGARGS));
}
setOutputLevel(_verbose ? 2 : 0);
unsigned seed = (unsigned)time(0);
g_info << "random seed " << seed << endl;
ndb_srand(seed);
ret = doall();
if (ret == -1)
{
exit(NDBT_ProgramExit(NDBT_FAILED));
}
exit(NDBT_ProgramExit(NDBT_OK));
}