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  }