1 /*
2 Copyright (c) 2009, 2011, 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 <assert.h>
26 #include <mysql.h>
27 #include <mysqld_error.h>
28
29 //#include <iostream>
30 //#include <stdio.h>
31
32 #include <ndb_global.h>
33 #include <ndb_opts.h>
34 #include <NDBT.hpp>
35 #include <NdbApi.hpp>
36 #include "../../src/ndbapi/NdbQueryBuilder.hpp"
37 #include "../../src/ndbapi/NdbQueryOperation.hpp"
38 #include <pthread.h>
39 #include <NdbTick.h>
40
41
42
43 #ifdef NDEBUG
44 // Some asserts have side effects, and there is no other error handling anyway.
45 #define ASSERT_ALWAYS(cond) if(unlikely(!(cond))){abort();}
46 #else
47 #define ASSERT_ALWAYS assert
48 #endif
49
50 #if 0
51 /**
52 * Helper debugging macros
53 */
54 #define PRINT_ERROR(code,msg) \
55 std::cout << "Error in " << __FILE__ << ", line: " << __LINE__ \
56 << ", code: " << code \
57 << ", msg: " << msg << "." << std::endl
58
59 #define APIERROR(error) { \
60 PRINT_ERROR((error).code,(error).message); \
61 exit(-1); }
62 #endif
63
64
65 //const char* databaseName = "TEST_DB";
66 //const char* tableName = "T";
67 const char* databaseName = "PTDB";
68 const char* tableName = "TT";
69
70 class TestParameters{
71 public:
72 int m_iterations;
73 /** Number of child lookup operations.*/
74 int m_depth;
75 int m_scanLength; // m_scanLength==0 means root should be lookup.
76 /** Specifies how many times a query definition should be reused before.
77 * It is recreated. Setting this to 0 means that the definition is never
78 * recreated.*/
79 int m_queryDefReuse;
80 bool m_useLinkedOperations;
81 /** If true, run an equivalent SQL query.*/
82 bool m_useSQL;
83
TestParameters()84 explicit TestParameters(){bzero(this, sizeof *this);}
85 };
86
87 /** Entry point for new posix threads.*/
88 static void *callback(void* thread);
89
90 class TestThread{
91 friend void *callback(void* thread);
92 public:
93 explicit TestThread(Ndb_cluster_connection& con, const char* host, int port);
94 ~TestThread();
95 /** Initiate a new test.*/
96 void start(const TestParameters& params);
97 /** Wait fo current test to complete.*/
98 void wait();
99 private:
100 struct Row{
101 Uint32 a;
102 Uint32 b;
103 };
104
105 struct KeyRow{
106 Uint32 a;
107 };
108
109 const TestParameters* m_params;
110 Ndb m_ndb;
111 enum {State_Active, State_Stopping, State_Stopped} m_state;
112 pthread_t m_posixThread;
113 pthread_mutex_t m_mutex;
114 pthread_cond_t m_condition;
115 const NdbDictionary::Table* m_tab;
116 const NdbDictionary::Index* m_index;
117 const NdbRecord* m_resultRec;
118 const NdbRecord* m_keyRec;
119 const NdbRecord* m_indexRec;
120 MYSQL m_mysql;
121
122 /** Entry point for POSIX thread.*/
123 void run();
124 void doLinkedAPITest();
125 void doNonLinkedAPITest();
126 void doSQLTest();
127 };
128
callback(void * thread)129 static void *callback(void* thread){
130 reinterpret_cast<TestThread*>(thread)->run();
131 return NULL;
132 }
133
printMySQLError(MYSQL & mysql,const char * before=NULL)134 static void printMySQLError(MYSQL& mysql, const char* before=NULL){
135 if(before!=NULL){
136 ndbout << before;
137 }
138 ndbout << mysql_error(&mysql) << endl;
139 exit(-1);
140 }
141
mySQLExec(MYSQL & mysql,const char * stmt)142 static void mySQLExec(MYSQL& mysql, const char* stmt){
143 //ndbout << stmt << endl;
144 if(mysql_query(&mysql, stmt) != 0){
145 ndbout << "Error executing '" << stmt << "' : ";
146 printMySQLError(mysql);
147 }
148 mysql_free_result(mysql_use_result(&mysql));
149 }
150
151 // TestThread methods.
TestThread(Ndb_cluster_connection & con,const char * host,int port)152 TestThread::TestThread(Ndb_cluster_connection& con,
153 const char* host,
154 int port):
155 m_params(NULL),
156 m_ndb(&con, databaseName),
157 m_state(State_Active){
158 ASSERT_ALWAYS(m_ndb.init()==0);
159 ASSERT_ALWAYS(pthread_mutex_init(&m_mutex, NULL)==0);
160 ASSERT_ALWAYS(pthread_cond_init(&m_condition, NULL)==0);
161 ASSERT_ALWAYS(pthread_create(&m_posixThread, NULL, callback, this)
162 ==0);
163 NdbDictionary::Dictionary* const dict = m_ndb.getDictionary();
164 m_tab = dict->getTable(tableName);
165
166 m_index = dict->getIndex("PRIMARY", tableName);
167 ASSERT_ALWAYS(m_index != NULL);
168
169 /* Create NdbRecord for row. */
170 m_resultRec = m_tab->getDefaultRecord();
171 ASSERT_ALWAYS(m_resultRec!=NULL);
172
173 /* Create NdbRecord for primary key. */
174 const NdbDictionary::Column *col1= m_tab->getColumn("a");
175 ASSERT_ALWAYS(col1 != NULL);
176 NdbDictionary::RecordSpecification spec = {
177 col1, 0, 0, 0
178 };
179
180 m_keyRec = dict->createRecord(m_tab, &spec, 1, sizeof spec);
181 ASSERT_ALWAYS(m_keyRec != NULL);
182
183 m_indexRec = m_index->getDefaultRecord();
184 ASSERT_ALWAYS(m_indexRec != NULL);
185
186 // Make SQL connection.
187 ASSERT_ALWAYS(mysql_init(&m_mysql));
188 if(!mysql_real_connect(&m_mysql, host, "root", "", "",
189 port, NULL, 0)){
190 printMySQLError(m_mysql, "mysql_real_connect() failed:");
191 ASSERT_ALWAYS(false);
192 }
193 char text[50];
194 sprintf(text, "use %s", databaseName);
195 mySQLExec(m_mysql, text);
196 }
197
~TestThread()198 TestThread::~TestThread(){
199 ASSERT_ALWAYS(pthread_mutex_lock(&m_mutex)==0);
200 // Tell thread to stop.
201 m_state = State_Stopping;
202 ASSERT_ALWAYS(pthread_cond_signal(&m_condition)==0);
203 // Wait for thread to stop.
204 while(m_state != State_Stopped){
205 ASSERT_ALWAYS(pthread_cond_wait(&m_condition, &m_mutex)==0);
206 }
207 ASSERT_ALWAYS(m_params == NULL);
208 ASSERT_ALWAYS(pthread_mutex_unlock(&m_mutex)==0);
209
210 ASSERT_ALWAYS(pthread_cond_destroy(&m_condition)==0);
211 ASSERT_ALWAYS(pthread_mutex_destroy(&m_mutex)==0);
212 }
213
start(const TestParameters & params)214 void TestThread::start(const TestParameters& params){
215 ASSERT_ALWAYS(pthread_mutex_lock(&m_mutex)==0);
216 ASSERT_ALWAYS(m_params == NULL);
217 m_params = ¶ms;
218 ASSERT_ALWAYS(pthread_cond_signal(&m_condition)==0);
219 ASSERT_ALWAYS(pthread_mutex_unlock(&m_mutex)==0);
220 }
221
run()222 void TestThread::run(){
223
224 ASSERT_ALWAYS(pthread_mutex_lock(&m_mutex)==0);
225 while(true){
226 while(m_params==NULL && m_state==State_Active){
227 // Wait for a new command from master thread.
228 ASSERT_ALWAYS(pthread_cond_wait(&m_condition, &m_mutex)==0);
229 }
230 if(m_state != State_Active){
231 // We have been told to stop.
232 ASSERT_ALWAYS(m_state == State_Stopping);
233 m_state = State_Stopped;
234 // Wake up master thread and release lock.
235 ASSERT_ALWAYS(pthread_cond_signal(&m_condition)==0);
236 ASSERT_ALWAYS(pthread_mutex_unlock(&m_mutex)==0);
237 // Exit thread.
238 return;
239 }
240
241 if(m_params->m_useSQL){
242 doSQLTest();
243 }else{
244 if(m_params->m_useLinkedOperations){
245 doLinkedAPITest();
246 }else{
247 doNonLinkedAPITest();
248 }
249 }
250
251 ASSERT_ALWAYS(m_params != NULL);
252 m_params = NULL;
253 ASSERT_ALWAYS(pthread_cond_signal(&m_condition)==0);
254 }
255 }
256
doLinkedAPITest()257 void TestThread::doLinkedAPITest(){
258 NdbQueryBuilder* const builder = NdbQueryBuilder::create();
259
260 const NdbQueryDef* queryDef = NULL;
261 const Row** resultPtrs = new const Row*[m_params->m_depth+1];
262
263 NdbTransaction* trans = NULL;
264
265 for(int iterNo = 0; iterNo<m_params->m_iterations; iterNo++){
266 //ndbout << "Starting next iteration " << endl;
267 // Build query definition if needed.
268 if(iterNo==0 || (m_params->m_queryDefReuse>0 &&
269 iterNo%m_params->m_queryDefReuse==0)){
270 if(queryDef != NULL){
271 queryDef->destroy();
272 }
273 const NdbQueryOperationDef* parentOpDef = NULL;
274 if(m_params->m_scanLength==0){
275 // Root is lookup
276 const NdbQueryOperand* rootKey[] = {
277 builder->constValue(0), //a
278 NULL
279 };
280 parentOpDef = builder->readTuple(m_tab, rootKey);
281 }else if(m_params->m_scanLength==1){ //Pruned scan
282 const NdbQueryOperand* const key[] = {
283 builder->constValue(m_params->m_scanLength),
284 NULL
285 };
286
287 const NdbQueryIndexBound eqBound(key);
288 parentOpDef = builder->scanIndex(m_index, m_tab, &eqBound);
289 }else{
290 // Root is index scan with single bound.
291 const NdbQueryOperand* const highKey[] = {
292 builder->constValue(m_params->m_scanLength),
293 NULL
294 };
295
296 const NdbQueryIndexBound bound(NULL, false, highKey, false);
297 parentOpDef = builder->scanIndex(m_index, m_tab, &bound);
298 }
299
300 // Add child lookup operations.
301 for(int i = 0; i<m_params->m_depth; i++){
302 const NdbQueryOperand* key[] = {
303 builder->linkedValue(parentOpDef, "b"),
304 NULL
305 };
306 parentOpDef = builder->readTuple(m_tab, key);
307 }
308 queryDef = builder->prepare();
309 }
310
311 if (!trans) {
312 trans = m_ndb.startTransaction();
313 }
314 // Execute query.
315 NdbQuery* const query = trans->createQuery(queryDef);
316 for(int i = 0; i<m_params->m_depth+1; i++){
317 query->getQueryOperation(i)
318 ->setResultRowRef(m_resultRec,
319 reinterpret_cast<const char*&>(resultPtrs[i]),
320 NULL);
321 }
322 int res = trans->execute(NoCommit);
323 // if (res != 0)
324 // APIERROR(trans->getNdbError());
325 ASSERT_ALWAYS(res == 0);
326 int cnt=0;
327 while(true){
328 const NdbQuery::NextResultOutcome outcome
329 = query->nextResult(true, false);
330 if(outcome == NdbQuery::NextResult_scanComplete){
331 break;
332 }
333 ASSERT_ALWAYS(outcome== NdbQuery::NextResult_gotRow);
334 cnt++;
335 // if (m_params->m_scanLength==0)
336 // break;
337 }
338 ASSERT_ALWAYS(cnt== MAX(1,m_params->m_scanLength));
339 // query->close();
340 if ((iterNo % 5) == 0) {
341 m_ndb.closeTransaction(trans);
342 trans = NULL;
343 }
344 }
345 if (trans) {
346 m_ndb.closeTransaction(trans);
347 trans = NULL;
348 }
349 builder->destroy();
350 }
351
doNonLinkedAPITest()352 void TestThread::doNonLinkedAPITest(){
353 Row row = {0, 0};
354 NdbTransaction* const trans = m_ndb.startTransaction();
355 for(int iterNo = 0; iterNo<m_params->m_iterations; iterNo++){
356 // NdbTransaction* const trans = m_ndb.startTransaction();
357 if(m_params->m_scanLength>0){
358 const KeyRow highKey = { m_params->m_scanLength };
359 NdbIndexScanOperation* scanOp = NULL;
360 if(m_params->m_scanLength==1){ // Pruned scan
361 const NdbIndexScanOperation::IndexBound bound = {
362 reinterpret_cast<const char*>(&highKey),
363 1, // Low key count.
364 true, // Low key inclusive
365 reinterpret_cast<const char*>(&highKey),
366 1, // High key count.
367 true, // High key inclusive.
368 0
369 };
370
371 scanOp =
372 trans->scanIndex(m_indexRec,
373 m_resultRec,
374 NdbOperation::LM_Dirty,
375 NULL, // Result mask
376 &bound);
377 }else{
378 // Scan with upper bound only.
379 const NdbIndexScanOperation::IndexBound bound = {
380 NULL, // Low key
381 0, // Low key count.
382 false, // Low key inclusive
383 reinterpret_cast<const char*>(&highKey),
384 1, // High key count.
385 false, // High key inclusive.
386 0
387 };
388
389 scanOp =
390 trans->scanIndex(m_indexRec,
391 m_resultRec,
392 NdbOperation::LM_Dirty,
393 NULL, // Result mask
394 &bound);
395 }
396 ASSERT_ALWAYS(scanOp != NULL);
397
398 ASSERT_ALWAYS(trans->execute(NoCommit) == 0);
399
400 // Iterate over scan result
401 int cnt = 0;
402 while(true){
403 const Row* scanRow = NULL;
404 const int retVal =
405 scanOp->nextResult(reinterpret_cast<const char**>(&scanRow),
406 true,
407 false);
408 if(retVal==1){
409 break;
410 }
411 ASSERT_ALWAYS(retVal== 0);
412 //ndbout << "ScanRow: " << scanRow->a << " " << scanRow->b << endl;
413 row = *scanRow;
414
415 // Do a chain of lookups for each scan row.
416 for(int i = 0; i < m_params->m_depth; i++){
417 const KeyRow key = {row.b};
418 const NdbOperation* const lookupOp =
419 trans->readTuple(m_keyRec,
420 reinterpret_cast<const char*>(&key),
421 m_resultRec,
422 reinterpret_cast<char*>(&row),
423 NdbOperation::LM_Dirty);
424 ASSERT_ALWAYS(lookupOp != NULL);
425 ASSERT_ALWAYS(trans->execute(NoCommit) == 0);
426 //ndbout << "LookupRow: " << row.a << " " << row.b << endl;
427 }
428 cnt++;
429 // if (m_params->m_scanLength==0)
430 // break;
431 }
432 ASSERT_ALWAYS(cnt== m_params->m_scanLength);
433 scanOp->close(false,true);
434 }else{
435 // Root is lookup.
436 for(int i = 0; i < m_params->m_depth+1; i++){
437 const KeyRow key = {row.b};
438 const NdbOperation* const lookupOp =
439 trans->readTuple(m_keyRec,
440 reinterpret_cast<const char*>(&key),
441 m_resultRec,
442 reinterpret_cast<char*>(&row),
443 NdbOperation::LM_Dirty);
444 ASSERT_ALWAYS(lookupOp != NULL);
445 ASSERT_ALWAYS(trans->execute(NoCommit) == 0);
446 }
447 }//if(m_params->m_isScan)
448 // m_ndb.closeTransaction(trans);
449 }//for(int iterNo = 0; iterNo<m_params->m_iterations; iterNo++)
450 m_ndb.closeTransaction(trans);
451 }
452
453 static bool printQuery = false;
454
doSQLTest()455 void TestThread::doSQLTest(){
456 if(m_params->m_useLinkedOperations){
457 mySQLExec(m_mysql, "set ndb_join_pushdown = on;");
458 }else{
459 mySQLExec(m_mysql, "set ndb_join_pushdown = off;");
460 }
461 mySQLExec(m_mysql, "SET SESSION query_cache_type = OFF");
462
463 class TextBuf{
464 public:
465 char m_buffer[1000];
466
467 explicit TextBuf(){m_buffer[0] = '\0';}
468
469 // For appending to the string.
470 char* tail(){ return m_buffer + strlen(m_buffer);}
471 };
472
473 TextBuf text;
474
475 sprintf(text.tail(), "select * from ");
476 for(int i = 0; i<m_params->m_depth+1; i++){
477 sprintf(text.tail(), "%s t%d", tableName, i);
478 if(i < m_params->m_depth){
479 sprintf(text.tail(), ", ");
480 }else{
481 sprintf(text.tail(), " where ");
482 }
483 }
484
485 if(m_params->m_scanLength==0){
486 // Root is lookup
487 sprintf(text.tail(), "t0.a=0 ");
488 }else{
489 // Root is scan.
490 sprintf(text.tail(), "t0.a<%d ", m_params->m_scanLength);
491 }
492
493 for(int i = 1; i<m_params->m_depth+1; i++){
494 // Compare primary key of Tn to attribute of Tn-1.
495 sprintf(text.tail(), "and t%d.b=t%d.a ", i-1, i);
496 }
497 if(printQuery){
498 ndbout << text.m_buffer << endl;
499 }
500
501 for(int i = 0; i < m_params->m_iterations; i++){
502 mySQLExec(m_mysql, text.m_buffer);
503 }
504 }
505
wait()506 void TestThread::wait(){
507 ASSERT_ALWAYS(pthread_mutex_lock(&m_mutex)==0);
508 while(m_params!=NULL){
509 ASSERT_ALWAYS(pthread_cond_wait(&m_condition, &m_mutex)==0);
510 }
511 ASSERT_ALWAYS(pthread_mutex_unlock(&m_mutex)==0);
512 }
513
514
515
516
makeDatabase(const char * host,int port,int rowCount)517 static void makeDatabase(const char* host, int port, int rowCount){
518 MYSQL mysql;
519 ASSERT_ALWAYS(mysql_init(&mysql));
520 if(!mysql_real_connect(&mysql, host, "root", "", "",
521 port, NULL, 0)){
522 printMySQLError(mysql, "mysql_real_connect() failed:");
523 ASSERT_ALWAYS(false);
524 }
525 char text[200];
526 sprintf(text, "create database if not exists %s", databaseName);
527 mySQLExec(mysql, text);
528 sprintf(text, "use %s", databaseName);
529 mySQLExec(mysql, text);
530 sprintf(text, "drop table if exists %s", tableName);
531 mySQLExec(mysql, text);
532 sprintf(text, "create table %s(a int not null,"
533 "b int not null,"
534 "primary key(a)) ENGINE=NDB", tableName);
535 mySQLExec(mysql, text);
536 for(int i = 0; i<rowCount; i++){
537 sprintf(text, "insert into %s values(%d, %d)", tableName,
538 i, (i+1)%rowCount);
539 mySQLExec(mysql, text);
540 }
541 }
542
printHeading()543 static void printHeading(){
544 ndbout << endl << "Use SQL; Use linked; Thread count; Iterations; "
545 "Scan length; Depth; Def re-use; Duration (ms); Tuples per sec;" << endl;
546 }
547
548
runTest(TestThread ** threads,int threadCount,TestParameters & param)549 void runTest(TestThread** threads, int threadCount,
550 TestParameters& param){
551 //ndbout << "Doing test " << name << endl;
552 const NDB_TICKS start = NdbTick_CurrentMillisecond();
553 for(int i = 0; i<threadCount; i++){
554 threads[i]->start(param);
555 }
556 for(int i = 0; i<threadCount; i++){
557 threads[i]->wait();
558 }
559 const NDB_TICKS duration = NdbTick_CurrentMillisecond() - start;
560 ndbout << param.m_useSQL << "; ";
561 ndbout << param.m_useLinkedOperations << "; ";
562 ndbout << threadCount << "; ";
563 ndbout << param.m_iterations << "; ";
564 ndbout << param.m_scanLength << "; ";
565 ndbout << param.m_depth <<"; ";
566 ndbout << param.m_queryDefReuse << "; ";
567 ndbout << duration << "; ";
568 int tupPerSec;
569 if(duration==0){
570 tupPerSec = -1;
571 }else{
572 if(param.m_scanLength==0){
573 tupPerSec = threadCount *
574 param.m_iterations *
575 (param.m_depth+1) * 1000 / duration;
576 }else{
577 tupPerSec = threadCount *
578 param.m_iterations *
579 param.m_scanLength *
580 (param.m_depth+1) * 1000 / duration;
581 }
582 }
583 ndbout << tupPerSec << "; ";
584 ndbout << endl;
585 //ndbout << "Test " << name << " done in " << duration << "ms"<< endl;
586 }
587
588 const int threadCount = 1;
589 TestThread** threads = NULL;
590
warmUp()591 void warmUp(){
592 ndbout << endl << "warmUp()" << endl;
593 TestParameters param;
594 param.m_useSQL = true;
595 param.m_iterations = 10;
596 param.m_useLinkedOperations = false;
597 param.m_scanLength = 0;
598 param.m_queryDefReuse = 0;
599
600 printHeading();
601 for(int i = 0; i<20; i++){
602 param.m_depth = i;
603 runTest(threads, threadCount, param);
604 }
605 printHeading();
606 param.m_useLinkedOperations = true;
607 for(int i = 0; i<20; i++){
608 param.m_depth = i;
609 runTest(threads, threadCount, param);
610 }
611 }
612
testLookupDepth(bool useSQL)613 void testLookupDepth(bool useSQL){
614 ndbout << endl << "testLookupDepth()" << endl;
615 TestParameters param;
616 param.m_useSQL = useSQL;
617 param.m_iterations = 100;
618 param.m_useLinkedOperations = false;
619 param.m_scanLength = 0;
620 param.m_queryDefReuse = 0;
621
622 printHeading();
623 for(int i = 0; i<20; i++){
624 param.m_depth = i;
625 runTest(threads, threadCount, param);
626 }
627 printHeading();
628 param.m_useLinkedOperations = true;
629 for(int i = 0; i<20; i++){
630 param.m_depth = i;
631 runTest(threads, threadCount, param);
632 }
633 }
634
testScanDepth(int scanLength,bool useSQL)635 void testScanDepth(int scanLength, bool useSQL){
636 ndbout << endl << "testScanDepth()" << endl;
637 TestParameters param;
638 param.m_useSQL = useSQL;
639 param.m_iterations = 20;
640 param.m_useLinkedOperations = false;
641 param.m_scanLength = scanLength;
642 param.m_queryDefReuse = 0;
643 printHeading();
644 for(int i = 0; i<10; i++){
645 param.m_depth = i;
646 runTest(threads, threadCount, param);
647 }
648 printHeading();
649 param.m_useLinkedOperations = true;
650 for(int i = 0; i<10; i++){
651 param.m_depth = i;
652 runTest(threads, threadCount, param);
653 }
654 }
655
main(int argc,char * argv[])656 int main(int argc, char* argv[]){
657 NDB_INIT(argv[0]);
658 if(argc!=4 && argc!=5){
659 ndbout << "Usage: " << argv[0] << " [--print-query]"
660 << " <mysql IP address> <mysql port> <cluster connect string>"
661 << endl;
662 return -1;
663 }
664 int argno = 1;
665 if(strcmp(argv[argno],"--print-query")==0){
666 printQuery = true;
667 argno++;
668 }
669 const char* const host=argv[argno++];
670 const int port = atoi(argv[argno++]);
671 const char* const connectString = argv[argno];
672
673 makeDatabase(host, port, 200);
674 {
675 Ndb_cluster_connection con(connectString);
676 ASSERT_ALWAYS(con.connect(12, 5, 1) == 0);
677 ASSERT_ALWAYS(con.wait_until_ready(30,30) == 0);
678
679 const int threadCount = 1;
680 threads = new TestThread*[threadCount];
681 for(int i = 0; i<threadCount; i++){
682 threads[i] = new TestThread(con, host, port);
683 }
684 sleep(1);
685
686 //testScanDepth(1);
687 //testScanDepth(2);
688 //testScanDepth(5);
689 warmUp();
690 testScanDepth(50, true);
691 testLookupDepth(true);
692
693 for(int i = 0; i<threadCount; i++){
694 delete threads[i];
695 }
696 delete[] threads;
697 } // Must call ~Ndb_cluster_connection() before ndb_end().
698 ndb_end(0);
699 return 0;
700 }
701
702