modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/lsm1/lsm-test/lsmtest5.c (about) 1 2 /* 3 ** This file is broken into three semi-autonomous parts: 4 ** 5 ** 1. The database functions. 6 ** 2. The thread wrappers. 7 ** 3. The implementation of the mt1.* tests. 8 */ 9 10 /************************************************************************* 11 ** DATABASE CONTENTS: 12 ** 13 ** The database contains up to N key/value pairs, where N is some large 14 ** number (say 10,000,000). Keys are integer values between 0 and (N-1). 15 ** The value associated with each key is a pseudo-random blob of data. 16 ** 17 ** Key/value pair keys are encoded as the two bytes "k." followed by a 18 ** 10-digit decimal number. i.e. key 45 -> "k.0000000045". 19 ** 20 ** As well as the key/value pairs, the database also contains checksum 21 ** entries. The checksums form a hierarchy - for every F key/value 22 ** entries there is one level 1 checksum. And for each F level 1 checksums 23 ** there is one level 2 checksum. And so on. 24 ** 25 ** Checksum keys are encoded as the two byte "c." followed by the 26 ** checksum level, followed by a 10 digit decimal number containing 27 ** the value of the first key that contributes to the checksum value. 28 ** For example, assuming F==10, the level 1 checksum that spans keys 29 ** 10 to 19 is "c.1.0000000010". 30 ** 31 ** Clients may perform one of two operations on the database: a read 32 ** or a write. 33 ** 34 ** READ OPERATIONS: 35 ** 36 ** A read operation scans a range of F key/value pairs. It computes 37 ** the expected checksum and then compares the computed value to the 38 ** actual value stored in the level 1 checksum entry. It then scans 39 ** the group of F level 1 checksums, and compares the computed checksum 40 ** to the associated level 2 checksum value, and so on until the 41 ** highest level checksum value has been verified. 42 ** 43 ** If a checksum ever fails to match the expected value, the test 44 ** has failed. 45 ** 46 ** WRITE OPERATIONS: 47 ** 48 ** A write operation involves writing (possibly clobbering) a single 49 ** key/value pair. The associated level 1 checksum is then recalculated 50 ** updated. Then the level 2 checksum, and so on until the highest 51 ** level checksum has been modified. 52 ** 53 ** All updates occur inside a single transaction. 54 ** 55 ** INTERFACE: 56 ** 57 ** The interface used by test cases to read and write the db consists 58 ** of type DbParameters and the following functions: 59 ** 60 ** dbReadOperation() 61 ** dbWriteOperation() 62 */ 63 64 #include "lsmtest.h" 65 66 typedef struct DbParameters DbParameters; 67 struct DbParameters { 68 int nFanout; /* Checksum fanout (F) */ 69 int nKey; /* Size of key space (N) */ 70 }; 71 72 #define DB_KEY_BYTES (2+5+10+1) 73 74 /* 75 ** Argument aBuf[] must point to a buffer at least DB_KEY_BYTES in size. 76 ** This function populates the buffer with a nul-terminated key string 77 ** corresponding to key iKey. 78 */ 79 static void dbFormatKey( 80 DbParameters *pParam, 81 int iLevel, 82 int iKey, /* Key value */ 83 char *aBuf /* Write key string here */ 84 ){ 85 if( iLevel==0 ){ 86 snprintf(aBuf, DB_KEY_BYTES, "k.%.10d", iKey); 87 }else{ 88 int f = 1; 89 int i; 90 for(i=0; i<iLevel; i++) f = f * pParam->nFanout; 91 snprintf(aBuf, DB_KEY_BYTES, "c.%d.%.10d", iLevel, f*(iKey/f)); 92 } 93 } 94 95 /* 96 ** Argument aBuf[] must point to a buffer at least DB_KEY_BYTES in size. 97 ** This function populates the buffer with the string representation of 98 ** checksum value iVal. 99 */ 100 static void dbFormatCksumValue(u32 iVal, char *aBuf){ 101 snprintf(aBuf, DB_KEY_BYTES, "%.10u", iVal); 102 } 103 104 /* 105 ** Return the highest level of checksum in the database described 106 ** by *pParam. 107 */ 108 static int dbMaxLevel(DbParameters *pParam){ 109 int iMax; 110 int n = 1; 111 for(iMax=0; n<pParam->nKey; iMax++){ 112 n = n * pParam->nFanout; 113 } 114 return iMax; 115 } 116 117 static void dbCksum( 118 void *pCtx, /* IN/OUT: Pointer to u32 containing cksum */ 119 void *pKey, int nKey, /* Database key. Unused. */ 120 void *pVal, int nVal /* Database value. Checksum this. */ 121 ){ 122 u8 *aVal = (u8 *)pVal; 123 u32 *pCksum = (u32 *)pCtx; 124 u32 cksum = *pCksum; 125 int i; 126 127 unused_parameter(pKey); 128 unused_parameter(nKey); 129 130 for(i=0; i<nVal; i++){ 131 cksum += (cksum<<3) + (int)aVal[i]; 132 } 133 134 *pCksum = cksum; 135 } 136 137 /* 138 ** Compute the value of the checksum stored on level iLevel that contains 139 ** data from key iKey by scanning the pParam->nFanout entries at level 140 ** iLevel-1. 141 */ 142 static u32 dbComputeCksum( 143 DbParameters *pParam, /* Database parameters */ 144 TestDb *pDb, /* Database connection handle */ 145 int iLevel, /* Level of checksum to compute */ 146 int iKey, /* Compute checksum for this key */ 147 int *pRc /* IN/OUT: Error code */ 148 ){ 149 u32 cksum = 0; 150 if( *pRc==0 ){ 151 int nFirst; 152 int nLast; 153 int iFirst = 0; 154 int iLast = 0; 155 int i; 156 int f = 1; 157 char zFirst[DB_KEY_BYTES]; 158 char zLast[DB_KEY_BYTES]; 159 160 assert( iLevel>=1 ); 161 for(i=0; i<iLevel; i++) f = f * pParam->nFanout; 162 163 iFirst = f*(iKey/f); 164 iLast = iFirst + f - 1; 165 dbFormatKey(pParam, iLevel-1, iFirst, zFirst); 166 dbFormatKey(pParam, iLevel-1, iLast, zLast); 167 nFirst = strlen(zFirst); 168 nLast = strlen(zLast); 169 170 *pRc = tdb_scan(pDb, (u32*)&cksum, 0, zFirst, nFirst, zLast, nLast,dbCksum); 171 } 172 173 return cksum; 174 } 175 176 static void dbReadOperation( 177 DbParameters *pParam, /* Database parameters */ 178 TestDb *pDb, /* Database connection handle */ 179 void (*xDelay)(void *), 180 void *pDelayCtx, 181 int iKey, /* Key to read */ 182 int *pRc /* IN/OUT: Error code */ 183 ){ 184 const int iMax = dbMaxLevel(pParam); 185 int i; 186 187 if( tdb_transaction_support(pDb) ) testBegin(pDb, 1, pRc); 188 for(i=1; *pRc==0 && i<=iMax; i++){ 189 char zCksum[DB_KEY_BYTES]; 190 char zKey[DB_KEY_BYTES]; 191 u32 iCksum = 0; 192 193 iCksum = dbComputeCksum(pParam, pDb, i, iKey, pRc); 194 if( iCksum ){ 195 if( xDelay && i==1 ) xDelay(pDelayCtx); 196 dbFormatCksumValue(iCksum, zCksum); 197 dbFormatKey(pParam, i, iKey, zKey); 198 testFetchStr(pDb, zKey, zCksum, pRc); 199 } 200 } 201 if( tdb_transaction_support(pDb) ) testCommit(pDb, 0, pRc); 202 } 203 204 static int dbWriteOperation( 205 DbParameters *pParam, /* Database parameters */ 206 TestDb *pDb, /* Database connection handle */ 207 int iKey, /* Key to write to */ 208 const char *zValue, /* Nul-terminated value to write */ 209 int *pRc /* IN/OUT: Error code */ 210 ){ 211 const int iMax = dbMaxLevel(pParam); 212 char zKey[DB_KEY_BYTES]; 213 int i; 214 int rc; 215 216 assert( iKey>=0 && iKey<pParam->nKey ); 217 dbFormatKey(pParam, 0, iKey, zKey); 218 219 /* Open a write transaction. This may fail - SQLITE4_BUSY */ 220 if( *pRc==0 && tdb_transaction_support(pDb) ){ 221 rc = tdb_begin(pDb, 2); 222 if( rc==5 ) return 0; 223 *pRc = rc; 224 } 225 226 testWriteStr(pDb, zKey, zValue, pRc); 227 for(i=1; i<=iMax; i++){ 228 char zCksum[DB_KEY_BYTES]; 229 u32 iCksum = 0; 230 231 iCksum = dbComputeCksum(pParam, pDb, i, iKey, pRc); 232 dbFormatCksumValue(iCksum, zCksum); 233 dbFormatKey(pParam, i, iKey, zKey); 234 testWriteStr(pDb, zKey, zCksum, pRc); 235 } 236 if( tdb_transaction_support(pDb) ) testCommit(pDb, 0, pRc); 237 return 1; 238 } 239 240 /************************************************************************* 241 ** The following block contains testXXX() functions that implement a 242 ** wrapper around the systems native multi-thread support. There are no 243 ** synchronization primitives - just functions to launch and join 244 ** threads. Wrapper functions are: 245 ** 246 ** testThreadSupport() 247 ** 248 ** testThreadInit() 249 ** testThreadShutdown() 250 ** testThreadLaunch() 251 ** testThreadWait() 252 ** 253 ** testThreadSetHalt() 254 ** testThreadGetHalt() 255 ** testThreadSetResult() 256 ** testThreadGetResult() 257 ** 258 ** testThreadEnterMutex() 259 ** testThreadLeaveMutex() 260 */ 261 typedef struct ThreadSet ThreadSet; 262 #ifdef LSM_MUTEX_PTHREADS 263 264 #include <pthread.h> 265 #include <unistd.h> 266 267 typedef struct Thread Thread; 268 struct Thread { 269 int rc; 270 char *zMsg; 271 pthread_t id; 272 void (*xMain)(ThreadSet *, int, void *); 273 void *pCtx; 274 ThreadSet *pThreadSet; 275 }; 276 277 struct ThreadSet { 278 int bHalt; /* Halt flag */ 279 int nThread; /* Number of threads */ 280 Thread *aThread; /* Array of Thread structures */ 281 pthread_mutex_t mutex; /* Mutex used for cheating */ 282 }; 283 284 /* 285 ** Return true if this build supports threads, or false otherwise. If 286 ** this function returns false, no other testThreadXXX() functions should 287 ** be called. 288 */ 289 static int testThreadSupport(){ return 1; } 290 291 /* 292 ** Allocate and return a thread-set handle with enough space allocated 293 ** to handle up to nMax threads. Each call to this function should be 294 ** matched by a call to testThreadShutdown() to delete the object. 295 */ 296 static ThreadSet *testThreadInit(int nMax){ 297 int nByte; /* Total space to allocate */ 298 ThreadSet *p; /* Return value */ 299 300 nByte = sizeof(ThreadSet) + sizeof(struct Thread) * nMax; 301 p = (ThreadSet *)testMalloc(nByte); 302 p->nThread = nMax; 303 p->aThread = (Thread *)&p[1]; 304 pthread_mutex_init(&p->mutex, 0); 305 306 return p; 307 } 308 309 /* 310 ** Delete a thread-set object and release all resources held by it. 311 */ 312 static void testThreadShutdown(ThreadSet *p){ 313 int i; 314 for(i=0; i<p->nThread; i++){ 315 testFree(p->aThread[i].zMsg); 316 } 317 pthread_mutex_destroy(&p->mutex); 318 testFree(p); 319 } 320 321 static void *ttMain(void *pArg){ 322 Thread *pThread = (Thread *)pArg; 323 int iThread; 324 iThread = (pThread - pThread->pThreadSet->aThread); 325 pThread->xMain(pThread->pThreadSet, iThread, pThread->pCtx); 326 return 0; 327 } 328 329 /* 330 ** Launch a new thread. 331 */ 332 static int testThreadLaunch( 333 ThreadSet *p, 334 int iThread, 335 void (*xMain)(ThreadSet *, int, void *), 336 void *pCtx 337 ){ 338 int rc; 339 Thread *pThread; 340 341 assert( iThread>=0 && iThread<p->nThread ); 342 343 pThread = &p->aThread[iThread]; 344 assert( pThread->pThreadSet==0 ); 345 pThread->xMain = xMain; 346 pThread->pCtx = pCtx; 347 pThread->pThreadSet = p; 348 rc = pthread_create(&pThread->id, 0, ttMain, (void *)pThread); 349 350 return rc; 351 } 352 353 /* 354 ** Set the thread-set "halt" flag. 355 */ 356 static void testThreadSetHalt(ThreadSet *pThreadSet){ 357 pThreadSet->bHalt = 1; 358 } 359 360 /* 361 ** Return the current value of the thread-set "halt" flag. 362 */ 363 static int testThreadGetHalt(ThreadSet *pThreadSet){ 364 return pThreadSet->bHalt; 365 } 366 367 static void testThreadSleep(ThreadSet *pThreadSet, int nMs){ 368 int nRem = nMs; 369 while( nRem>0 && testThreadGetHalt(pThreadSet)==0 ){ 370 usleep(50000); 371 nRem -= 50; 372 } 373 } 374 375 /* 376 ** Wait for all threads launched to finish before returning. If nMs 377 ** is greater than zero, set the "halt" flag to tell all threads 378 ** to halt after waiting nMs milliseconds. 379 */ 380 static void testThreadWait(ThreadSet *pThreadSet, int nMs){ 381 int i; 382 383 testThreadSleep(pThreadSet, nMs); 384 testThreadSetHalt(pThreadSet); 385 for(i=0; i<pThreadSet->nThread; i++){ 386 Thread *pThread = &pThreadSet->aThread[i]; 387 if( pThread->xMain ){ 388 pthread_join(pThread->id, 0); 389 } 390 } 391 } 392 393 /* 394 ** Set the result for thread iThread. 395 */ 396 static void testThreadSetResult( 397 ThreadSet *pThreadSet, /* Thread-set handle */ 398 int iThread, /* Set result for this thread */ 399 int rc, /* Result error code */ 400 char *zFmt, /* Result string format */ 401 ... /* Result string formatting args... */ 402 ){ 403 va_list ap; 404 405 testFree(pThreadSet->aThread[iThread].zMsg); 406 pThreadSet->aThread[iThread].rc = rc; 407 pThreadSet->aThread[iThread].zMsg = 0; 408 if( zFmt ){ 409 va_start(ap, zFmt); 410 pThreadSet->aThread[iThread].zMsg = testMallocVPrintf(zFmt, ap); 411 va_end(ap); 412 } 413 } 414 415 /* 416 ** Retrieve the result for thread iThread. 417 */ 418 static int testThreadGetResult( 419 ThreadSet *pThreadSet, /* Thread-set handle */ 420 int iThread, /* Get result for this thread */ 421 const char **pzRes /* OUT: Pointer to result string */ 422 ){ 423 if( pzRes ) *pzRes = pThreadSet->aThread[iThread].zMsg; 424 return pThreadSet->aThread[iThread].rc; 425 } 426 427 /* 428 ** Enter and leave the test case mutex. 429 */ 430 #if 0 431 static void testThreadEnterMutex(ThreadSet *p){ 432 pthread_mutex_lock(&p->mutex); 433 } 434 static void testThreadLeaveMutex(ThreadSet *p){ 435 pthread_mutex_unlock(&p->mutex); 436 } 437 #endif 438 #endif 439 440 #if !defined(LSM_MUTEX_PTHREADS) 441 static int testThreadSupport(){ return 0; } 442 443 #define testThreadInit(a) 0 444 #define testThreadShutdown(a) 445 #define testThreadLaunch(a,b,c,d) 0 446 #define testThreadWait(a,b) 447 #define testThreadSetHalt(a) 448 #define testThreadGetHalt(a) 0 449 #define testThreadGetResult(a,b,c) 0 450 #define testThreadSleep(a,b) 0 451 452 static void testThreadSetResult(ThreadSet *a, int b, int c, char *d, ...){ 453 unused_parameter(a); 454 unused_parameter(b); 455 unused_parameter(c); 456 unused_parameter(d); 457 } 458 #endif 459 /* End of threads wrapper. 460 *************************************************************************/ 461 462 /************************************************************************* 463 ** Below this point is the third part of this file - the implementation 464 ** of the mt1.* tests. 465 */ 466 typedef struct Mt1Test Mt1Test; 467 struct Mt1Test { 468 DbParameters param; /* Description of database to read/write */ 469 int nReadwrite; /* Number of read/write threads */ 470 int nFastReader; /* Number of fast reader threads */ 471 int nSlowReader; /* Number of slow reader threads */ 472 int nMs; /* How long to run for */ 473 const char *zSystem; /* Database system to test */ 474 }; 475 476 typedef struct Mt1DelayCtx Mt1DelayCtx; 477 struct Mt1DelayCtx { 478 ThreadSet *pSet; /* Threadset to sleep within */ 479 int nMs; /* Sleep in ms */ 480 }; 481 482 static void xMt1Delay(void *pCtx){ 483 Mt1DelayCtx *p = (Mt1DelayCtx *)pCtx; 484 testThreadSleep(p->pSet, p->nMs); 485 } 486 487 #define MT1_THREAD_RDWR 0 488 #define MT1_THREAD_SLOW 1 489 #define MT1_THREAD_FAST 2 490 491 static void xMt1Work(lsm_db *pDb, void *pCtx){ 492 #if 0 493 char *z = 0; 494 lsm_info(pDb, LSM_INFO_DB_STRUCTURE, &z); 495 printf("%s\n", z); 496 fflush(stdout); 497 #endif 498 } 499 500 /* 501 ** This is the main() proc for all threads in test case "mt1". 502 */ 503 static void mt1Main(ThreadSet *pThreadSet, int iThread, void *pCtx){ 504 Mt1Test *p = (Mt1Test *)pCtx; /* Test parameters */ 505 Mt1DelayCtx delay; 506 int nRead = 0; /* Number of calls to dbReadOperation() */ 507 int nWrite = 0; /* Number of completed database writes */ 508 int rc = 0; /* Error code */ 509 int iPrng; /* Prng argument variable */ 510 TestDb *pDb; /* Database handle */ 511 int eType; 512 513 delay.pSet = pThreadSet; 514 delay.nMs = 0; 515 if( iThread<p->nReadwrite ){ 516 eType = MT1_THREAD_RDWR; 517 }else if( iThread<(p->nReadwrite+p->nFastReader) ){ 518 eType = MT1_THREAD_FAST; 519 }else{ 520 eType = MT1_THREAD_SLOW; 521 delay.nMs = (p->nMs / 20); 522 } 523 524 /* Open a new database connection. Initialize the pseudo-random number 525 ** argument based on the thread number. */ 526 iPrng = testPrngValue(iThread); 527 pDb = testOpen(p->zSystem, 0, &rc); 528 529 if( rc==0 ){ 530 tdb_lsm_config_work_hook(pDb, xMt1Work, 0); 531 } 532 533 /* Loop until either an error occurs or some other thread sets the 534 ** halt flag. */ 535 while( rc==0 && testThreadGetHalt(pThreadSet)==0 ){ 536 int iKey; 537 538 /* Perform a read operation on an arbitrarily selected key. */ 539 iKey = (testPrngValue(iPrng++) % p->param.nKey); 540 dbReadOperation(&p->param, pDb, xMt1Delay, (void *)&delay, iKey, &rc); 541 if( rc ) continue; 542 nRead++; 543 544 /* Attempt to write an arbitrary key value pair (and update the associated 545 ** checksum entries). dbWriteOperation() returns 1 if the write is 546 ** successful, or 0 if it failed with an LSM_BUSY error. */ 547 if( eType==MT1_THREAD_RDWR ){ 548 char aValue[50]; 549 char aRnd[25]; 550 551 iKey = (testPrngValue(iPrng++) % p->param.nKey); 552 testPrngString(iPrng, aRnd, sizeof(aRnd)); 553 iPrng += sizeof(aRnd); 554 snprintf(aValue, sizeof(aValue), "%d.%s", iThread, aRnd); 555 nWrite += dbWriteOperation(&p->param, pDb, iKey, aValue, &rc); 556 } 557 } 558 testClose(&pDb); 559 560 /* If an error has occured, set the thread error code and the threadset 561 ** halt flag to tell the other test threads to halt. Otherwise, set the 562 ** thread error code to 0 and post a message with the number of read 563 ** and write operations completed. */ 564 if( rc ){ 565 testThreadSetResult(pThreadSet, iThread, rc, 0); 566 testThreadSetHalt(pThreadSet); 567 }else{ 568 testThreadSetResult(pThreadSet, iThread, 0, "r/w: %d/%d", nRead, nWrite); 569 } 570 } 571 572 static void do_test_mt1( 573 const char *zSystem, /* Database system name */ 574 const char *zPattern, /* Run test cases that match this pattern */ 575 int *pRc /* IN/OUT: Error code */ 576 ){ 577 Mt1Test aTest[] = { 578 /* param, nReadwrite, nFastReader, nSlowReader, nMs, zSystem */ 579 { {10, 1000}, 4, 0, 0, 10000, 0 }, 580 { {10, 1000}, 4, 4, 2, 100000, 0 }, 581 { {10, 100000}, 4, 0, 0, 10000, 0 }, 582 { {10, 100000}, 4, 4, 2, 100000, 0 }, 583 }; 584 int i; 585 586 for(i=0; *pRc==0 && i<ArraySize(aTest); i++){ 587 Mt1Test *p = &aTest[i]; 588 int bRun = testCaseBegin(pRc, zPattern, 589 "mt1.%s.db=%d,%d.ms=%d.rdwr=%d.fast=%d.slow=%d", 590 zSystem, p->param.nFanout, p->param.nKey, 591 p->nMs, p->nReadwrite, p->nFastReader, p->nSlowReader 592 ); 593 if( bRun ){ 594 TestDb *pDb; 595 ThreadSet *pSet; 596 int iThread; 597 int nThread; 598 599 p->zSystem = zSystem; 600 pDb = testOpen(zSystem, 1, pRc); 601 602 nThread = p->nReadwrite + p->nFastReader + p->nSlowReader; 603 pSet = testThreadInit(nThread); 604 for(iThread=0; *pRc==0 && iThread<nThread; iThread++){ 605 testThreadLaunch(pSet, iThread, mt1Main, (void *)p); 606 } 607 608 testThreadWait(pSet, p->nMs); 609 for(iThread=0; *pRc==0 && iThread<nThread; iThread++){ 610 *pRc = testThreadGetResult(pSet, iThread, 0); 611 } 612 testCaseFinish(*pRc); 613 614 for(iThread=0; *pRc==0 && iThread<nThread; iThread++){ 615 const char *zMsg = 0; 616 *pRc = testThreadGetResult(pSet, iThread, &zMsg); 617 printf(" Info: thread %d (%d): %s\n", iThread, *pRc, zMsg); 618 } 619 620 testThreadShutdown(pSet); 621 testClose(&pDb); 622 } 623 } 624 } 625 626 void test_mt( 627 const char *zSystem, /* Database system name */ 628 const char *zPattern, /* Run test cases that match this pattern */ 629 int *pRc /* IN/OUT: Error code */ 630 ){ 631 if( testThreadSupport()==0 ) return; 632 do_test_mt1(zSystem, zPattern, pRc); 633 }