diff options
Diffstat (limited to 'ext/lsm1/lsm-test/lsmtest8.c')
-rw-r--r-- | ext/lsm1/lsm-test/lsmtest8.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/ext/lsm1/lsm-test/lsmtest8.c b/ext/lsm1/lsm-test/lsmtest8.c new file mode 100644 index 0000000..7efa0df --- /dev/null +++ b/ext/lsm1/lsm-test/lsmtest8.c @@ -0,0 +1,324 @@ + +/* +** This file contains test cases to verify that "live-recovery" following +** a mid-transaction failure of a writer process. +*/ + + +/* +** This test file includes lsmInt.h to get access to the definition of the +** ShmHeader structure. This is required to cause strategic damage to the +** shared memory header as part of recovery testing. +*/ +#include "lsmInt.h" + +#include "lsmtest.h" + +typedef struct SetupStep SetupStep; +struct SetupStep { + int bFlush; /* Flush to disk and checkpoint */ + int iInsStart; /* First key-value from ds to insert */ + int nIns; /* Number of rows to insert */ + int iDelStart; /* First key from ds to delete */ + int nDel; /* Number of rows to delete */ +}; + +static void doSetupStep( + TestDb *pDb, + Datasource *pData, + const SetupStep *pStep, + int *pRc +){ + testWriteDatasourceRange(pDb, pData, pStep->iInsStart, pStep->nIns, pRc); + testDeleteDatasourceRange(pDb, pData, pStep->iDelStart, pStep->nDel, pRc); + if( *pRc==0 ){ + int nSave = -1; + int nBuf = 64; + lsm_db *db = tdb_lsm(pDb); + + lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave); + lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf); + lsm_begin(db, 1); + lsm_commit(db, 0); + lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave); + + *pRc = lsm_work(db, 0, 0, 0); + if( *pRc==0 ){ + *pRc = lsm_checkpoint(db, 0); + } + } +} + +static void doSetupStepArray( + TestDb *pDb, + Datasource *pData, + const SetupStep *aStep, + int nStep +){ + int i; + for(i=0; i<nStep; i++){ + int rc = 0; + doSetupStep(pDb, pData, &aStep[i], &rc); + assert( rc==0 ); + } +} + +static void setupDatabase1(TestDb *pDb, Datasource **ppData){ + const SetupStep aStep[] = { + { 0, 1, 2000, 0, 0 }, + { 1, 0, 0, 0, 0 }, + { 0, 10001, 1000, 0, 0 }, + }; + const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 100, 500}; + Datasource *pData; + + pData = testDatasourceNew(&defn); + doSetupStepArray(pDb, pData, aStep, ArraySize(aStep)); + if( ppData ){ + *ppData = pData; + }else{ + testDatasourceFree(pData); + } +} + +#include <stdio.h> +void testReadFile(const char *zFile, int iOff, void *pOut, int nByte, int *pRc){ + if( *pRc==0 ){ + FILE *fd; + fd = fopen(zFile, "rb"); + if( fd==0 ){ + *pRc = 1; + }else{ + if( 0!=fseek(fd, iOff, SEEK_SET) ){ + *pRc = 1; + }else{ + assert( nByte>=0 ); + if( (size_t)nByte!=fread(pOut, 1, nByte, fd) ){ + *pRc = 1; + } + } + fclose(fd); + } + } +} + +void testWriteFile( + const char *zFile, + int iOff, + void *pOut, + int nByte, + int *pRc +){ + if( *pRc==0 ){ + FILE *fd; + fd = fopen(zFile, "r+b"); + if( fd==0 ){ + *pRc = 1; + }else{ + if( 0!=fseek(fd, iOff, SEEK_SET) ){ + *pRc = 1; + }else{ + assert( nByte>=0 ); + if( (size_t)nByte!=fwrite(pOut, 1, nByte, fd) ){ + *pRc = 1; + } + } + fclose(fd); + } + } +} + +static ShmHeader *getShmHeader(const char *zDb){ + int rc = 0; + char *zShm = testMallocPrintf("%s-shm", zDb); + ShmHeader *pHdr; + + pHdr = testMalloc(sizeof(ShmHeader)); + testReadFile(zShm, 0, (void *)pHdr, sizeof(ShmHeader), &rc); + assert( rc==0 ); + + return pHdr; +} + +/* +** This function makes a copy of the three files associated with LSM +** database zDb (i.e. if zDb is "test.db", it makes copies of "test.db", +** "test.db-log" and "test.db-shm"). +** +** It then opens a new database connection to the copy with the xLock() call +** instrumented so that it appears that some other process already connected +** to the db (holding a shared lock on DMS2). This prevents recovery from +** running. Then: +** +** 1) Check that the checksum of the database is zCksum. +** 2) Write a few keys to the database. Then delete the same keys. +** 3) Check that the checksum is zCksum. +** 4) Flush the db to disk and run a checkpoint. +** 5) Check once more that the checksum is still zCksum. +*/ +static void doLiveRecovery(const char *zDb, const char *zCksum, int *pRc){ + if( *pRc==LSM_OK ){ + const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 20, 25, 100, 500}; + Datasource *pData; + const char *zCopy = "testcopy.lsm"; + char zCksum2[TEST_CKSUM_BYTES]; + TestDb *pDb = 0; + int rc; + + pData = testDatasourceNew(&defn); + + testCopyLsmdb(zDb, zCopy); + rc = tdb_lsm_open("test_no_recovery=1", zCopy, 0, &pDb); + if( rc==0 ){ + ShmHeader *pHdr; + lsm_db *db; + testCksumDatabase(pDb, zCksum2); + testCompareStr(zCksum, zCksum2, &rc); + + testWriteDatasourceRange(pDb, pData, 1, 10, &rc); + testDeleteDatasourceRange(pDb, pData, 1, 10, &rc); + + /* Test that the two tree-headers are now consistent. */ + pHdr = getShmHeader(zCopy); + if( rc==0 && memcmp(&pHdr->hdr1, &pHdr->hdr2, sizeof(pHdr->hdr1)) ){ + rc = 1; + } + testFree(pHdr); + + if( rc==0 ){ + int nBuf = 64; + db = tdb_lsm(pDb); + lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf); + lsm_begin(db, 1); + lsm_commit(db, 0); + rc = lsm_work(db, 0, 0, 0); + } + + testCksumDatabase(pDb, zCksum2); + testCompareStr(zCksum, zCksum2, &rc); + } + + testDatasourceFree(pData); + testClose(&pDb); + testDeleteLsmdb(zCopy); + *pRc = rc; + } +} + +static void doWriterCrash1(int *pRc){ + const int nWrite = 2000; + const int nStep = 10; + const int iWriteStart = 20000; + int rc = 0; + TestDb *pDb = 0; + Datasource *pData = 0; + + rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb); + if( rc==0 ){ + int iDot = 0; + char zCksum[TEST_CKSUM_BYTES]; + int i; + setupDatabase1(pDb, &pData); + testCksumDatabase(pDb, zCksum); + testBegin(pDb, 2, &rc); + for(i=0; rc==0 && i<nWrite; i+=nStep){ + testCaseProgress(i, nWrite, testCaseNDot(), &iDot); + testWriteDatasourceRange(pDb, pData, iWriteStart+i, nStep, &rc); + doLiveRecovery("testdb.lsm", zCksum, &rc); + } + } + testCommit(pDb, 0, &rc); + testClose(&pDb); + testDatasourceFree(pData); + *pRc = rc; +} + +/* +** This test case verifies that inconsistent tree-headers in shared-memory +** are resolved correctly. +*/ +static void doWriterCrash2(int *pRc){ + int rc = 0; + TestDb *pDb = 0; + Datasource *pData = 0; + + rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb); + if( rc==0 ){ + ShmHeader *pHdr1; + ShmHeader *pHdr2; + char zCksum1[TEST_CKSUM_BYTES]; + char zCksum2[TEST_CKSUM_BYTES]; + + pHdr1 = testMalloc(sizeof(ShmHeader)); + pHdr2 = testMalloc(sizeof(ShmHeader)); + setupDatabase1(pDb, &pData); + + /* Grab a copy of the shared-memory header. And the db checksum */ + testReadFile("testdb.lsm-shm", 0, (void *)pHdr1, sizeof(ShmHeader), &rc); + testCksumDatabase(pDb, zCksum1); + + /* Modify the database */ + testBegin(pDb, 2, &rc); + testWriteDatasourceRange(pDb, pData, 30000, 200, &rc); + testCommit(pDb, 0, &rc); + + /* Grab a second copy of the shared-memory header. And the db checksum */ + testReadFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); + testCksumDatabase(pDb, zCksum2); + doLiveRecovery("testdb.lsm", zCksum2, &rc); + + /* If both tree-headers are valid, tree-header-1 is used. */ + memcpy(&pHdr2->hdr1, &pHdr1->hdr1, sizeof(pHdr1->hdr1)); + pHdr2->bWriter = 1; + testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); + doLiveRecovery("testdb.lsm", zCksum1, &rc); + + /* If both tree-headers are valid, tree-header-1 is used. */ + memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1)); + memcpy(&pHdr2->hdr2, &pHdr1->hdr1, sizeof(pHdr1->hdr1)); + pHdr2->bWriter = 1; + testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); + doLiveRecovery("testdb.lsm", zCksum2, &rc); + + /* If tree-header 1 is invalid, tree-header-2 is used */ + memcpy(&pHdr2->hdr2, &pHdr2->hdr1, sizeof(pHdr1->hdr1)); + pHdr2->hdr1.aCksum[0] = 5; + pHdr2->hdr1.aCksum[0] = 6; + pHdr2->bWriter = 1; + testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); + doLiveRecovery("testdb.lsm", zCksum2, &rc); + + /* If tree-header 2 is invalid, tree-header-1 is used */ + memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1)); + pHdr2->hdr2.aCksum[0] = 5; + pHdr2->hdr2.aCksum[0] = 6; + pHdr2->bWriter = 1; + testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc); + doLiveRecovery("testdb.lsm", zCksum2, &rc); + + testFree(pHdr1); + testFree(pHdr2); + testClose(&pDb); + } + + *pRc = rc; +} + +void do_writer_crash_test(const char *zPattern, int *pRc){ + struct Test { + const char *zName; + void (*xFunc)(int *); + } aTest[] = { + { "writercrash1.lsm", doWriterCrash1 }, + { "writercrash2.lsm", doWriterCrash2 }, + }; + int i; + for(i=0; i<ArraySize(aTest); i++){ + struct Test *p = &aTest[i]; + if( testCaseBegin(pRc, zPattern, p->zName) ){ + p->xFunc(pRc); + testCaseFinish(*pRc); + } + } + +} |