• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #3903

24 Apr 2025 11:36AM UTC coverage: 55.307% (+0.09%) from 55.213%
#3903

push

travis-ci

happyguoxy
Sync branches at 2025-04-24 19:35

175024 of 316459 relevant lines covered (55.31%)

1151858.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.17
/tools/taos-tools/src/benchQuery.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the MIT license as published by the Free Software
6
 * Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 */
12

13
#include <bench.h>
14
#include "benchLog.h"
15

16
// query and get result  record is true to total request
17
int selectAndGetResult(qThreadInfo *pThreadInfo, char *command, bool record) {
3,170✔
18
    int ret = 0;
3,170✔
19

20
    // user cancel
21
    if (g_arguments->terminate) {
3,170✔
22
        return -1;
×
23
    }
24

25
    // execute sql
26
    uint32_t threadID = pThreadInfo->threadID;
3,170✔
27
    char dbName[TSDB_DB_NAME_LEN] = {0};
3,170✔
28
    TOOLS_STRNCPY(dbName, g_queryInfo.dbName, TSDB_DB_NAME_LEN);
3,170✔
29

30
    if (g_queryInfo.iface == REST_IFACE) {
3,170✔
31
        int retCode = postProcessSql(command, g_queryInfo.dbName, 0, REST_IFACE,
16✔
32
                                   0, g_arguments->port, false,
16✔
33
                                   pThreadInfo->sockfd, pThreadInfo->filePath);
16✔
34
        if (0 != retCode) {
16✔
35
            errorPrint("====restful return fail, threadID[%u]\n",
×
36
                       threadID);
37
            ret = -1;
×
38
        }
39
    } else {
40
        // query
41
        TAOS *taos = pThreadInfo->conn->taos;
3,154✔
42
        int64_t rows  = 0;
3,154✔
43
        TAOS_RES *res = taos_query(taos, command);
3,154✔
44
        int code = taos_errno(res);
3,148✔
45
        if (res == NULL || code) {
3,155✔
46
            // failed query
47
            errorPrint("failed to execute sql:%s, "
3✔
48
                        "code: 0x%08x, reason:%s\n",
49
                        command, code, taos_errstr(res));
50
            ret = -1;
3✔
51
        } else {
52
            // succ query
53
            if (record)
3,152✔
54
                rows = fetchResult(res, pThreadInfo->filePath);
3,152✔
55
        }
56

57
        // free result
58
        if (res) {
3,155✔
59
            taos_free_result(res);
3,155✔
60
        }
61
        debugPrint("query sql:%s rows:%"PRId64"\n", command, rows);
3,154✔
62
    }
63

64
    // record count
65
    if (ret ==0) {
3,170✔
66
        // succ
67
        if (record) 
3,167✔
68
            pThreadInfo->nSucc ++;
3,167✔
69
    } else {
70
        // fail
71
        if (record)
3✔
72
            pThreadInfo->nFail ++;
3✔
73

74
        // continue option
75
        if (YES_IF_FAILED == g_arguments->continueIfFail) {
3✔
76
            ret = 0; // force continue
×
77
        }
78
    }
79

80
    return ret;
3,170✔
81
}
82

83
// interlligent sleep
84
int32_t autoSleep(uint64_t interval, uint64_t delay ) {
121✔
85
    int32_t msleep = 0;
121✔
86
    if (delay < interval * 1000) {
121✔
87
        msleep = (int32_t)((interval - delay/1000));
44✔
88
        infoPrint("do sleep %dms ...\n", msleep);
44✔
89
        toolsMsleep(msleep);  // ms
44✔
90
        debugPrint("%s\n","do sleep end");
44✔
91
    }
92
    return msleep;
121✔
93
}
94

95
// reset 
96
int32_t resetQueryCache(qThreadInfo* pThreadInfo) {    
×
97
    // execute sql 
98
    if (selectAndGetResult(pThreadInfo, "RESET QUERY CACHE", false)) {
×
99
        errorPrint("%s() LN%d, reset query cache failed\n", __func__, __LINE__);
×
100
        return -1;
×
101
    }
102
    // succ
103
    return 0;
×
104
}
105

106

107

108
//
109
//  ---------------------------------  second levle funtion for Thread -----------------------------------
110
//
111

112
// show rela qps
113
int64_t showRealQPS(qThreadInfo* pThreadInfo, int64_t lastPrintTime, int64_t startTs) {
3,167✔
114
    int64_t now = toolsGetTimestampMs();
3,167✔
115
    if (now - lastPrintTime > 10 * 1000) {
3,168✔
116
        // real total
117
        uint64_t totalQueried = pThreadInfo->nSucc;
1✔
118
        if(g_arguments->continueIfFail == YES_IF_FAILED) {
1✔
119
            totalQueried += pThreadInfo->nFail;
×
120
        }
121
        infoPrint(
1✔
122
            "thread[%d] has currently completed queries: %" PRIu64 ", QPS: %10.3f\n",
123
            pThreadInfo->threadID, totalQueried,
124
            (double)(totalQueried / ((now - startTs) / 1000.0)));
125
        return now;
×
126
    } else {
127
        return lastPrintTime;
3,167✔
128
    }    
129
}
130

