modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/lsm1/lsm-test/lsmtest2.c (about) 1 2 /* 3 ** This file contains tests related to recovery following application 4 ** and system crashes (power failures) while writing to the database. 5 */ 6 7 #include "lsmtest.h" 8 9 /* 10 ** Structure used by testCksumDatabase() to accumulate checksum values in. 11 */ 12 typedef struct Cksum Cksum; 13 struct Cksum { 14 int nRow; 15 int cksum1; 16 int cksum2; 17 }; 18 19 /* 20 ** tdb_scan() callback used by testCksumDatabase() 21 */ 22 static void scanCksumDb( 23 void *pCtx, 24 void *pKey, int nKey, 25 void *pVal, int nVal 26 ){ 27 Cksum *p = (Cksum *)pCtx; 28 int i; 29 30 p->nRow++; 31 for(i=0; i<nKey; i++){ 32 p->cksum1 += ((u8 *)pKey)[i]; 33 p->cksum2 += p->cksum1; 34 } 35 for(i=0; i<nVal; i++){ 36 p->cksum1 += ((u8 *)pVal)[i]; 37 p->cksum2 += p->cksum1; 38 } 39 } 40 41 /* 42 ** tdb_scan() callback used by testCountDatabase() 43 */ 44 static void scanCountDb( 45 void *pCtx, 46 void *pKey, int nKey, 47 void *pVal, int nVal 48 ){ 49 Cksum *p = (Cksum *)pCtx; 50 p->nRow++; 51 52 unused_parameter(pKey); 53 unused_parameter(nKey); 54 unused_parameter(pVal); 55 unused_parameter(nVal); 56 } 57 58 59 /* 60 ** Iterate through the entire contents of database pDb. Write a checksum 61 ** string based on the db contents into buffer zOut before returning. A 62 ** checksum string is at most 29 (TEST_CKSUM_BYTES) bytes in size: 63 ** 64 ** * 32-bit integer (10 bytes) 65 ** * 1 space (1 byte) 66 ** * 32-bit hex (8 bytes) 67 ** * 1 space (1 byte) 68 ** * 32-bit hex (8 bytes) 69 ** * nul-terminator (1 byte) 70 ** 71 ** The number of entries in the database is returned. 72 */ 73 int testCksumDatabase( 74 TestDb *pDb, /* Database handle */ 75 char *zOut /* Buffer to write checksum to */ 76 ){ 77 Cksum cksum; 78 memset(&cksum, 0, sizeof(Cksum)); 79 tdb_scan(pDb, (void *)&cksum, 0, 0, 0, 0, 0, scanCksumDb); 80 sprintf(zOut, "%d %x %x", 81 cksum.nRow, (u32)cksum.cksum1, (u32)cksum.cksum2 82 ); 83 assert( strlen(zOut)<TEST_CKSUM_BYTES ); 84 return cksum.nRow; 85 } 86 87 int testCountDatabase(TestDb *pDb){ 88 Cksum cksum; 89 memset(&cksum, 0, sizeof(Cksum)); 90 tdb_scan(pDb, (void *)&cksum, 0, 0, 0, 0, 0, scanCountDb); 91 return cksum.nRow; 92 } 93 94 /* 95 ** This function is a no-op if *pRc is not 0 when it is called. 96 ** 97 ** Otherwise, the two nul-terminated strings z1 and z1 are compared. If 98 ** they are the same, the function returns without doing anything. Otherwise, 99 ** an error message is printed, *pRc is set to 1 and the test_failed() 100 ** function called. 101 */ 102 void testCompareStr(const char *z1, const char *z2, int *pRc){ 103 if( *pRc==0 ){ 104 if( strcmp(z1, z2) ){ 105 testPrintError("testCompareStr: \"%s\" != \"%s\"\n", z1, z2); 106 *pRc = 1; 107 test_failed(); 108 } 109 } 110 } 111 112 /* 113 ** This function is a no-op if *pRc is not 0 when it is called. 114 ** 115 ** Otherwise, the two integers i1 and i2 are compared. If they are equal, 116 ** the function returns without doing anything. Otherwise, an error message 117 ** is printed, *pRc is set to 1 and the test_failed() function called. 118 */ 119 void testCompareInt(int i1, int i2, int *pRc){ 120 if( *pRc==0 && i1!=i2 ){ 121 testPrintError("testCompareInt: %d != %d\n", i1, i2); 122 *pRc = 1; 123 test_failed(); 124 } 125 } 126 127 void testCaseStart(int *pRc, char *zFmt, ...){ 128 va_list ap; 129 va_start(ap, zFmt); 130 vprintf(zFmt, ap); 131 printf(" ..."); 132 va_end(ap); 133 *pRc = 0; 134 fflush(stdout); 135 } 136 137 /* 138 ** This function is a no-op if *pRc is non-zero when it is called. Zero 139 ** is returned in this case. 140 ** 141 ** Otherwise, the zFmt (a printf style format string) and following arguments 142 ** are used to create a test case name. If zPattern is NULL or a glob pattern 143 ** that matches the test case name, 1 is returned and the test case started. 144 ** Otherwise, zero is returned and the test case does not start. 145 */ 146 int testCaseBegin(int *pRc, const char *zPattern, const char *zFmt, ...){ 147 int res = 0; 148 if( *pRc==0 ){ 149 char *zTest; 150 va_list ap; 151 152 va_start(ap, zFmt); 153 zTest = testMallocVPrintf(zFmt, ap); 154 va_end(ap); 155 if( zPattern==0 || testGlobMatch(zPattern, zTest) ){ 156 printf("%-50s ...", zTest); 157 res = 1; 158 } 159 testFree(zTest); 160 fflush(stdout); 161 } 162 163 return res; 164 } 165 166 void testCaseFinish(int rc){ 167 if( rc==0 ){ 168 printf("Ok\n"); 169 }else{ 170 printf("FAILED\n"); 171 } 172 fflush(stdout); 173 } 174 175 void testCaseSkip(){ 176 printf("Skipped\n"); 177 } 178 179 void testSetupSavedLsmdb( 180 const char *zCfg, 181 const char *zFile, 182 Datasource *pData, 183 int nRow, 184 int *pRc 185 ){ 186 if( *pRc==0 ){ 187 int rc; 188 TestDb *pDb; 189 rc = tdb_lsm_open(zCfg, zFile, 1, &pDb); 190 if( rc==0 ){ 191 testWriteDatasourceRange(pDb, pData, 0, nRow, &rc); 192 testClose(&pDb); 193 if( rc==0 ) testSaveDb(zFile, "log"); 194 } 195 *pRc = rc; 196 } 197 } 198 199 /* 200 ** This function is a no-op if *pRc is non-zero when it is called. 201 ** 202 ** Open the LSM database identified by zFile and compute its checksum 203 ** (a string, as returned by testCksumDatabase()). If the checksum is 204 ** identical to zExpect1 or, if it is not NULL, zExpect2, the test passes. 205 ** Otherwise, print an error message and set *pRc to 1. 206 */ 207 static void testCompareCksumLsmdb( 208 const char *zFile, /* Path to LSM database */ 209 int bCompress, /* True if db is compressed */ 210 const char *zExpect1, /* Expected checksum 1 */ 211 const char *zExpect2, /* Expected checksum 2 (or NULL) */ 212 int *pRc /* IN/OUT: Test case error code */ 213 ){ 214 if( *pRc==0 ){ 215 char zCksum[TEST_CKSUM_BYTES]; 216 TestDb *pDb; 217 218 *pRc = tdb_lsm_open((bCompress?"compression=1 mmap=0":""), zFile, 0, &pDb); 219 testCksumDatabase(pDb, zCksum); 220 testClose(&pDb); 221 222 if( *pRc==0 ){ 223 int r1 = 0; 224 int r2 = -1; 225 226 r1 = strcmp(zCksum, zExpect1); 227 if( zExpect2 ) r2 = strcmp(zCksum, zExpect2); 228 if( r1 && r2 ){ 229 if( zExpect2 ){ 230 testPrintError("testCompareCksumLsmdb: \"%s\" != (\"%s\" OR \"%s\")", 231 zCksum, zExpect1, zExpect2 232 ); 233 }else{ 234 testPrintError("testCompareCksumLsmdb: \"%s\" != \"%s\"", 235 zCksum, zExpect1 236 ); 237 } 238 *pRc = 1; 239 test_failed(); 240 } 241 } 242 } 243 } 244 245 #if 0 /* not used */ 246 static void testCompareCksumBtdb( 247 const char *zFile, /* Path to LSM database */ 248 const char *zExpect1, /* Expected checksum 1 */ 249 const char *zExpect2, /* Expected checksum 2 (or NULL) */ 250 int *pRc /* IN/OUT: Test case error code */ 251 ){ 252 if( *pRc==0 ){ 253 char zCksum[TEST_CKSUM_BYTES]; 254 TestDb *pDb; 255 256 *pRc = tdb_open("bt", zFile, 0, &pDb); 257 testCksumDatabase(pDb, zCksum); 258 testClose(&pDb); 259 260 if( *pRc==0 ){ 261 int r1 = 0; 262 int r2 = -1; 263 264 r1 = strcmp(zCksum, zExpect1); 265 if( zExpect2 ) r2 = strcmp(zCksum, zExpect2); 266 if( r1 && r2 ){ 267 if( zExpect2 ){ 268 testPrintError("testCompareCksumLsmdb: \"%s\" != (\"%s\" OR \"%s\")", 269 zCksum, zExpect1, zExpect2 270 ); 271 }else{ 272 testPrintError("testCompareCksumLsmdb: \"%s\" != \"%s\"", 273 zCksum, zExpect1 274 ); 275 } 276 *pRc = 1; 277 test_failed(); 278 } 279 } 280 } 281 } 282 #endif /* not used */ 283 284 /* Above this point are reusable test routines. Not clear that they 285 ** should really be in this file. 286 *************************************************************************/ 287 288 /* 289 ** This test verifies that if a system crash occurs while doing merge work 290 ** on the db, no data is lost. 291 */ 292 static void crash_test1(int bCompress, int *pRc){ 293 const char *DBNAME = "testdb.lsm"; 294 const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 200, 200}; 295 296 const int nRow = 5000; /* Database size */ 297 const int nIter = 200; /* Number of test iterations */ 298 const int nWork = 20; /* Maximum lsm_work() calls per iteration */ 299 const int nPage = 15; /* Pages per lsm_work call */ 300 301 int i; 302 int iDot = 0; 303 Datasource *pData; 304 CksumDb *pCksumDb; 305 TestDb *pDb; 306 char *zCfg; 307 308 const char *azConfig[2] = { 309 "page_size=1024 block_size=65536 autoflush=16384 safety=2 mmap=0", 310 "page_size=1024 block_size=65536 autoflush=16384 safety=2 " 311 " compression=1 mmap=0" 312 }; 313 assert( bCompress==0 || bCompress==1 ); 314 315 /* Allocate datasource. And calculate the expected checksums. */ 316 pData = testDatasourceNew(&defn); 317 pCksumDb = testCksumArrayNew(pData, nRow, nRow, 1); 318 319 /* Setup and save the initial database. */ 320 321 zCfg = testMallocPrintf("%s automerge=7", azConfig[bCompress]); 322 testSetupSavedLsmdb(zCfg, DBNAME, pData, 5000, pRc); 323 testFree(zCfg); 324 325 for(i=0; i<nIter && *pRc==0; i++){ 326 int iWork; 327 int testrc = 0; 328 329 testCaseProgress(i, nIter, testCaseNDot(), &iDot); 330 331 /* Restore and open the database. */ 332 testRestoreDb(DBNAME, "log"); 333 testrc = tdb_lsm_open(azConfig[bCompress], DBNAME, 0, &pDb); 334 assert( testrc==0 ); 335 336 /* Call lsm_work() on the db */ 337 tdb_lsm_prepare_sync_crash(pDb, 1 + (i%(nWork*2))); 338 for(iWork=0; testrc==0 && iWork<nWork; iWork++){ 339 int nWrite = 0; 340 lsm_db *db = tdb_lsm(pDb); 341 testrc = lsm_work(db, 0, nPage, &nWrite); 342 /* assert( testrc!=0 || nWrite>0 ); */ 343 if( testrc==0 ) testrc = lsm_checkpoint(db, 0); 344 } 345 tdb_close(pDb); 346 347 /* Check that the database content is still correct */ 348 testCompareCksumLsmdb(DBNAME, 349 bCompress, testCksumArrayGet(pCksumDb, nRow), 0, pRc); 350 } 351 352 testCksumArrayFree(pCksumDb); 353 testDatasourceFree(pData); 354 } 355 356 /* 357 ** This test verifies that if a system crash occurs while committing a 358 ** transaction to the log file, no earlier transactions are lost or damaged. 359 */ 360 static void crash_test2(int bCompress, int *pRc){ 361 const char *DBNAME = "testdb.lsm"; 362 const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 1000, 1000}; 363 364 const int nIter = 200; 365 const int nInsert = 20; 366 367 int i; 368 int iDot = 0; 369 Datasource *pData; 370 CksumDb *pCksumDb; 371 TestDb *pDb; 372 373 /* Allocate datasource. And calculate the expected checksums. */ 374 pData = testDatasourceNew(&defn); 375 pCksumDb = testCksumArrayNew(pData, 100, 100+nInsert, 1); 376 377 /* Setup and save the initial database. */ 378 testSetupSavedLsmdb("", DBNAME, pData, 100, pRc); 379 380 for(i=0; i<nIter && *pRc==0; i++){ 381 int iIns; 382 int testrc = 0; 383 384 testCaseProgress(i, nIter, testCaseNDot(), &iDot); 385 386 /* Restore and open the database. */ 387 testRestoreDb(DBNAME, "log"); 388 testrc = tdb_lsm_open("safety=2", DBNAME, 0, &pDb); 389 assert( testrc==0 ); 390 391 /* Insert nInsert records into the database. Crash midway through. */ 392 tdb_lsm_prepare_sync_crash(pDb, 1 + (i%(nInsert+2))); 393 for(iIns=0; iIns<nInsert; iIns++){ 394 void *pKey; int nKey; 395 void *pVal; int nVal; 396 397 testDatasourceEntry(pData, 100+iIns, &pKey, &nKey, &pVal, &nVal); 398 testrc = tdb_write(pDb, pKey, nKey, pVal, nVal); 399 if( testrc ) break; 400 } 401 tdb_close(pDb); 402 403 /* Check that no data was lost when the system crashed. */ 404 testCompareCksumLsmdb(DBNAME, bCompress, 405 testCksumArrayGet(pCksumDb, 100 + iIns), 406 testCksumArrayGet(pCksumDb, 100 + iIns + 1), 407 pRc 408 ); 409 } 410 411 testDatasourceFree(pData); 412 testCksumArrayFree(pCksumDb); 413 } 414 415 416 /* 417 ** This test verifies that if a system crash occurs when checkpointing 418 ** the database, data is not lost (assuming that any writes not synced 419 ** to the db have been synced into the log file). 420 */ 421 static void crash_test3(int bCompress, int *pRc){ 422 const char *DBNAME = "testdb.lsm"; 423 const int nIter = 100; 424 const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 1000, 1000}; 425 426 int i; 427 int iDot = 0; 428 Datasource *pData; 429 CksumDb *pCksumDb; 430 TestDb *pDb; 431 432 /* Allocate datasource. And calculate the expected checksums. */ 433 pData = testDatasourceNew(&defn); 434 pCksumDb = testCksumArrayNew(pData, 110, 150, 10); 435 436 /* Setup and save the initial database. */ 437 testSetupSavedLsmdb("", DBNAME, pData, 100, pRc); 438 439 for(i=0; i<nIter && *pRc==0; i++){ 440 int iOpen; 441 testCaseProgress(i, nIter, testCaseNDot(), &iDot); 442 testRestoreDb(DBNAME, "log"); 443 444 for(iOpen=0; iOpen<5; iOpen++){ 445 /* Open the database. Insert 10 more records. */ 446 pDb = testOpen("lsm", 0, pRc); 447 testWriteDatasourceRange(pDb, pData, 100+iOpen*10, 10, pRc); 448 449 /* Schedule a crash simulation then close the db. */ 450 tdb_lsm_prepare_sync_crash(pDb, 1 + (i%2)); 451 tdb_close(pDb); 452 453 /* Open the database and check that the crash did not cause any 454 ** data loss. */ 455 testCompareCksumLsmdb(DBNAME, bCompress, 456 testCksumArrayGet(pCksumDb, 110 + iOpen*10), 0, 457 pRc 458 ); 459 } 460 } 461 462 testDatasourceFree(pData); 463 testCksumArrayFree(pCksumDb); 464 } 465 466 void do_crash_test(const char *zPattern, int *pRc){ 467 struct Test { 468 const char *zTest; 469 void (*x)(int, int *); 470 int bCompress; 471 } aTest [] = { 472 { "crash.lsm.1", crash_test1, 0 }, 473 #ifdef HAVE_ZLIB 474 { "crash.lsm_zip.1", crash_test1, 1 }, 475 #endif 476 { "crash.lsm.2", crash_test2, 0 }, 477 { "crash.lsm.3", crash_test3, 0 }, 478 }; 479 int i; 480 481 for(i=0; *pRc==LSM_OK && i<ArraySize(aTest); i++){ 482 struct Test *p = &aTest[i]; 483 if( testCaseBegin(pRc, zPattern, "%s", p->zTest) ){ 484 p->x(p->bCompress, pRc); 485 testCaseFinish(*pRc); 486 } 487 } 488 }