summaryrefslogtreecommitdiffstats
path: root/ext/lsm1/lsm-test/lsmtest8.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/lsm1/lsm-test/lsmtest8.c')
-rw-r--r--ext/lsm1/lsm-test/lsmtest8.c324
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);
+ }
+ }
+
+}