131
// spec query mixed thread
132
static void *specQueryMixThread(void *sarg) {
4✔
133
    qThreadInfo *pThreadInfo = (qThreadInfo*)sarg;
4✔
134
#ifdef LINUX
135
    prctl(PR_SET_NAME, "specQueryMixThread");
4✔
136
#endif
137
    // use db
138
    if (g_queryInfo.dbName) {
4✔
139
        if (pThreadInfo->conn &&
4✔
140
            pThreadInfo->conn->taos &&
8✔
141
            taos_select_db(pThreadInfo->conn->taos, g_queryInfo.dbName)) {
4✔
142
                errorPrint("thread[%d]: failed to select database(%s)\n", pThreadInfo->threadID, g_queryInfo.dbName);
×
143
                return NULL;
×
144
        }
145
    }
146

147
    int64_t st = 0;
4✔
148
    int64_t et = 0;
4✔
149
    int64_t startTs        = toolsGetTimestampMs();
4✔
150
    int64_t lastPrintTime  = startTs;
4✔
151
    // batchQuery
152
    bool     batchQuery    = g_queryInfo.specifiedQueryInfo.batchQuery;
4✔
153
    uint64_t queryTimes    = batchQuery ? 1 : g_queryInfo.specifiedQueryInfo.queryTimes;
4✔
154
    uint64_t interval      = batchQuery ? 0 : g_queryInfo.specifiedQueryInfo.queryInterval;
4✔
155
    
156
    pThreadInfo->query_delay_list = benchArrayInit(queryTimes, sizeof(int64_t));
4✔
157
    for (int i = pThreadInfo->start_sql; i <= pThreadInfo->end_sql; ++i) {
8✔
158
        SSQL * sql = benchArrayGet(g_queryInfo.specifiedQueryInfo.sqls, i);
4✔
159
        for (int j = 0; j < queryTimes; ++j) {
28✔
160
            // use cancel
161
            if(g_arguments->terminate) {
24✔
162
                infoPrint("%s\n", "user cancel , so exit testing.");
×
163
                break;
×
164
            }
165

166
            // reset cache
167
            if (g_queryInfo.reset_query_cache) {
24✔
168
                if (resetQueryCache(pThreadInfo)) {
×
169
                    errorPrint("%s() LN%d, reset query cache failed\n", __func__, __LINE__);
×
170
                    return NULL;
×
171
                }
172
            }
173

174
            // execute sql
175
            st = toolsGetTimestampUs();
24✔
176
            int ret = selectAndGetResult(pThreadInfo, sql->command, true);
24✔
177
            if (ret) {
24✔
178
                g_fail = true;
×
179
                errorPrint("failed call mix selectAndGetResult, i=%d j=%d", i, j);
×
180
                return NULL;
×
181
            }
182
            et = toolsGetTimestampUs();
24✔
183

184
            // sleep
185
            if (interval > 0) {
24✔
186
                autoSleep(interval, et - st);
4✔
187
            }
188

189
            // delay
190
            if (ret == 0) {
24✔
191
                int64_t* delay = benchCalloc(1, sizeof(int64_t), false);
24✔
192
                *delay = et - st;
24✔
193
                debugPrint("%s() LN%d, delay: %"PRId64"\n", __func__, __LINE__, *delay);
24✔
194

195
                pThreadInfo->total_delay += *delay;
24✔
196
                if(benchArrayPush(pThreadInfo->query_delay_list, delay) == NULL){
24✔
197
                    tmfree(delay);
×
198
                }
199
            }
200

201
            // real show
202
            lastPrintTime = showRealQPS(pThreadInfo, lastPrintTime, startTs);
24✔
203
        }
204
    }
205

206
    return NULL;
4✔
207
}
208

209
// spec query thread
210
static void *specQueryThread(void *sarg) {
51✔
211
    qThreadInfo *pThreadInfo = (qThreadInfo *)sarg;
51✔
212
#ifdef LINUX
213
    prctl(PR_SET_NAME, "specQueryThread");
51✔
214
#endif
215
    uint64_t st = 0;
51✔
216
    uint64_t et = 0;
51✔
217
    int32_t  index = 0;
51✔
218

219
    // use db
220
    if (g_queryInfo.dbName) {
51✔
221
        if (pThreadInfo->conn &&
51✔
222
            pThreadInfo->conn->taos &&
90✔
223
            taos_select_db(pThreadInfo->conn->taos, g_queryInfo.dbName)) {
45✔
224
                errorPrint("thread[%d]: failed to select database(%s)\n", pThreadInfo->threadID, g_queryInfo.dbName);
×
225
                return NULL;
×
226
        }
227
    }
228

229
    uint64_t  queryTimes = g_queryInfo.specifiedQueryInfo.queryTimes;
51✔
230
    uint64_t  interval   = g_queryInfo.specifiedQueryInfo.queryInterval;
51✔
231
    pThreadInfo->query_delay_list = benchArrayInit(queryTimes, sizeof(int64_t));
51✔
232

233
    uint64_t  startTs       = toolsGetTimestampMs();
51✔
234
    uint64_t  lastPrintTime = startTs;
51✔
235

236
    SSQL * sql = benchArrayGet(g_queryInfo.specifiedQueryInfo.sqls, pThreadInfo->querySeq);
51✔
237

238
    if (sql->result[0] != '\0') {
51✔
239
        snprintf(pThreadInfo->filePath, MAX_PATH_LEN, "%s-%d",
47✔
240
                sql->result, pThreadInfo->threadID);
47✔
241
    }
242

243
    while (index < queryTimes) {
171✔
244
        // use cancel
245
        if(g_arguments->terminate) {
123✔
246
            infoPrint("thread[%d] user cancel , so exit testing.\n", pThreadInfo->threadID);
×
247
            break;
3✔
248
        }
249

250
        // reset cache
251
        if (g_queryInfo.reset_query_cache) {
123✔
252
            if (resetQueryCache(pThreadInfo)) {
×
253
                errorPrint("%s() LN%d, reset query cache failed\n", __func__, __LINE__);
×
254
                return NULL;
×
255
            }
256
        }
257

258
        // execute sql
259
        st = toolsGetTimestampUs();
123✔
260
        int ret = selectAndGetResult(pThreadInfo, sql->command, true);
123✔
261
        if (ret) {
123✔
262
            g_fail = true;
3✔
263
            errorPrint("failed call spec selectAndGetResult, index=%d\n", index);
3✔
264
            break;
3✔
265
        }
266
        et = toolsGetTimestampUs();
120✔
267

268
        // sleep
269
        if (interval > 0) {
120✔
270
            autoSleep(interval, et - st);
80✔
271
        }
272

273

274
        uint64_t delay = et - st;
120✔
275
        debugPrint("%s() LN%d, delay: %"PRIu64"\n", __func__, __LINE__, delay);
120✔
276

277
        if (ret == 0) {
120✔
278
            // only succ add delay list
279
            benchArrayPushNoFree(pThreadInfo->query_delay_list, &delay);
120✔
280
            pThreadInfo->total_delay += delay;
120✔
281
        }
282
        index++;
120✔
283

284
        // real show
285
        lastPrintTime = showRealQPS(pThreadInfo, lastPrintTime, startTs);
120✔
286
    }
287

288
    return NULL;
51✔
289
}
290

