1 /*
2    Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include <NDBT.hpp>
26 #include <NDBT_Test.hpp>
27 #include "../../src/ndbapi/NdbInfo.hpp"
28 
29 #include <NdbRestarter.hpp>
30 
31 
runTestNdbInfo(NDBT_Context * ctx,NDBT_Step * step)32 int runTestNdbInfo(NDBT_Context* ctx, NDBT_Step* step)
33 {
34   NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
35   if (!ndbinfo.init())
36   {
37     g_err << "ndbinfo.init failed" << endl;
38     return NDBT_FAILED;
39   }
40 
41   const NdbInfo::Table* table;
42   if (ndbinfo.openTable("ndbinfo/tables", &table) != 0)
43   {
44     g_err << "Failed to openTable(tables)" << endl;
45     return NDBT_FAILED;
46   }
47 
48   for (int l = 0; l < ctx->getNumLoops(); l++)
49   {
50 
51     NdbInfoScanOperation* scanOp = NULL;
52     if (ndbinfo.createScanOperation(table, &scanOp))
53     {
54       g_err << "No NdbInfoScanOperation" << endl;
55       return NDBT_FAILED;
56     }
57 
58     if (scanOp->readTuples() != 0)
59     {
60       g_err << "scanOp->readTuples failed" << endl;
61       return NDBT_FAILED;
62     }
63 
64     const NdbInfoRecAttr* tableName = scanOp->getValue("table_name");
65     const NdbInfoRecAttr* comment = scanOp->getValue("comment");
66 
67     if(scanOp->execute() != 0)
68     {
69       g_err << "scanOp->execute failed" << endl;
70       return NDBT_FAILED;
71     }
72 
73     while(scanOp->nextResult() == 1)
74     {
75       g_info << "NAME: " << tableName->c_str() << endl;
76       g_info << "COMMENT: " << comment->c_str() << endl;
77     }
78     ndbinfo.releaseScanOperation(scanOp);
79   }
80 
81   ndbinfo.closeTable(table);
82   return NDBT_OK;
83 }
84 
85 static bool
scan_table(NdbInfo & ndbinfo,const NdbInfo::Table * table,int & rows)86 scan_table(NdbInfo& ndbinfo, const NdbInfo::Table* table, int &rows)
87 {
88   NdbInfoScanOperation* scanOp = NULL;
89   if (ndbinfo.createScanOperation(table, &scanOp))
90   {
91     g_err << "No NdbInfoScanOperation" << endl;
92     return false;
93   }
94 
95   if (scanOp->readTuples() != 0)
96   {
97     g_err << "scanOp->readTuples failed" << endl;
98     ndbinfo.releaseScanOperation(scanOp);
99     return false;
100   }
101 
102   int columnId = 0;
103   while (scanOp->getValue(columnId))
104     columnId++;
105   // At least one column
106   require(columnId >= 1);
107   int ret;
108   if((ret = scanOp->execute()) != 0)
109   {
110     g_err << "scanOp->execute failed, ret: " << ret << endl;
111     ndbinfo.releaseScanOperation(scanOp);
112     return false;
113   }
114 
115   while((ret = scanOp->nextResult()) == 1)
116     rows++;
117 
118   ndbinfo.releaseScanOperation(scanOp);
119 
120   if (ret != 0)
121   {
122     g_err << "scanOp->nextResult failed, ret: " << ret << endl;
123     return false;
124   }
125 
126   return true;
127 }
128 
129 
runScanAll(NDBT_Context * ctx,NDBT_Step * step)130 int runScanAll(NDBT_Context* ctx, NDBT_Step* step)
131 {
132   NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
133   if (!ndbinfo.init())
134   {
135     g_err << "ndbinfo.init failed" << endl;
136     return NDBT_FAILED;
137   }
138 
139   Uint32 tableId = 0;
140   while(true) {
141     const NdbInfo::Table* table;
142 
143     int err = ndbinfo.openTable(tableId, &table);
144     if (err == NdbInfo::ERR_NoSuchTable)
145     {
146       // No more tables -> return
147       return NDBT_OK;
148     }
149     else if (err != 0)
150     {
151       // Unexpected return code
152       g_err << "Failed to openTable(" << tableId << "), err: " << err << endl;
153       return NDBT_FAILED;
154     }
155     ndbout << "table("<<tableId<<"): " << table->getName() << endl;
156 
157     int last_rows = 0;
158     bool rows_may_increase = (strstr(table->getName(), "cpustat_") != nullptr);
159     for (int l = 0; l < ctx->getNumLoops(); l++)
160     {
161       if (ctx->isTestStopped())
162         return NDBT_OK;
163 
164       int rows = 0;
165       if (!scan_table(ndbinfo, table, rows))
166       {
167         ctx->stopTest();
168         return NDBT_FAILED;
169       }
170       // Check if the number of rows is as expected:
171       // Expected scenario 1: Same number of rows as last round (or)
172       // Expected scenario 2: Same or increased number of rows for tables which
173       //                      might be still getting filled.
174       if (l > 0 &&
175           (rows != last_rows &&
176            !(rows_may_increase && rows > last_rows)))
177       {
178         g_err << "Got different number of rows this round, table: "
179           << table->getName() << ", expected: "
180           << ((rows_may_increase)?"equal to or more than ":"")
181           << last_rows << ", got: " << rows << endl;
182         ndbinfo.closeTable(table);
183         ctx->stopTest();
184         return NDBT_FAILED;
185       }
186       last_rows = rows;
187     }
188     ndbinfo.closeTable(table);
189     tableId++;
190   }
191 
192   // Should never come here
193   require(false);
194   return NDBT_FAILED;
195 }
196 
197 
runScanStop(NDBT_Context * ctx,NDBT_Step * step)198 int runScanStop(NDBT_Context* ctx, NDBT_Step* step)
199 {
200   NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
201   if (!ndbinfo.init())
202   {
203     g_err << "ndbinfo.init failed" << endl;
204     return NDBT_FAILED;
205   }
206 
207   Uint32 tableId = 0;
208   while(true) {
209     const NdbInfo::Table* table;
210 
211     int err = ndbinfo.openTable(tableId, &table);
212     if (err == NdbInfo::ERR_NoSuchTable)
213     {
214       // No more tables -> return
215       return NDBT_OK;
216     }
217     else if (err != 0)
218     {
219       // Unexpected return code
220       g_err << "Failed to openTable(" << tableId << "), err: " << err << endl;
221       return NDBT_FAILED;
222     }
223     ndbout << "table: " << table->getName() << endl;
224 
225     for (int l = 0; l < ctx->getNumLoops()*10; l++)
226     {
227       NdbInfoScanOperation* scanOp = NULL;
228       if (ndbinfo.createScanOperation(table, &scanOp))
229       {
230         g_err << "No NdbInfoScanOperation" << endl;
231         return NDBT_FAILED;
232       }
233 
234       if (scanOp->readTuples() != 0)
235       {
236         g_err << "scanOp->readTuples failed" << endl;
237         return NDBT_FAILED;
238       }
239 
240       int columnId = 0;
241       while (scanOp->getValue(columnId))
242         columnId++;
243       // At least one column
244       require(columnId >= 1);
245 
246       if(scanOp->execute() != 0)
247       {
248         g_err << "scanOp->execute failed" << endl;
249         return NDBT_FAILED;
250       }
251 
252       int stopRow = rand() % 100;
253       int row = 0;
254       while(scanOp->nextResult() == 1)
255       {
256         row++;
257         if (row == stopRow)
258         {
259           ndbout_c("Aborting scan at row %d", stopRow);
260           break;
261         }
262       }
263       ndbinfo.releaseScanOperation(scanOp);
264     }
265     ndbinfo.closeTable(table);
266     tableId++;
267   }
268 
269   // Should never come here
270   require(false);
271   return NDBT_FAILED;
272 }
273 
274 
runRatelimit(NDBT_Context * ctx,NDBT_Step * step)275 int runRatelimit(NDBT_Context* ctx, NDBT_Step* step)
276 {
277   NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
278   if (!ndbinfo.init())
279   {
280     g_err << "ndbinfo.init failed" << endl;
281     return NDBT_FAILED;
282   }
283 
284   Uint32 tableId = 0;
285   while(true) {
286 
287     const NdbInfo::Table* table;
288 
289     int err = ndbinfo.openTable(tableId, &table);
290     if (err == NdbInfo::ERR_NoSuchTable)
291     {
292       // No more tables -> return
293       return NDBT_OK;
294     }
295     else if (err != 0)
296     {
297       // Unexpected return code
298       g_err << "Failed to openTable(" << tableId << "), err: " << err << endl;
299       return NDBT_FAILED;
300     }
301     ndbout << "table: " << table->getName() << endl;
302 
303 
304     struct { Uint32 rows; Uint32 bytes; } limits[] = {
305       { 0, 0 },
306       { 1, 0 }, { 2, 0 }, { 10, 0 }, { 37, 0 }, { 1000, 0 },
307       { 0, 1 }, { 0, 2 }, { 0, 10 }, { 0, 37 }, { 0, 1000 },
308       { 1, 1 }, { 2, 2 }, { 10, 10 }, { 37, 37 }, { 1000, 1000 }
309     };
310 
311     int lastRows = 0;
312     bool rows_may_increase = (strstr(table->getName(), "cpustat_") != nullptr);
313     for (int l = 0; l < (int)(sizeof(limits)/sizeof(limits[0])); l++)
314     {
315 
316       Uint32 maxRows = limits[l].rows;
317       Uint32 maxBytes = limits[l].bytes;
318 
319       NdbInfoScanOperation* scanOp = NULL;
320       if (ndbinfo.createScanOperation(table, &scanOp, maxRows, maxBytes))
321       {
322         g_err << "No NdbInfoScanOperation" << endl;
323         return NDBT_FAILED;
324       }
325 
326       if (scanOp->readTuples() != 0)
327       {
328         g_err << "scanOp->readTuples failed" << endl;
329         return NDBT_FAILED;
330       }
331 
332       int columnId = 0;
333       while (scanOp->getValue(columnId))
334         columnId++;
335       // At least one column
336       require(columnId >= 1);
337 
338       if(scanOp->execute() != 0)
339       {
340         g_err << "scanOp->execute failed" << endl;
341         return NDBT_FAILED;
342       }
343 
344       int row = 0;
345       while(scanOp->nextResult() == 1)
346         row++;
347       ndbinfo.releaseScanOperation(scanOp);
348 
349       ndbout_c("[%u,%u] rows: %d", maxRows, maxBytes, row);
350       // Check if the number of rows is as expected:
351       // Expected scenario 1: Same number of rows as last round (or)
352       // Expected scenario 2: Same or increased number of rows for tables which
353       //                      might be still getting filled.
354       if (lastRows != 0 &&
355           (row != lastRows &&
356            !(rows_may_increase && row > lastRows)))
357       {
358         g_err << "Got different number of rows this round, table: "
359           << table->getName() << ", expected: "
360           << ((rows_may_increase)?"equal to or more than ":"")
361           << lastRows << ", got: " << row << endl;
362         ndbinfo.closeTable(table);
363         return NDBT_FAILED;
364       }
365       lastRows = row;
366     }
367     ndbinfo.closeTable(table);
368     tableId++;
369   }
370 
371   // Should never come here
372   require(false);
373   return NDBT_FAILED;
374 }
375 
runTestTable(NDBT_Context * ctx,NDBT_Step * step)376 int runTestTable(NDBT_Context* ctx, NDBT_Step* step)
377 {
378   NdbInfo ndbinfo(&ctx->m_cluster_connection, "ndbinfo/");
379   if (!ndbinfo.init())
380   {
381     g_err << "ndbinfo.init failed" << endl;
382     return NDBT_FAILED;
383   }
384 
385   const NdbInfo::Table* table;
386   if (ndbinfo.openTable("ndbinfo/test", &table) != 0)
387   {
388     g_err << "Failed to openTable(test)" << endl;
389     return NDBT_FAILED;
390   }
391 
392   for (int l = 0; l < ctx->getNumLoops(); l++)
393   {
394 
395     NdbInfoScanOperation* scanOp = NULL;
396     if (ndbinfo.createScanOperation(table, &scanOp))
397     {
398       ndbinfo.closeTable(table);
399       g_err << "No NdbInfoScanOperation" << endl;
400       return NDBT_FAILED;
401     }
402 
403     if (scanOp->readTuples() != 0)
404     {
405       ndbinfo.releaseScanOperation(scanOp);
406       ndbinfo.closeTable(table);
407       g_err << "scanOp->readTuples failed" << endl;
408       return NDBT_FAILED;
409     }
410 
411     const NdbInfoRecAttr* nodeId= scanOp->getValue("node_id");
412     const NdbInfoRecAttr* blockNumber= scanOp->getValue("block_number");
413     const NdbInfoRecAttr* blockInstance= scanOp->getValue("block_instance");
414     const NdbInfoRecAttr* counter= scanOp->getValue("counter");
415     const NdbInfoRecAttr* counter2= scanOp->getValue("counter2");
416 
417     if(scanOp->execute() != 0)
418     {
419       ndbinfo.releaseScanOperation(scanOp);
420       ndbinfo.closeTable(table);
421       g_err << "scanOp->execute failed" << endl;
422       return NDBT_FAILED;
423     }
424 
425     int ret;
426     int rows = 0;
427     while((ret = scanOp->nextResult()) == 1)
428     {
429        rows++;
430        (void)nodeId->u_32_value();
431        (void)blockNumber->u_32_value();
432        (void)blockInstance->u_32_value();
433        (void)counter->u_32_value();
434        (void)counter2->u_64_value();
435     }
436     ndbinfo.releaseScanOperation(scanOp);
437     if (ret != 0)
438     {
439       ndbinfo.closeTable(table);
440       g_err << "scan failed, ret: " << ret << endl;
441       return NDBT_FAILED;
442     }
443     ndbout << "rows: " << rows << endl;
444 
445   }
446 
447   ndbinfo.closeTable(table);
448   return NDBT_OK;
449 }
450 
451 
runTestTableUntilStopped(NDBT_Context * ctx,NDBT_Step * step)452 int runTestTableUntilStopped(NDBT_Context* ctx, NDBT_Step* step){
453   int i = 0;
454   while (ctx->isTestStopped() == false) {
455     g_info << i << ": ";
456     (void)runTestTable(ctx,  step);
457     i++;
458   }
459   return NDBT_OK;
460 }
461 
462 
runRestarter(NDBT_Context * ctx,NDBT_Step * step)463 int runRestarter(NDBT_Context* ctx, NDBT_Step* step){
464   int result = NDBT_OK;
465   int loops = ctx->getNumLoops();
466   int sync_threads = ctx->getProperty("SyncThreads", (unsigned)0);
467   int sleep0 = ctx->getProperty("Sleep0", (unsigned)0);
468   int sleep1 = ctx->getProperty("Sleep1", (unsigned)0);
469   int randnode = ctx->getProperty("RandNode", (unsigned)0);
470   NdbRestarter restarter;
471   int i = 0;
472   int lastId = 0;
473 
474   if (restarter.getNumDbNodes() < 2){
475     ctx->stopTest();
476     return NDBT_OK;
477   }
478 
479   if(restarter.waitClusterStarted() != 0){
480     g_err << "Cluster failed to start" << endl;
481     return NDBT_FAILED;
482   }
483 
484   if (loops > restarter.getNumDbNodes())
485     loops = restarter.getNumDbNodes();
486 
487   while(i<loops && result != NDBT_FAILED && !ctx->isTestStopped()){
488 
489     int id = lastId % restarter.getNumDbNodes();
490     if (randnode == 1)
491     {
492       id = rand() % restarter.getNumDbNodes();
493     }
494     int nodeId = restarter.getDbNodeId(id);
495     ndbout << "Restart node " << nodeId << endl;
496     if(restarter.restartOneDbNode(nodeId, false, true, true) != 0){
497       g_err << "Failed to restartNextDbNode" << endl;
498       result = NDBT_FAILED;
499       break;
500     }
501 
502     if (restarter.waitNodesNoStart(&nodeId, 1))
503     {
504       g_err << "Failed to waitNodesNoStart" << endl;
505       result = NDBT_FAILED;
506       break;
507     }
508 
509     if (sleep1)
510       NdbSleep_MilliSleep(sleep1);
511 
512     if (restarter.startNodes(&nodeId, 1))
513     {
514       g_err << "Failed to start node" << endl;
515       result = NDBT_FAILED;
516       break;
517     }
518 
519     if(restarter.waitClusterStarted() != 0){
520       g_err << "Cluster failed to start" << endl;
521       result = NDBT_FAILED;
522       break;
523     }
524 
525     if (sleep0)
526       NdbSleep_MilliSleep(sleep0);
527 
528     ctx->sync_up_and_wait("PauseThreads", sync_threads);
529 
530     lastId++;
531     i++;
532   }
533 
534   ctx->stopTest();
535 
536   return result;
537 }
538 
539 
540 
541 NDBT_TESTSUITE(testNdbinfo);
542 TESTCASE("NodeRestart", "Scan NdbInfo tables while restarting nodes"){
543   STEP(runRestarter);
544   STEPS(runTestTableUntilStopped, 1);
545 }
546 TESTCASE("Ndbinfo",
547          "Test ndbapi interface to NDB$INFO"){
548   INITIALIZER(runTestNdbInfo);
549 }
550 TESTCASE("Ndbinfo10",
551          "Test ndbapi interface to NDB$INFO"){
552   STEPS(runTestNdbInfo, 10);
553 }
554 TESTCASE("ScanAll",
555          "Scan all colums of all table known to NdbInfo"
556          "check that number of rows returned are as expected."
557          "Either they should be same across multiple iterations or"
558          "should increasing if in case the table is still getting filled."){
559   STEPS(runScanAll, 1);
560 }
561 TESTCASE("ScanAll10",
562          "Scan all columns of all table known to NdbInfo from "
563          "10 parallel threads, check that number of rows returned are as"
564          "expected. Either they should be same across multiple iterations or"
565          "should increasing if in case the table is still getting filled."){
566   STEPS(runScanAll, 10);
567 }
568 TESTCASE("ScanStop",
569          "Randomly stop the scan"){
570   STEPS(runScanStop, 1);
571 }
572 TESTCASE("Ratelimit",
573          "Scan wit different combinations of ratelimit"){
574   STEPS(runRatelimit, 1);
575 }
576 TESTCASE("TestTable",
577          "Scan the test table and make sure it returns correct number "
578           "of rows which will depend on how many TUP blocks are configured"){
579   STEP(runTestTable);
580 }
NDBT_TESTSUITE_END(testNdbinfo)581 NDBT_TESTSUITE_END(testNdbinfo)
582 
583 
584 int main(int argc, const char** argv){
585   ndb_init();
586   NDBT_TESTSUITE_INSTANCE(testNdbinfo);
587   testNdbinfo.setCreateTable(false);
588   testNdbinfo.setRunAllTables(true);
589   return testNdbinfo.execute(argc, argv);
590 }
591