Home | History | Annotate | Download | only in test
      1 /*
      2 ** 2001 September 15
      3 **
      4 ** The author disclaims copyright to this source code.  In place of
      5 ** a legal notice, here is a blessing:
      6 **
      7 **    May you do good and not evil.
      8 **    May you find forgiveness for yourself and forgive others.
      9 **    May you share freely, never taking more than you give.
     10 **
     11 *************************************************************************
     12 ** This file is part of the test program "threadtest3". Despite being a C
     13 ** file it is not compiled separately, but included by threadtest3.c using
     14 ** the #include directive normally used with header files.
     15 **
     16 ** This file contains the implementation of test cases:
     17 **
     18 **     checkpoint_starvation_1
     19 **     checkpoint_starvation_2
     20 */
     21 
     22 /*
     23 ** Both test cases involve 1 writer/checkpointer thread and N reader threads.
     24 **
     25 ** Each reader thread performs a series of read transactions, one after
     26 ** another. Each read transaction lasts for 100 ms.
     27 **
     28 ** The writer writes transactions as fast as possible. It uses a callback
     29 ** registered with sqlite3_wal_hook() to try to keep the WAL-size limited to
     30 ** around 50 pages.
     31 **
     32 ** In test case checkpoint_starvation_1, the auto-checkpoint uses
     33 ** SQLITE_CHECKPOINT_PASSIVE. In checkpoint_starvation_2, it uses RESTART.
     34 ** The expectation is that in the first case the WAL file will grow very
     35 ** large, and in the second will be limited to the 50 pages or thereabouts.
     36 ** However, the overall transaction throughput will be lower for
     37 ** checkpoint_starvation_2, as every checkpoint will block for up to 200 ms
     38 ** waiting for readers to clear.
     39 */
     40 
     41 /* Frame limit used by the WAL hook for these tests. */
     42 #define CHECKPOINT_STARVATION_FRAMELIMIT 50
     43 
     44 /* Duration in ms of each read transaction */
     45 #define CHECKPOINT_STARVATION_READMS    100
     46 
     47 struct CheckpointStarvationCtx {
     48   int eMode;
     49   int nMaxFrame;
     50 };
     51 typedef struct CheckpointStarvationCtx CheckpointStarvationCtx;
     52 
     53 static int checkpoint_starvation_walhook(
     54   void *pCtx,
     55   sqlite3 *db,
     56   const char *zDb,
     57   int nFrame
     58 ){
     59   CheckpointStarvationCtx *p = (CheckpointStarvationCtx *)pCtx;
     60   if( nFrame>p->nMaxFrame ){
     61     p->nMaxFrame = nFrame;
     62   }
     63   if( nFrame>=CHECKPOINT_STARVATION_FRAMELIMIT ){
     64     sqlite3_wal_checkpoint_v2(db, zDb, p->eMode, 0, 0);
     65   }
     66   return SQLITE_OK;
     67 }
     68 
     69 static char *checkpoint_starvation_reader(int iTid, int iArg){
     70   Error err = {0};
     71   Sqlite db = {0};
     72 
     73   opendb(&err, &db, "test.db", 0);
     74   while( !timetostop(&err) ){
     75     i64 iCount1, iCount2;
     76     sql_script(&err, &db, "BEGIN");
     77     iCount1 = execsql_i64(&err, &db, "SELECT count(x) FROM t1");
     78     usleep(CHECKPOINT_STARVATION_READMS*1000);
     79     iCount2 = execsql_i64(&err, &db, "SELECT count(x) FROM t1");
     80     sql_script(&err, &db, "COMMIT");
     81 
     82     if( iCount1!=iCount2 ){
     83       test_error(&err, "Isolation failure - %lld %lld", iCount1, iCount2);
     84     }
     85   }
     86   closedb(&err, &db);
     87 
     88   print_and_free_err(&err);
     89   return 0;
     90 }
     91 
     92 static void checkpoint_starvation_main(int nMs, CheckpointStarvationCtx *p){
     93   Error err = {0};
     94   Sqlite db = {0};
     95   Threadset threads = {0};
     96   int nInsert = 0;
     97   int i;
     98 
     99   opendb(&err, &db, "test.db", 1);
    100   sql_script(&err, &db,
    101       "PRAGMA page_size = 1024;"
    102       "PRAGMA journal_mode = WAL;"
    103       "CREATE TABLE t1(x);"
    104   );
    105 
    106   setstoptime(&err, nMs);
    107 
    108   for(i=0; i<4; i++){
    109     launch_thread(&err, &threads, checkpoint_starvation_reader, 0);
    110     usleep(CHECKPOINT_STARVATION_READMS*1000/4);
    111   }
    112 
    113   sqlite3_wal_hook(db.db, checkpoint_starvation_walhook, (void *)p);
    114   while( !timetostop(&err) ){
    115     sql_script(&err, &db, "INSERT INTO t1 VALUES(randomblob(1200))");
    116     nInsert++;
    117   }
    118 
    119   printf(" Checkpoint mode  : %s\n",
    120       p->eMode==SQLITE_CHECKPOINT_PASSIVE ? "PASSIVE" : "RESTART"
    121   );
    122   printf(" Peak WAL         : %d frames\n", p->nMaxFrame);
    123   printf(" Transaction count: %d transactions\n", nInsert);
    124 
    125   join_all_threads(&err, &threads);
    126   closedb(&err, &db);
    127   print_and_free_err(&err);
    128 }
    129 
    130 static void checkpoint_starvation_1(int nMs){
    131   Error err = {0};
    132   CheckpointStarvationCtx ctx = { SQLITE_CHECKPOINT_PASSIVE, 0 };
    133   checkpoint_starvation_main(nMs, &ctx);
    134   if( ctx.nMaxFrame<(CHECKPOINT_STARVATION_FRAMELIMIT*10) ){
    135     test_error(&err, "WAL failed to grow - %d frames", ctx.nMaxFrame);
    136   }
    137   print_and_free_err(&err);
    138 }
    139 
    140 static void checkpoint_starvation_2(int nMs){
    141   Error err = {0};
    142   CheckpointStarvationCtx ctx = { SQLITE_CHECKPOINT_RESTART, 0 };
    143   checkpoint_starvation_main(nMs, &ctx);
    144   if( ctx.nMaxFrame>CHECKPOINT_STARVATION_FRAMELIMIT+10 ){
    145     test_error(&err, "WAL grew too large - %d frames", ctx.nMaxFrame);
    146   }
    147   print_and_free_err(&err);
    148 }
    149 
    150 
    151