291
// super table query thread
292
static void *stbQueryThread(void *sarg) {
14✔
293
    char *sqlstr = benchCalloc(1, TSDB_MAX_ALLOWED_SQL_LEN, false);
14✔
294
    qThreadInfo *pThreadInfo = (qThreadInfo *)sarg;
14✔
295
#ifdef LINUX
296
    prctl(PR_SET_NAME, "stbQueryThread");
14✔
297
#endif
298

299
    uint64_t st = 0;
14✔
300
    uint64_t et = 0;
14✔
301

302
    uint64_t queryTimes = g_queryInfo.superQueryInfo.queryTimes;
14✔
303
    uint64_t interval   = g_queryInfo.superQueryInfo.queryInterval;
14✔
304
    pThreadInfo->query_delay_list = benchArrayInit(queryTimes, sizeof(uint64_t));
14✔
305
    
306
    uint64_t startTs = toolsGetTimestampMs();
14✔
307
    uint64_t lastPrintTime = startTs;
14✔
308
    while (queryTimes--) {
51✔
309
        // use cancel
310
        if(g_arguments->terminate) {
37✔
311
            infoPrint("%s\n", "user cancel , so exit testing.");
×
312
            break;
×
313
        }
314

315
        // reset cache
316
        if (g_queryInfo.reset_query_cache) {
37✔
317
            if (resetQueryCache(pThreadInfo)) {
×
318
                errorPrint("%s() LN%d, reset query cache failed\n", __func__, __LINE__);
×
319
                return NULL;
×
320
            }
321
        }
322

323
        // execute
324
        st = toolsGetTimestampMs();
37✔
325
        // for each table
326
        for (int i = (int)pThreadInfo->start_table_from; i <= pThreadInfo->end_table_to; i++) {
91✔
327
            // use cancel
328
            if(g_arguments->terminate) {
54✔
329
                infoPrint("%s\n", "user cancel , so exit testing.");
×
330
                break;
×
331
            }
332

333
            // for each sql
334
            for (int j = 0; j < g_queryInfo.superQueryInfo.sqlCount; j++) {
3,077✔
335
                memset(sqlstr, 0, TSDB_MAX_ALLOWED_SQL_LEN);
3,024✔
336
                // use cancel
337
                if(g_arguments->terminate) {
3,024✔
338
                    infoPrint("%s\n", "user cancel , so exit testing.");
×
339
                    break;
1✔
340
                }
341
                
342
                // get real child name sql
343
                if (replaceChildTblName(g_queryInfo.superQueryInfo.sql[j], sqlstr, i)) {
3,024✔
344
                    // fault
345
                    tmfree(sqlstr);
×
346
                    return NULL;
×
347
                }
348

349
                if (g_queryInfo.superQueryInfo.result[j][0] != '\0') {
3,023✔
350
                    snprintf(pThreadInfo->filePath, MAX_PATH_LEN, "%s-%d",
3,024✔
351
                            g_queryInfo.superQueryInfo.result[j],
3,024✔
352
                            pThreadInfo->threadID);
353
                }
354

355
                // execute sql
356
                uint64_t s = toolsGetTimestampUs();
3,023✔
357
                int ret = selectAndGetResult(pThreadInfo, sqlstr, true);
3,023✔
358
                if (ret) {
3,023✔
359
                    // found error
360
                    errorPrint("failed call stb selectAndGetResult, i=%d j=%d\n", i, j);
×
361
                    g_fail = true;
×
362
                    tmfree(sqlstr);
×
363
                    return NULL;
×
364
                }
365
                uint64_t delay = toolsGetTimestampUs() - s;
3,023✔
366
                debugPrint("%s() LN%d, delay: %"PRIu64"\n", __func__, __LINE__, delay);
3,023✔
367
                if (ret == 0) {
3,023✔
368
                    // only succ add delay list
369
                    benchArrayPushNoFree(pThreadInfo->query_delay_list, &delay);
3,023✔
370
                    pThreadInfo->total_delay += delay;
3,023✔
371
                }
372

373
                // show real QPS
374
                lastPrintTime = showRealQPS(pThreadInfo, lastPrintTime, startTs);
3,023✔
375
            }
376
        }
377
        et = toolsGetTimestampMs();
37✔
378

379
        // sleep
380
        if (interval > 0) {
37✔
381
            autoSleep(interval, et - st);
37✔
382
        }
383

384
    }
385
    tmfree(sqlstr);
14✔
386

387
    return NULL;
14✔
388
}
389

390
//
391
// ---------------------------------  firse level function ------------------------------
392
//
393

394
void totalChildQuery(qThreadInfo* infos, int threadCnt, int64_t spend, BArray *pDelays) {
5✔
395
    // valid check
396
    if (infos == NULL || threadCnt == 0) {
5✔
397
        return ;
×
398
    }
399
    
400
    // statistic
401
    BArray * delay_list = benchArrayInit(1, sizeof(int64_t));
5✔
402
    double total_delays = 0;
5✔
403

404
    // clear
405
    for (int i = 0; i < threadCnt; ++i) {
23✔
406
        qThreadInfo * pThreadInfo = infos + i;
18✔
407
        if(pThreadInfo->query_delay_list == NULL) {
18✔
408
            continue;;
×
409
        }
410
        
411
        // append delay
412
        benchArrayAddBatch(delay_list, pThreadInfo->query_delay_list->pData,
18✔
413
                pThreadInfo->query_delay_list->size, false);
18✔
414
        total_delays += pThreadInfo->total_delay;
18✔
415

416
        // free delay
417
        benchArrayDestroy(pThreadInfo->query_delay_list);
18✔
418
        pThreadInfo->query_delay_list = NULL;
18✔
419

420
    }
421

422
    // succ is zero
423
    if (delay_list->size == 0) {
5✔
424
        errorPrint("%s", "succ queries count is zero.\n");
×
425
        benchArrayDestroy(delay_list);
×
426
        return ;
×
427
    }
428

429

430
    // sort
431
    qsort(delay_list->pData, delay_list->size, delay_list->elemSize, compare);
5✔
432

433
    // show delay min max
434
    if (delay_list->size) {
5✔
435
        infoPrint(
5✔
436
                "spend %.6fs using "
437
                "%d threads complete query %d times,  "
438
                "min delay: %.6fs, "
439
                "avg delay: %.6fs, "
440
                "p90: %.6fs, "
441
                "p95: %.6fs, "
442
                "p99: %.6fs, "
443
                "max: %.6fs\n",
444
                spend/1E6,
445
                threadCnt, (int)delay_list->size,
446
                *(int64_t *)(benchArrayGet(delay_list, 0))/1E6,
447
                (double)total_delays/delay_list->size/1E6,
448
                *(int64_t *)(benchArrayGet(delay_list,
449
                                    (int32_t)(delay_list->size * 0.9)))/1E6,
450
                *(int64_t *)(benchArrayGet(delay_list,
451
                                    (int32_t)(delay_list->size * 0.95)))/1E6,
452
                *(int64_t *)(benchArrayGet(delay_list,
453
                                    (int32_t)(delay_list->size * 0.99)))/1E6,
454
                *(int64_t *)(benchArrayGet(delay_list,
455
                                    (int32_t)(delay_list->size - 1)))/1E6);
456
    }
457

458
    // copy to another
459
    if (pDelays) {
5✔
460
        benchArrayAddBatch(pDelays, delay_list->pData, delay_list->size, false);
×
461
    }
462
    benchArrayDestroy(delay_list);
5✔
463
}
464

