582 lines
14 KiB
C++

/*
Copyright (c) 2009, 2015, 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 as published by
the Free Software Foundation; version 2 of the License.
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 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 <NDBT.hpp>
#include <NDBT_Test.hpp>
#include "../../src/ndbapi/NdbInfo.hpp"
#include <NdbRestarter.hpp>
int runTestNdbInfo(NDBT_Context* ctx, NDBT_Step* step)
{
NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
if (!ndbinfo.init())
{
g_err << "ndbinfo.init failed" << endl;
return NDBT_FAILED;
}
const NdbInfo::Table* table;
if (ndbinfo.openTable("ndbinfo/tables", &table) != 0)
{
g_err << "Failed to openTable(tables)" << endl;
return NDBT_FAILED;
}
for (int l = 0; l < ctx->getNumLoops(); l++)
{
NdbInfoScanOperation* scanOp = NULL;
if (ndbinfo.createScanOperation(table, &scanOp))
{
g_err << "No NdbInfoScanOperation" << endl;
return NDBT_FAILED;
}
if (scanOp->readTuples() != 0)
{
g_err << "scanOp->readTuples failed" << endl;
return NDBT_FAILED;
}
const NdbInfoRecAttr* tableName = scanOp->getValue("table_name");
const NdbInfoRecAttr* comment = scanOp->getValue("comment");
if(scanOp->execute() != 0)
{
g_err << "scanOp->execute failed" << endl;
return NDBT_FAILED;
}
while(scanOp->nextResult() == 1)
{
g_info << "NAME: " << tableName->c_str() << endl;
g_info << "COMMENT: " << comment->c_str() << endl;
}
ndbinfo.releaseScanOperation(scanOp);
}
ndbinfo.closeTable(table);
return NDBT_OK;
}
static bool
scan_table(NdbInfo& ndbinfo, const NdbInfo::Table* table, int &rows)
{
NdbInfoScanOperation* scanOp = NULL;
if (ndbinfo.createScanOperation(table, &scanOp))
{
g_err << "No NdbInfoScanOperation" << endl;
return false;
}
if (scanOp->readTuples() != 0)
{
g_err << "scanOp->readTuples failed" << endl;
ndbinfo.releaseScanOperation(scanOp);
return false;
}
int columnId = 0;
while (scanOp->getValue(columnId))
columnId++;
// At least one column
require(columnId >= 1);
int ret;
if((ret = scanOp->execute()) != 0)
{
g_err << "scanOp->execute failed, ret: " << ret << endl;
ndbinfo.releaseScanOperation(scanOp);
return false;
}
while((ret = scanOp->nextResult()) == 1)
rows++;
ndbinfo.releaseScanOperation(scanOp);
if (ret != 0)
{
g_err << "scanOp->nextResult failed, ret: " << ret << endl;
return false;
}
return true;
}
int runScanAll(NDBT_Context* ctx, NDBT_Step* step)
{
NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
if (!ndbinfo.init())
{
g_err << "ndbinfo.init failed" << endl;
return NDBT_FAILED;
}
Uint32 tableId = 0;
while(true) {
const NdbInfo::Table* table;
int err = ndbinfo.openTable(tableId, &table);
if (err == NdbInfo::ERR_NoSuchTable)
{
// No more tables -> return
return NDBT_OK;
}
else if (err != 0)
{
// Unexpected return code
g_err << "Failed to openTable(" << tableId << "), err: " << err << endl;
return NDBT_FAILED;
}
ndbout << "table("<<tableId<<"): " << table->getName() << endl;
int last_rows;
for (int l = 0; l < ctx->getNumLoops(); l++)
{
if (ctx->isTestStopped())
return NDBT_OK;
int rows = 0;
if (!scan_table(ndbinfo, table, rows))
{
ctx->stopTest();
return NDBT_FAILED;
}
// Check that the number of rows is same as last round on same table
if (l > 0 &&
last_rows != rows)
{
g_err << "Got different number of rows this round, expected: "
<< last_rows << ", got: " << rows << endl;
ndbinfo.closeTable(table);
ctx->stopTest();
return NDBT_FAILED;
}
last_rows = rows;
}
ndbinfo.closeTable(table);
tableId++;
}
// Should never come here
require(false);
return NDBT_FAILED;
}
int runScanAllUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
int i = 0;
while (ctx->isTestStopped() == false) {
g_info << i << ": ";
if (runScanAll(ctx, step) != NDBT_OK){
return NDBT_FAILED;
}
i++;
}
return NDBT_OK;
}
int runScanStop(NDBT_Context* ctx, NDBT_Step* step)
{
NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
if (!ndbinfo.init())
{
g_err << "ndbinfo.init failed" << endl;
return NDBT_FAILED;
}
Uint32 tableId = 0;
while(true) {
const NdbInfo::Table* table;
int err = ndbinfo.openTable(tableId, &table);
if (err == NdbInfo::ERR_NoSuchTable)
{
// No more tables -> return
return NDBT_OK;
}
else if (err != 0)
{
// Unexpected return code
g_err << "Failed to openTable(" << tableId << "), err: " << err << endl;
return NDBT_FAILED;
}
ndbout << "table: " << table->getName() << endl;
for (int l = 0; l < ctx->getNumLoops()*10; l++)
{
NdbInfoScanOperation* scanOp = NULL;
if (ndbinfo.createScanOperation(table, &scanOp))
{
g_err << "No NdbInfoScanOperation" << endl;
return NDBT_FAILED;
}
if (scanOp->readTuples() != 0)
{
g_err << "scanOp->readTuples failed" << endl;
return NDBT_FAILED;
}
int columnId = 0;
while (scanOp->getValue(columnId))
columnId++;
// At least one column
require(columnId >= 1);
if(scanOp->execute() != 0)
{
g_err << "scanOp->execute failed" << endl;
return NDBT_FAILED;
}
int stopRow = rand() % 100;
int row = 0;
while(scanOp->nextResult() == 1)
{
row++;
if (row == stopRow)
{
ndbout_c("Aborting scan at row %d", stopRow);
break;
}
}
ndbinfo.releaseScanOperation(scanOp);
}
ndbinfo.closeTable(table);
tableId++;
}
// Should never come here
require(false);
return NDBT_FAILED;
}
int runRatelimit(NDBT_Context* ctx, NDBT_Step* step)
{
NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
if (!ndbinfo.init())
{
g_err << "ndbinfo.init failed" << endl;
return NDBT_FAILED;
}
Uint32 tableId = 0;
while(true) {
const NdbInfo::Table* table;
int err = ndbinfo.openTable(tableId, &table);
if (err == NdbInfo::ERR_NoSuchTable)
{
// No more tables -> return
return NDBT_OK;
}
else if (err != 0)
{
// Unexpected return code
g_err << "Failed to openTable(" << tableId << "), err: " << err << endl;
return NDBT_FAILED;
}
ndbout << "table: " << table->getName() << endl;
struct { Uint32 rows; Uint32 bytes; } limits[] = {
{ 0, 0 },
{ 1, 0 }, { 2, 0 }, { 10, 0 }, { 37, 0 }, { 1000, 0 },
{ 0, 1 }, { 0, 2 }, { 0, 10 }, { 0, 37 }, { 0, 1000 },
{ 1, 1 }, { 2, 2 }, { 10, 10 }, { 37, 37 }, { 1000, 1000 }
};
int lastRows = 0;
for (int l = 0; l < (int)(sizeof(limits)/sizeof(limits[0])); l++)
{
Uint32 maxRows = limits[l].rows;
Uint32 maxBytes = limits[l].bytes;
NdbInfoScanOperation* scanOp = NULL;
if (ndbinfo.createScanOperation(table, &scanOp, maxRows, maxBytes))
{
g_err << "No NdbInfoScanOperation" << endl;
return NDBT_FAILED;
}
if (scanOp->readTuples() != 0)
{
g_err << "scanOp->readTuples failed" << endl;
return NDBT_FAILED;
}
int columnId = 0;
while (scanOp->getValue(columnId))
columnId++;
// At least one column
require(columnId >= 1);
if(scanOp->execute() != 0)
{
g_err << "scanOp->execute failed" << endl;
return NDBT_FAILED;
}
int row = 0;
while(scanOp->nextResult() == 1)
row++;
ndbinfo.releaseScanOperation(scanOp);
ndbout_c("[%u,%u] rows: %d", maxRows, maxBytes, row);
if (lastRows != 0)
{
// Check that the number of rows is same as last round on same table
if (lastRows != row)
{
g_err << "Got different number of rows this round, expected: "
<< lastRows << ", got: " << row << endl;
ndbinfo.closeTable(table);
return NDBT_FAILED;
}
}
lastRows = row;
}
ndbinfo.closeTable(table);
tableId++;
}
// Should never come here
require(false);
return NDBT_FAILED;
}
int runTestTable(NDBT_Context* ctx, NDBT_Step* step)
{
NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
if (!ndbinfo.init())
{
g_err << "ndbinfo.init failed" << endl;
return NDBT_FAILED;
}
const NdbInfo::Table* table;
if (ndbinfo.openTable("ndbinfo/test", &table) != 0)
{
g_err << "Failed to openTable(test)" << endl;
return NDBT_FAILED;
}
for (int l = 0; l < ctx->getNumLoops(); l++)
{
NdbInfoScanOperation* scanOp = NULL;
if (ndbinfo.createScanOperation(table, &scanOp))
{
ndbinfo.closeTable(table);
g_err << "No NdbInfoScanOperation" << endl;
return NDBT_FAILED;
}
if (scanOp->readTuples() != 0)
{
ndbinfo.releaseScanOperation(scanOp);
ndbinfo.closeTable(table);
g_err << "scanOp->readTuples failed" << endl;
return NDBT_FAILED;
}
const NdbInfoRecAttr* nodeId= scanOp->getValue("node_id");
const NdbInfoRecAttr* blockNumber= scanOp->getValue("block_number");
const NdbInfoRecAttr* blockInstance= scanOp->getValue("block_instance");
const NdbInfoRecAttr* counter= scanOp->getValue("counter");
const NdbInfoRecAttr* counter2= scanOp->getValue("counter2");
if(scanOp->execute() != 0)
{
ndbinfo.releaseScanOperation(scanOp);
ndbinfo.closeTable(table);
g_err << "scanOp->execute failed" << endl;
return NDBT_FAILED;
}
int ret;
int rows = 0;
while((ret = scanOp->nextResult()) == 1)
{
rows++;
(void)nodeId->u_32_value();
(void)blockNumber->u_32_value();
(void)blockInstance->u_32_value();
(void)counter->u_32_value();
(void)counter2->u_64_value();
}
ndbinfo.releaseScanOperation(scanOp);
if (ret != 0)
{
ndbinfo.closeTable(table);
g_err << "scan failed, ret: " << ret << endl;
return NDBT_FAILED;
}
ndbout << "rows: " << rows << endl;
}
ndbinfo.closeTable(table);
return NDBT_OK;
}
int runTestTableUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
int i = 0;
while (ctx->isTestStopped() == false) {
g_info << i << ": ";
(void)runTestTable(ctx, step);
i++;
}
return NDBT_OK;
}
int runRestarter(NDBT_Context* ctx, NDBT_Step* step){
int result = NDBT_OK;
int loops = ctx->getNumLoops();
int sync_threads = ctx->getProperty("SyncThreads", (unsigned)0);
int sleep0 = ctx->getProperty("Sleep0", (unsigned)0);
int sleep1 = ctx->getProperty("Sleep1", (unsigned)0);
int randnode = ctx->getProperty("RandNode", (unsigned)0);
NdbRestarter restarter;
int i = 0;
int lastId = 0;
if (restarter.getNumDbNodes() < 2){
ctx->stopTest();
return NDBT_OK;
}
if(restarter.waitClusterStarted() != 0){
g_err << "Cluster failed to start" << endl;
return NDBT_FAILED;
}
if (loops > restarter.getNumDbNodes())
loops = restarter.getNumDbNodes();
while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
int id = lastId % restarter.getNumDbNodes();
if (randnode == 1)
{
id = rand() % restarter.getNumDbNodes();
}
int nodeId = restarter.getDbNodeId(id);
ndbout << "Restart node " << nodeId << endl;
if(restarter.restartOneDbNode(nodeId, false, true, true) != 0){
g_err << "Failed to restartNextDbNode" << endl;
result = NDBT_FAILED;
break;
}
if (restarter.waitNodesNoStart(&nodeId, 1))
{
g_err << "Failed to waitNodesNoStart" << endl;
result = NDBT_FAILED;
break;
}
if (sleep1)
NdbSleep_MilliSleep(sleep1);
if (restarter.startNodes(&nodeId, 1))
{
g_err << "Failed to start node" << endl;
result = NDBT_FAILED;
break;
}
if(restarter.waitClusterStarted() != 0){
g_err << "Cluster failed to start" << endl;
result = NDBT_FAILED;
break;
}
if (sleep0)
NdbSleep_MilliSleep(sleep0);
ctx->sync_up_and_wait("PauseThreads", sync_threads);
lastId++;
i++;
}
ctx->stopTest();
return result;
}
NDBT_TESTSUITE(testNdbinfo);
TESTCASE("NodeRestart", "Scan NdbInfo tables while restarting nodes"){
STEP(runRestarter);
STEPS(runTestTableUntilStopped, 1);
}
TESTCASE("Ndbinfo",
"Test ndbapi interface to NDB$INFO"){
INITIALIZER(runTestNdbInfo);
}
TESTCASE("Ndbinfo10",
"Test ndbapi interface to NDB$INFO"){
STEPS(runTestNdbInfo, 10);
}
TESTCASE("ScanAll",
"Scan all colums of all table known to NdbInfo"
"check that number of rows returned are constant"){
STEPS(runScanAll, 1);
}
TESTCASE("ScanAll10",
"Scan all columns of all table known to NdbInfo from "
"10 parallel threads, check that number of rows returned "
"are constant"){
STEPS(runScanAll, 10);
}
TESTCASE("ScanStop",
"Randomly stop the scan"){
STEPS(runScanStop, 1);
}
TESTCASE("Ratelimit",
"Scan wit different combinations of ratelimit"){
STEPS(runRatelimit, 1);
}
TESTCASE("TestTable",
"Scan the test table and make sure it returns correct number "
"of rows which will depend on how many TUP blocks are configured"){
STEP(runTestTable);
}
NDBT_TESTSUITE_END(testNdbinfo);
int main(int argc, const char** argv){
ndb_init();
NDBT_TESTSUITE_INSTANCE(testNdbinfo);
testNdbinfo.setCreateTable(false);
testNdbinfo.setRunAllTables(true);
return testNdbinfo.execute(argc, argv);
}