modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/lsm1/lsm-test/lsmtest8.c (about)

     1  
     2  /*
     3  ** This file contains test cases to verify that "live-recovery" following
     4  ** a mid-transaction failure of a writer process.
     5  */
     6  
     7  
     8  /* 
     9  ** This test file includes lsmInt.h to get access to the definition of the
    10  ** ShmHeader structure. This is required to cause strategic damage to the
    11  ** shared memory header as part of recovery testing.
    12  */
    13  #include "lsmInt.h"
    14  
    15  #include "lsmtest.h"
    16  
    17  typedef struct SetupStep SetupStep;
    18  struct SetupStep {
    19    int bFlush;                     /* Flush to disk and checkpoint */
    20    int iInsStart;                  /* First key-value from ds to insert */
    21    int nIns;                       /* Number of rows to insert */
    22    int iDelStart;                  /* First key from ds to delete */
    23    int nDel;                       /* Number of rows to delete */
    24  };
    25  
    26  static void doSetupStep(
    27    TestDb *pDb, 
    28    Datasource *pData, 
    29    const SetupStep *pStep, 
    30    int *pRc
    31  ){
    32    testWriteDatasourceRange(pDb, pData, pStep->iInsStart, pStep->nIns, pRc);
    33    testDeleteDatasourceRange(pDb, pData, pStep->iDelStart, pStep->nDel, pRc);
    34    if( *pRc==0 ){
    35      int nSave = -1;
    36      int nBuf = 64;
    37      lsm_db *db = tdb_lsm(pDb);
    38  
    39      lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave);
    40      lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf);
    41      lsm_begin(db, 1);
    42      lsm_commit(db, 0);
    43      lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave);
    44  
    45      *pRc = lsm_work(db, 0, 0, 0);
    46      if( *pRc==0 ){
    47        *pRc = lsm_checkpoint(db, 0);
    48      }
    49    }
    50  }
    51  
    52  static void doSetupStepArray(
    53    TestDb *pDb, 
    54    Datasource *pData, 
    55    const SetupStep *aStep, 
    56    int nStep
    57  ){
    58    int i;
    59    for(i=0; i<nStep; i++){
    60      int rc = 0;
    61      doSetupStep(pDb, pData, &aStep[i], &rc);
    62      assert( rc==0 );
    63    }
    64  }
    65  
    66  static void setupDatabase1(TestDb *pDb, Datasource **ppData){
    67    const SetupStep aStep[] = {
    68      { 0,                                  1,     2000, 0, 0 },
    69      { 1,                                  0,     0, 0, 0 },
    70      { 0,                                  10001, 1000, 0, 0 },
    71    };
    72    const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 100, 500};
    73    Datasource *pData;
    74  
    75    pData = testDatasourceNew(&defn);
    76    doSetupStepArray(pDb, pData, aStep, ArraySize(aStep));
    77    if( ppData ){
    78      *ppData = pData;
    79    }else{
    80      testDatasourceFree(pData);
    81    }
    82  }
    83  
    84  #include <stdio.h>
    85  void testReadFile(const char *zFile, int iOff, void *pOut, int nByte, int *pRc){
    86    if( *pRc==0 ){
    87      FILE *fd;
    88      fd = fopen(zFile, "rb");
    89      if( fd==0 ){
    90        *pRc = 1;
    91      }else{
    92        if( 0!=fseek(fd, iOff, SEEK_SET) ){
    93          *pRc = 1;
    94        }else{
    95          assert( nByte>=0 );
    96          if( (size_t)nByte!=fread(pOut, 1, nByte, fd) ){
    97            *pRc = 1;
    98          }
    99        }
   100        fclose(fd);
   101      }
   102    }
   103  }
   104  
   105  void testWriteFile(
   106    const char *zFile, 
   107    int iOff, 
   108    void *pOut, 
   109    int nByte, 
   110    int *pRc
   111  ){
   112    if( *pRc==0 ){
   113      FILE *fd;
   114      fd = fopen(zFile, "r+b");
   115      if( fd==0 ){
   116        *pRc = 1;
   117      }else{
   118        if( 0!=fseek(fd, iOff, SEEK_SET) ){
   119          *pRc = 1;
   120        }else{
   121          assert( nByte>=0 );
   122          if( (size_t)nByte!=fwrite(pOut, 1, nByte, fd) ){
   123            *pRc = 1;
   124          }
   125        }
   126        fclose(fd);
   127      }
   128    }
   129  }
   130  
   131  static ShmHeader *getShmHeader(const char *zDb){
   132    int rc = 0;
   133    char *zShm = testMallocPrintf("%s-shm", zDb);
   134    ShmHeader *pHdr;
   135  
   136    pHdr = testMalloc(sizeof(ShmHeader));
   137    testReadFile(zShm, 0, (void *)pHdr, sizeof(ShmHeader), &rc);
   138    assert( rc==0 );
   139  
   140    return pHdr;
   141  }
   142  
   143  /*
   144  ** This function makes a copy of the three files associated with LSM 
   145  ** database zDb (i.e. if zDb is "test.db", it makes copies of "test.db",
   146  ** "test.db-log" and "test.db-shm").
   147  **
   148  ** It then opens a new database connection to the copy with the xLock() call
   149  ** instrumented so that it appears that some other process already connected
   150  ** to the db (holding a shared lock on DMS2). This prevents recovery from
   151  ** running. Then:
   152  **
   153  **    1) Check that the checksum of the database is zCksum. 
   154  **    2) Write a few keys to the database. Then delete the same keys. 
   155  **    3) Check that the checksum is zCksum.
   156  **    4) Flush the db to disk and run a checkpoint. 
   157  **    5) Check once more that the checksum is still zCksum.
   158  */
   159  static void doLiveRecovery(const char *zDb, const char *zCksum, int *pRc){
   160    if( *pRc==LSM_OK ){
   161      const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 20, 25, 100, 500};
   162      Datasource *pData;
   163      const char *zCopy = "testcopy.lsm";
   164      char zCksum2[TEST_CKSUM_BYTES];
   165      TestDb *pDb = 0;
   166      int rc;
   167  
   168      pData = testDatasourceNew(&defn);
   169  
   170      testCopyLsmdb(zDb, zCopy);
   171      rc = tdb_lsm_open("test_no_recovery=1", zCopy, 0, &pDb);
   172      if( rc==0 ){
   173        ShmHeader *pHdr;
   174        lsm_db *db;
   175        testCksumDatabase(pDb, zCksum2);
   176        testCompareStr(zCksum, zCksum2, &rc);
   177  
   178        testWriteDatasourceRange(pDb, pData, 1, 10, &rc);
   179        testDeleteDatasourceRange(pDb, pData, 1, 10, &rc);
   180  
   181        /* Test that the two tree-headers are now consistent. */
   182        pHdr = getShmHeader(zCopy);
   183        if( rc==0 && memcmp(&pHdr->hdr1, &pHdr->hdr2, sizeof(pHdr->hdr1)) ){
   184          rc = 1;
   185        }
   186        testFree(pHdr);
   187  
   188        if( rc==0 ){
   189          int nBuf = 64;
   190          db = tdb_lsm(pDb);
   191          lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf);
   192          lsm_begin(db, 1);
   193          lsm_commit(db, 0);
   194          rc = lsm_work(db, 0, 0, 0);
   195        }
   196  
   197        testCksumDatabase(pDb, zCksum2);
   198        testCompareStr(zCksum, zCksum2, &rc);
   199      }
   200  
   201      testDatasourceFree(pData);
   202      testClose(&pDb);
   203      testDeleteLsmdb(zCopy);
   204      *pRc = rc;
   205    }
   206  }
   207  
   208  static void doWriterCrash1(int *pRc){
   209    const int nWrite = 2000;
   210    const int nStep = 10;
   211    const int iWriteStart = 20000;
   212    int rc = 0;
   213    TestDb *pDb = 0;
   214    Datasource *pData = 0;
   215  
   216    rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb);
   217    if( rc==0 ){
   218      int iDot = 0;
   219      char zCksum[TEST_CKSUM_BYTES];
   220      int i;
   221      setupDatabase1(pDb, &pData);
   222      testCksumDatabase(pDb, zCksum);
   223      testBegin(pDb, 2, &rc);
   224      for(i=0; rc==0 && i<nWrite; i+=nStep){
   225        testCaseProgress(i, nWrite, testCaseNDot(), &iDot);
   226        testWriteDatasourceRange(pDb, pData, iWriteStart+i, nStep, &rc);
   227        doLiveRecovery("testdb.lsm", zCksum, &rc);
   228      }
   229    }
   230    testCommit(pDb, 0, &rc);
   231    testClose(&pDb);
   232    testDatasourceFree(pData);
   233    *pRc = rc;
   234  }
   235  
   236  /*
   237  ** This test case verifies that inconsistent tree-headers in shared-memory
   238  ** are resolved correctly. 
   239  */
   240  static void doWriterCrash2(int *pRc){
   241    int rc = 0;
   242    TestDb *pDb = 0;
   243    Datasource *pData = 0;
   244  
   245    rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb);
   246    if( rc==0 ){
   247      ShmHeader *pHdr1;
   248      ShmHeader *pHdr2;
   249      char zCksum1[TEST_CKSUM_BYTES];
   250      char zCksum2[TEST_CKSUM_BYTES];
   251  
   252      pHdr1 = testMalloc(sizeof(ShmHeader));
   253      pHdr2 = testMalloc(sizeof(ShmHeader));
   254      setupDatabase1(pDb, &pData);
   255  
   256      /* Grab a copy of the shared-memory header. And the db checksum */
   257      testReadFile("testdb.lsm-shm", 0, (void *)pHdr1, sizeof(ShmHeader), &rc);
   258      testCksumDatabase(pDb, zCksum1);
   259  
   260      /* Modify the database */
   261      testBegin(pDb, 2, &rc);
   262      testWriteDatasourceRange(pDb, pData, 30000, 200, &rc);
   263      testCommit(pDb, 0, &rc);
   264  
   265      /* Grab a second copy of the shared-memory header. And the db checksum */
   266      testReadFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
   267      testCksumDatabase(pDb, zCksum2);
   268      doLiveRecovery("testdb.lsm", zCksum2, &rc);
   269  
   270      /* If both tree-headers are valid, tree-header-1 is used. */
   271      memcpy(&pHdr2->hdr1, &pHdr1->hdr1, sizeof(pHdr1->hdr1));
   272      pHdr2->bWriter = 1;
   273      testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
   274      doLiveRecovery("testdb.lsm", zCksum1, &rc);
   275  
   276      /* If both tree-headers are valid, tree-header-1 is used. */
   277      memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1));
   278      memcpy(&pHdr2->hdr2, &pHdr1->hdr1, sizeof(pHdr1->hdr1));
   279      pHdr2->bWriter = 1;
   280      testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
   281      doLiveRecovery("testdb.lsm", zCksum2, &rc);
   282  
   283      /* If tree-header 1 is invalid, tree-header-2 is used */
   284      memcpy(&pHdr2->hdr2, &pHdr2->hdr1, sizeof(pHdr1->hdr1));
   285      pHdr2->hdr1.aCksum[0] = 5;
   286      pHdr2->hdr1.aCksum[0] = 6;
   287      pHdr2->bWriter = 1;
   288      testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
   289      doLiveRecovery("testdb.lsm", zCksum2, &rc);
   290  
   291      /* If tree-header 2 is invalid, tree-header-1 is used */
   292      memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1));
   293      pHdr2->hdr2.aCksum[0] = 5;
   294      pHdr2->hdr2.aCksum[0] = 6;
   295      pHdr2->bWriter = 1;
   296      testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
   297      doLiveRecovery("testdb.lsm", zCksum2, &rc);
   298  
   299      testFree(pHdr1);
   300      testFree(pHdr2);
   301      testClose(&pDb);
   302    }
   303  
   304    *pRc = rc;
   305  }
   306  
   307  void do_writer_crash_test(const char *zPattern, int *pRc){
   308    struct Test {
   309      const char *zName;
   310      void (*xFunc)(int *);
   311    } aTest[] = {
   312      { "writercrash1.lsm", doWriterCrash1 },
   313      { "writercrash2.lsm", doWriterCrash2 },
   314    };
   315    int i;
   316    for(i=0; i<ArraySize(aTest); i++){
   317      struct Test *p = &aTest[i];
   318      if( testCaseBegin(pRc, zPattern, p->zName) ){
   319        p->xFunc(pRc);
   320        testCaseFinish(*pRc);
   321      }
   322    }
   323  
   324  }
   325  
   326