465
//
466
// super table query
467
//
468
static int stbQuery(uint16_t iface, char* dbName) {
3✔
469
    int ret = -1;
3✔
470
    pthread_t * pidsOfSub   = NULL;
3✔
471
    qThreadInfo *threadInfos = NULL;
3✔
472
    g_queryInfo.superQueryInfo.totalQueried = 0;
3✔
473
    g_queryInfo.superQueryInfo.totalFail    = 0;
3✔
474

475
    // check
476
    if ((g_queryInfo.superQueryInfo.sqlCount == 0)
3✔
477
        || (g_queryInfo.superQueryInfo.threadCnt == 0)) {
3✔
478
        return 0;
×
479
    }
480

481
    // malloc 
482
    pidsOfSub = benchCalloc(1, g_queryInfo.superQueryInfo.threadCnt
3✔
483
                            *sizeof(pthread_t),
484
                            false);
485
    threadInfos = benchCalloc(1, g_queryInfo.superQueryInfo.threadCnt
3✔
486
                                *sizeof(qThreadInfo), false);
487

488
    int64_t ntables = g_queryInfo.superQueryInfo.childTblCount;
3✔
489
    int nConcurrent = g_queryInfo.superQueryInfo.threadCnt;
3✔
490

491
    int64_t a = ntables / nConcurrent;
3✔
492
    if (a < 1) {
3✔
493
        nConcurrent = (int)ntables;
1✔
494
        a = 1;
1✔
495
    }
496

497
    int64_t b = 0;
3✔
498
    if (nConcurrent != 0) {
3✔
499
        b = ntables % nConcurrent;
3✔
500
    }
501

502
    uint64_t tableFrom = 0;
3✔
503
    int threadCnt = 0;
3✔
504
    for (int i = 0; i < nConcurrent; i++) {
17✔
505
        qThreadInfo *pThreadInfo = threadInfos + i;
14✔
506
        pThreadInfo->threadID = i;
14✔
507
        pThreadInfo->start_table_from = tableFrom;
14✔
508
        pThreadInfo->ntables = i < b ? a + 1 : a;
14✔
509
        pThreadInfo->end_table_to =
14✔
510
                i < b ? tableFrom + a : tableFrom + a - 1;
14✔
511
        tableFrom = pThreadInfo->end_table_to + 1;
14✔
512
        // create conn
513
        if (initQueryConn(pThreadInfo, iface)){
14✔
514
            break;
×
515
        }
516
        int code = pthread_create(pidsOfSub + i, NULL, stbQueryThread, pThreadInfo);
14✔
517
        if (code != 0) {
14✔
518
            errorPrint("failed stbQueryThread create. error code =%d \n", code);
×
519
            break;
×
520
        }
521
        threadCnt ++;
14✔
522
    }
523

524
    bool needExit = false;
3✔
525
    // if failed, set termainte flag true like ctrl+c exit
526
    if (threadCnt != nConcurrent  ) {
3✔
527
        needExit = true;
×
528
        g_arguments->terminate = true;
×
529
        goto OVER;
×
530
    }
531

532
    // reset total
533
    g_queryInfo.superQueryInfo.totalQueried = 0;
3✔
534
    g_queryInfo.superQueryInfo.totalFail    = 0;
3✔
535

536
    // real thread count
537
    g_queryInfo.superQueryInfo.threadCnt = threadCnt;
3✔
538
    int64_t start = toolsGetTimestampUs();
3✔
539

540
    for (int i = 0; i < threadCnt; i++) {
17✔
541
        pthread_join(pidsOfSub[i], NULL);
14✔
542
        qThreadInfo *pThreadInfo = threadInfos + i;
14✔
543
        // add succ
544
        g_queryInfo.superQueryInfo.totalQueried += pThreadInfo->nSucc;
14✔
545
        if (g_arguments->continueIfFail == YES_IF_FAILED) {
14✔
546
            // "yes" need add fail cnt
547
            g_queryInfo.superQueryInfo.totalQueried += pThreadInfo->nFail;
×
548
            g_queryInfo.superQueryInfo.totalFail    += pThreadInfo->nFail;
×
549
        }
550

551
        // close conn
552
        closeQueryConn(pThreadInfo, iface);
14✔
553
    }
554
    int64_t end = toolsGetTimestampUs();
3✔
555

556
    if (needExit) {
3✔
557
        goto OVER;
×
558
    }
559

560
    // total show
561
    totalChildQuery(threadInfos, threadCnt, end - start, NULL);
3✔
562

563
    ret = 0;
3✔
564

565
OVER:
3✔
566
    tmfree((char *)pidsOfSub);
3✔
567
    tmfree((char *)threadInfos);
3✔
568

569
    for (int64_t i = 0; i < g_queryInfo.superQueryInfo.childTblCount; ++i) {
25✔
570
        tmfree(g_queryInfo.superQueryInfo.childTblName[i]);
22✔
571
    }
572
    tmfree(g_queryInfo.superQueryInfo.childTblName);
3✔
573
    return ret;
3✔
574
}
575

576
//
577
// specQuery
578
//
579
static int specQuery(uint16_t iface, char* dbName) {
4✔
580
    int ret = -1;
4✔
581
    pthread_t    *pids = NULL;
4✔
582
    qThreadInfo *infos = NULL;
4✔
583
    int    nConcurrent = g_queryInfo.specifiedQueryInfo.concurrent;
4✔
584
    uint64_t nSqlCount = g_queryInfo.specifiedQueryInfo.sqls->size;
4✔
585
    g_queryInfo.specifiedQueryInfo.totalQueried = 0;
4✔
586
    g_queryInfo.specifiedQueryInfo.totalFail    = 0;
4✔
587

588
    // check invaid
589
    if(nSqlCount == 0 || nConcurrent == 0 ) {
4✔
590
        if(nSqlCount == 0)
×
591
           warnPrint("specified table query sql count is %" PRIu64 ".\n", nSqlCount);
×
592
        if(nConcurrent == 0)
×
593
           warnPrint("nConcurrent is %d , specified_table_query->nConcurrent is zero. \n", nConcurrent);
×
594
        return 0;
×
595
    }
596

597
    // malloc threads memory
598
    pids  = benchCalloc(1, nConcurrent * sizeof(pthread_t),  false);
4✔
599
    infos = benchCalloc(1, nConcurrent * sizeof(qThreadInfo), false);
4✔
600

601
    for (uint64_t i = 0; i < nSqlCount; i++) {
20✔
602
        if( g_arguments->terminate ) {
17✔
603
            break;
×
604
        }
605

606
        // reset
607
        memset(pids,  0, nConcurrent * sizeof(pthread_t));
17✔
608
        memset(infos, 0, nConcurrent * sizeof(qThreadInfo));
17✔
609

610
        // get execute sql
611
        SSQL *sql = benchArrayGet(g_queryInfo.specifiedQueryInfo.sqls, i);
17✔
612

613
        // create threads
614
        int threadCnt = 0;
17✔
615
        for (int j = 0; j < nConcurrent; j++) {
68✔
616
           qThreadInfo *pThreadInfo = infos + j;
51✔
617
           pThreadInfo->threadID = i * nConcurrent + j;
51✔
618
           pThreadInfo->querySeq = i;
51✔
619

620
           // create conn
621
           if (initQueryConn(pThreadInfo, iface)) {
51✔
622
               break;
×
623
           }
624

625
           int code = pthread_create(pids + j, NULL, specQueryThread, pThreadInfo);
51✔
626
           if (code != 0) {
51✔
627
               errorPrint("failed specQueryThread create. error code =%d \n", code);
×
628
               break;
×
629
           }
630
           threadCnt++;
51✔
631
        }
632

633
        bool needExit = false;
17✔
634
        // if failed, set termainte flag true like ctrl+c exit
635
        if (threadCnt != nConcurrent  ) {
17✔
636
            needExit = true;
×
637
            g_arguments->terminate = true;
×
638
        }
639

640
        int64_t start = toolsGetTimestampUs();
17✔
641
        // wait threads execute finished one by one
642
        for (int j = 0; j < threadCnt ; j++) {
68✔
643
           pthread_join(pids[j], NULL);
51✔
644
           qThreadInfo *pThreadInfo = infos + j;
51✔
645
           closeQueryConn(pThreadInfo, iface);
51✔
646

647
           // need exit in loop
648
           if (needExit) {
51✔
649
                // free BArray
650
                benchArrayDestroy(pThreadInfo->query_delay_list);
×
651
                pThreadInfo->query_delay_list = NULL;
×
652
           }
653
        }
654
        int64_t spend = toolsGetTimestampUs() - start;
17✔
655
        if(spend == 0) {
17✔
656
            // avoid xx/spend expr throw error
657
            spend = 1;
×
658
        }
659

660
        // create 
661
        if (needExit) {
17✔
662
            errorPrint("failed to create thread. expect nConcurrent=%d real threadCnt=%d,  exit testing.\n", nConcurrent, threadCnt);
×
663
            goto OVER;
×
664
        }
665

666
        //
667
        // show QPS and P90 ...
668
        //
669
        uint64_t n = 0;
17✔
670
        double  total_delays = 0.0;
17✔
671
        uint64_t totalQueried = 0;
17✔
672
        uint64_t totalFail    = 0;
17✔
673
        for (int j = 0; j < threadCnt; j++) {
68✔
674
           qThreadInfo *pThreadInfo = infos + j;
51✔
675
           if(pThreadInfo->query_delay_list == NULL) {
51✔
676
                continue;;
×
677
           }
678
           
679
           // total one sql
680
           for (uint64_t k = 0; k < pThreadInfo->query_delay_list->size; k++) {
171✔
681
                int64_t * delay = benchArrayGet(pThreadInfo->query_delay_list, k);
120✔
682
                sql->delay_list[n++] = *delay;
120✔
683
                total_delays += *delay;
120✔
684
           }
685

686
           // total queries
687
           totalQueried += pThreadInfo->nSucc;
51✔
688
           if (g_arguments->continueIfFail == YES_IF_FAILED) {
51✔
689
                totalQueried += pThreadInfo->nFail;
4✔
690
                totalFail    += pThreadInfo->nFail;
4✔
691
           }
692

693
           // free BArray query_delay_list
694
           benchArrayDestroy(pThreadInfo->query_delay_list);
51✔
695
           pThreadInfo->query_delay_list = NULL;
51✔
696
        }
697

698
        // appand current sql
699
        g_queryInfo.specifiedQueryInfo.totalQueried += totalQueried;
17✔
700
        g_queryInfo.specifiedQueryInfo.totalFail    += totalFail;
17✔
701

702
        // succ is zero
703
        if(totalQueried == 0 || n == 0) {
17✔
704
            errorPrint("%s", "succ queries count is zero.\n");
1✔
705
            goto OVER;
1✔
706
        }
707

708
        qsort(sql->delay_list, n, sizeof(uint64_t), compare);
16✔
709
        int32_t bufLen = strlen(sql->command) + 512;
16✔
710
        char * buf = benchCalloc(bufLen, sizeof(char), false);
16✔
711
        snprintf(buf , bufLen, "complete query with %d threads and %" PRIu64 " "
16✔
712
                             "sql %"PRIu64" spend %.6fs QPS: %.3f "
713
                             "query delay "
714
                             "avg: %.6fs "
715
                             "min: %.6fs "
716
                             "max: %.6fs "
717
                             "p90: %.6fs "
718
                             "p95: %.6fs "
719
                             "p99: %.6fs "
720
                             "SQL command: %s \n",
721
                             threadCnt, totalQueried,
722
                             i + 1, spend/1E6, totalQueried / (spend/1E6),
16✔
723
                             total_delays/n/1E6,           /* avg */
16✔
724
                             sql->delay_list[0] / 1E6,     /* min */
16✔
725
                             sql->delay_list[n - 1] / 1E6, /* max */
16✔
726
                             /*  p90 */
727
                             sql->delay_list[(uint64_t)(n * 0.90)] / 1E6,
16✔
728
                             /*  p95 */
729
                             sql->delay_list[(uint64_t)(n * 0.95)] / 1E6,
16✔
730
                             /*  p99 */
731
                             sql->delay_list[(uint64_t)(n * 0.99)] / 1E6, 
16✔
732
                             sql->command);
733

734
        infoPrintNoTimestamp("%s", buf);
16✔
735
        infoPrintNoTimestampToFile("%s", buf);
16✔
736
        tmfree(buf);
16✔
737
    }
738
    ret = 0;
3✔
739

740
OVER:
4✔
741
    tmfree((char *)pids);
4✔
742
    tmfree((char *)infos);
4✔
743

744
    // free specialQueryInfo
745
    freeSpecialQueryInfo();
4✔
746
    return ret;
4✔
747
}
748

749
//
750
// specQueryMix
751
//
752
static int specQueryMix(uint16_t iface, char* dbName) {
2✔
753
    // init
754
    int ret            = -1;
2✔
755
    int nConcurrent    = g_queryInfo.specifiedQueryInfo.concurrent;
2✔
756
    pthread_t * pids   = benchCalloc(nConcurrent, sizeof(pthread_t), true);
2✔
757
    qThreadInfo *infos = benchCalloc(nConcurrent, sizeof(qThreadInfo), true);
2✔
758

759
    // concurent calc
760
    int total_sql_num = g_queryInfo.specifiedQueryInfo.sqls->size;
2✔
761
    int start_sql     = 0;
2✔
762
    int a             = total_sql_num / nConcurrent;
2✔
763
    if (a < 1) {
2✔
764
        warnPrint("sqls num:%d < concurent:%d, so set concurrent to %d\n", total_sql_num, nConcurrent, nConcurrent);
1✔
765
        nConcurrent = total_sql_num;
1✔
766
        a = 1;
1✔
767
    }
768
    int b = 0;
2✔
769
    if (nConcurrent != 0) {
2✔
770
        b = total_sql_num % nConcurrent;
2✔
771
    }
772

773
    //
774
    // running
775
    //
776
    int threadCnt = 0;
2✔
777
    for (int i = 0; i < nConcurrent; ++i) {
6✔
778
        qThreadInfo *pThreadInfo = infos + i;
4✔
779
        pThreadInfo->threadID    = i;
4✔
780
        pThreadInfo->start_sql   = start_sql;
4✔
781
        pThreadInfo->end_sql     = i < b ? start_sql + a : start_sql + a - 1;
4✔
782
        start_sql = pThreadInfo->end_sql + 1;
4✔
783
        pThreadInfo->total_delay = 0;
4✔
784

785
        // create conn
786
        if (initQueryConn(pThreadInfo, iface)){
4✔
787
            break;
×
788
        }
789
        // main run
790
        int code = pthread_create(pids + i, NULL, specQueryMixThread, pThreadInfo);
4✔
791
        if (code != 0) {
4✔
792
            errorPrint("failed specQueryMixThread create. error code =%d \n", code);
×
793
            break;
×
794
        }
795
        
796
        threadCnt ++;
4✔
797
    }
798

799
    bool needExit = false;
2✔
800
    // if failed, set termainte flag true like ctrl+c exit
801
    if (threadCnt != nConcurrent) {
2✔
802
        needExit = true;
×
803
        g_arguments->terminate = true;
×
804
    }
805

806
    // reset total
807
    g_queryInfo.specifiedQueryInfo.totalQueried = 0;
2✔
808
    g_queryInfo.specifiedQueryInfo.totalFail    = 0;
2✔
809

810
    int64_t start = toolsGetTimestampUs();
2✔
811
    for (int i = 0; i < threadCnt; ++i) {
6✔
812
        pthread_join(pids[i], NULL);
4✔
813
        qThreadInfo *pThreadInfo = infos + i;
4✔
814
        closeQueryConn(pThreadInfo, iface);
4✔
815

816
        // total queries
817
        g_queryInfo.specifiedQueryInfo.totalQueried += pThreadInfo->nSucc;
4✔
818
        if (g_arguments->continueIfFail == YES_IF_FAILED) {
4✔
819
            // yes need add failed count
820
            g_queryInfo.specifiedQueryInfo.totalQueried += pThreadInfo->nFail;
2✔
821
            g_queryInfo.specifiedQueryInfo.totalFail    += pThreadInfo->nFail;
2✔
822
        }
823

824
        // destory
825
        if (needExit) {
4✔
826
            benchArrayDestroy(pThreadInfo->query_delay_list);
×
827
            pThreadInfo->query_delay_list = NULL;
×
828
        }
829
    }
830
    int64_t end = toolsGetTimestampUs();
2✔
831

832
    // create 
833
    if (needExit) {
2✔
834
        errorPrint("failed to create thread. expect nConcurrent=%d real threadCnt=%d,  exit testing.\n", nConcurrent, threadCnt);
×
835
        goto OVER;
×
836
    }
837

838
    // statistic
839
    totalChildQuery(infos, threadCnt, end - start, NULL);
2✔
840
    ret = 0;
2✔
841

842
OVER:
2✔
843
    tmfree(pids);
2✔
844
    tmfree(infos);
2✔
845

846
    // free sqls
847
    freeSpecialQueryInfo();
2✔
848

849
    return ret;
2✔
850
}
851

852
void totalBatchQuery(int32_t allSleep, BArray *pDelays) {
×
853
    // sort
854
    qsort(pDelays->pData, pDelays->size, pDelays->elemSize, compare);
×
855

856
    // total delays
857
    double totalDelays = 0;
×
858
    for (size_t i = 0; i < pDelays->size; i++) {
×
859
        int64_t *delay = benchArrayGet(pDelays, i);
×
860
        totalDelays   += *delay;
×
861
    }    
862

863
    printf("\n");
×
864
    // show sleep times
865
    if (allSleep > 0) {
×
866
        infoPrint("All sleep spend: %.3fs\n", (float)allSleep/1000);
×
867
    }
868

869
    // show P90 ...
870
    if (pDelays->size) {
×
871
        infoPrint(
×
872
                "Total delay: "
873
                "min delay: %.6fs, "
874
                "avg delay: %.6fs, "
875
                "p90: %.6fs, "
876
                "p95: %.6fs, "
877
                "p99: %.6fs, "
878
                "max: %.6fs\n",
879
                *(int64_t *)(benchArrayGet(pDelays, 0))/1E6,
880
                (double)totalDelays/pDelays->size/1E6,
881
                *(int64_t *)(benchArrayGet(pDelays,
882
                                    (int32_t)(pDelays->size * 0.9)))/1E6,
883
                *(int64_t *)(benchArrayGet(pDelays,
884
                                    (int32_t)(pDelays->size * 0.95)))/1E6,
885
                *(int64_t *)(benchArrayGet(pDelays,
886
                                    (int32_t)(pDelays->size * 0.99)))/1E6,
887
                *(int64_t *)(benchArrayGet(pDelays,
888
                                    (int32_t)(pDelays->size - 1)))/1E6);
889
    }
890
}
×
891

892
//
893
// specQuery Mix Batch
894
//
895
static int specQueryBatch(uint16_t iface, char* dbName) {
×
896
    // init
897
    BArray *pDelays    = NULL;
×
898
    int ret            = -1;
×
899
    int nConcurrent    = g_queryInfo.specifiedQueryInfo.concurrent;
×
900
    uint64_t interval  = g_queryInfo.specifiedQueryInfo.queryInterval;
×
901
    pthread_t * pids   = benchCalloc(nConcurrent, sizeof(pthread_t), true);
×
902
    qThreadInfo *infos = benchCalloc(nConcurrent, sizeof(qThreadInfo), true);
×
903
    infoPrint("start batch query, sleep interval:%" PRIu64 "ms query times:%" PRIu64 " thread:%d \n", 
×
904
        interval, g_queryInfo.query_times, nConcurrent);    
905

906
    // concurent calc
907
    int total_sql_num = g_queryInfo.specifiedQueryInfo.sqls->size;
×
908
    int start_sql     = 0;
×
909
    int a             = total_sql_num / nConcurrent;
×
910
    if (a < 1) {
×
911
        warnPrint("sqls num:%d < concurent:%d, set concurrent %d\n", total_sql_num, nConcurrent, nConcurrent);
×
912
        nConcurrent = total_sql_num;
×
913
        a = 1;
×
914
    }
915
    int b = 0;
×
916
    if (nConcurrent != 0) {
×
917
        b = total_sql_num % nConcurrent;
×
918
    }
919

920
    //
921
    // connect
922
    //
923
    int connCnt = 0;
×
924
    for (int i = 0; i < nConcurrent; ++i) {
×
925
        qThreadInfo *pThreadInfo = infos + i;
×
926
        // create conn
927
        if (initQueryConn(pThreadInfo, iface)){
×
928
            ret = -1;
×
929
            goto OVER;      
×
930
        }
931
        
932
        connCnt ++;
×
933
    }
934

935

936
    // reset total
937
    g_queryInfo.specifiedQueryInfo.totalQueried = 0;
×
938
    g_queryInfo.specifiedQueryInfo.totalFail    = 0;
×
939

940
    //
941
    // running
942
    //
943
    int threadCnt = 0;
×
944
    int allSleep  = 0;
×
945
    pDelays       = benchArrayInit(10, sizeof(int64_t));
×
946
    for (int m = 0; m < g_queryInfo.query_times; ++m) {
×
947
        // reset
948
        threadCnt = 0;
×
949
        start_sql = 0;
×
950

951
        // create thread
952
        for (int i = 0; i < nConcurrent; ++i) {
×
953
            qThreadInfo *pThreadInfo = infos + i;
×
954
            pThreadInfo->threadID    = i;
×
955
            pThreadInfo->start_sql   = start_sql;
×
956
            pThreadInfo->end_sql     = i < b ? start_sql + a : start_sql + a - 1;
×
957
            start_sql = pThreadInfo->end_sql + 1;
×
958
            pThreadInfo->total_delay = 0;
×
959
            // total zero
960
            pThreadInfo->nSucc = 0;
×
961
            pThreadInfo->nFail = 0;
×
962
    
963
            // main run
964
            int code = pthread_create(pids + i, NULL, specQueryMixThread, pThreadInfo);
×
965
            if (code != 0) {
×
966
                errorPrint("failed specQueryBatchThread create. error code =%d \n", code);
×
967
                break;
×
968
            }
969
            
970
            threadCnt ++;
×
971
        }
972
        
973
        bool needExit = false;
×
974
        if (threadCnt != nConcurrent) {
×
975
            // if failed, set termainte flag true like ctrl+c exit
976
            needExit = true;
×
977
            g_arguments->terminate = true;
×
978
        }
979
        
980
        // wait thread finished
981
        int64_t start = toolsGetTimestampUs();
×
982
        for (int i = 0; i < threadCnt; ++i) {
×
983
            pthread_join(pids[i], NULL);
×
984
            qThreadInfo *pThreadInfo = infos + i;
×
985
            // total queries
986
            g_queryInfo.specifiedQueryInfo.totalQueried += pThreadInfo->nSucc;
×
987
            if (g_arguments->continueIfFail == YES_IF_FAILED) {
×
988
                // yes need add failed count
989
                g_queryInfo.specifiedQueryInfo.totalQueried += pThreadInfo->nFail;
×
990
                g_queryInfo.specifiedQueryInfo.totalFail    += pThreadInfo->nFail;
×
991
            }
992
    
993
            // destory
994
            if (needExit) {
×
995
                benchArrayDestroy(pThreadInfo->query_delay_list);
×
996
                pThreadInfo->query_delay_list = NULL;
×
997
            }
998
        }
999
        int64_t end = toolsGetTimestampUs();
×
1000
    
1001
        // create 
1002
        if (needExit) {
×
1003
            errorPrint("failed to create thread. expect nConcurrent=%d real threadCnt=%d,  exit testing.\n", nConcurrent, threadCnt);
×
1004
            goto OVER;
×
1005
        }
1006
    
1007
        // batch total
1008
        printf("\n");
×
1009
        totalChildQuery(infos, threadCnt, end - start, pDelays);
×
1010

1011
        // show batch total
1012
        int64_t delay = end - start;
×
1013
        infoPrint("count:%d execute batch spend: %" PRId64 "ms\n", m + 1, delay/1000);
×
1014

1015
        // sleep
1016
        if ( g_queryInfo.specifiedQueryInfo.batchQuery && interval > 0) {
×
1017
            allSleep += autoSleep(interval, delay);
×
1018
        }
1019

1020
        // check cancel
1021
        if(g_arguments->terminate) {
×
1022
            break;
×
1023
        }
1024
    }
1025
    ret = 0;
×
1026
    
1027
    // all total
1028
    totalBatchQuery(allSleep, pDelays);
×
1029

1030
OVER:
×
1031
    // close conn
1032
    for (int i = 0; i < connCnt; ++i) {
×
1033
        qThreadInfo *pThreadInfo = infos + i;
×
1034
        closeQueryConn(pThreadInfo, iface);
×
1035
    }
1036

1037
    // free threads
1038
    tmfree(pids);
×
1039
    tmfree(infos);
×
1040

1041
    // free sqls
1042
    freeSpecialQueryInfo();
×
1043

1044
    // free delays
1045
    if (pDelays) {
×
1046
        benchArrayDestroy(pDelays);
×
1047
    }
1048

1049
    return ret;
×
1050
}
1051

1052
// total query for end 
1053
void totalQuery(int64_t spends) {
8✔
1054
    // total QPS
1055
    uint64_t totalQueried = g_queryInfo.specifiedQueryInfo.totalQueried
8✔
1056
        + g_queryInfo.superQueryInfo.totalQueried;
8✔
1057

1058
    // error rate
1059
    char errRate[128] = "";
8✔
1060
    if(g_arguments->continueIfFail == YES_IF_FAILED) {
8✔
1061
        uint64_t totalFail = g_queryInfo.specifiedQueryInfo.totalFail + g_queryInfo.superQueryInfo.totalFail;
2✔
1062
        if (totalQueried > 0) {
2✔
1063
            snprintf(errRate, sizeof(errRate), " ,error %" PRIu64 " (rate:%.3f%%)", totalFail, ((float)totalFail * 100)/totalQueried);
2✔
1064
        }
1065
    }
1066

1067
    // show
1068
    double  tInS = (double)spends / 1000;
8✔
1069
    char buf[512] = "";
8✔
1070
    snprintf(buf, sizeof(buf),
8✔
1071
                "Spend %.4f second completed total queries: %" PRIu64
1072
                ", the QPS of all threads: %10.3f%s\n\n",
1073
                tInS, totalQueried, (double)totalQueried / tInS, errRate);
8✔
1074
    infoPrint("%s", buf);
8✔
1075
    infoPrintToFile("%s", buf);
8✔
1076
}
8✔
1077

1078
int queryTestProcess() {
9✔
1079
    prompt(0);
9✔
1080

1081
    // covert addr
1082
    if (g_queryInfo.iface == REST_IFACE) {
9✔
1083
        encodeAuthBase64();
2✔
1084
        char *host = g_arguments->host          ? g_arguments->host : DEFAULT_HOST;
2✔
1085
        int   port = g_arguments->port_inputted ? g_arguments->port : DEFAULT_REST_PORT;
2✔
1086
        if (convertHostToServAddr(host,
2✔
1087
                    port,
1088
                    &(g_arguments->serv_addr)) != 0) {
2✔
1089
            errorPrint("%s", "convert host to server address\n");
×
1090
            return -1;
×
1091
        }
1092
    }    
1093

1094
    // kill sql for executing seconds over "kill_slow_query_threshold"
1095
    if (g_queryInfo.iface == TAOSC_IFACE && g_queryInfo.killQueryThreshold) {
9✔
1096
        int32_t ret = killSlowQuery();
×
1097
        if (ret != 0) {
×
1098
            return ret;
×
1099
        }
1100
    }
1101

1102
    // fetch child name if super table
1103
    if ((g_queryInfo.superQueryInfo.sqlCount > 0) &&
9✔
1104
            (g_queryInfo.superQueryInfo.threadCnt > 0)) {
3✔
1105
        int32_t ret = fetchChildTableName(g_queryInfo.dbName, g_queryInfo.superQueryInfo.stbName);
3✔
1106
        if (ret != 0) {
3✔
1107
            errorPrint("fetchChildTableName dbName=%s stb=%s failed.", g_queryInfo.dbName, g_queryInfo.superQueryInfo.stbName);
×
1108
            return -1;
×
1109
        }
1110
    }
1111

1112
    // 
1113
    // start running
1114
    //
1115

1116
    uint64_t startTs = toolsGetTimestampMs();
9✔
1117
    if(g_queryInfo.specifiedQueryInfo.sqls && g_queryInfo.specifiedQueryInfo.sqls->size > 0) {
9✔
1118
        // specified table
1119
        if (g_queryInfo.specifiedQueryInfo.mixed_query) {
6✔
1120
            // mixed
1121
            if(g_queryInfo.specifiedQueryInfo.batchQuery) {
2✔
1122
                if (specQueryBatch(g_queryInfo.iface, g_queryInfo.dbName)) {
×
1123
                    return -1;
×
1124
                }    
1125
            } else {
1126
                if (specQueryMix(g_queryInfo.iface, g_queryInfo.dbName)) {
2✔
1127
                    return -1;
×
1128
                }    
1129
            }
1130
        } else {
1131
            // no mixied
1132
            if (specQuery(g_queryInfo.iface, g_queryInfo.dbName)) {
4✔
1133
                return -1;
1✔
1134
            }
1135
        }
1136
    } else if(g_queryInfo.superQueryInfo.sqlCount > 0) {
3✔
1137
        // super table
1138
        if (stbQuery(g_queryInfo.iface, g_queryInfo.dbName)) {
3✔
1139
            return -1;
×
1140
        }
1141
    } else {
1142
        // nothing
1143
        errorPrint("%s\n", "Both 'specified_table_query' and 'super_table_query' sqls is empty.");
×
1144
        return -1;
×
1145
    }
1146

1147
    // total 
1148
    totalQuery(toolsGetTimestampMs() - startTs); 
8✔
1149
    return g_fail ? -1 : 0;
8✔
1150
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc