gitlab.com/CoiaPrant/sqlite3@v1.19.1/testdata/tcl/tt3_checkpoint.c (about)

     1  /*
     2  ** 2011-02-02
     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, void *pArg){
    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      sqlite3_sleep(CHECKPOINT_STARVATION_READMS);
    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      sqlite3_sleep(CHECKPOINT_STARVATION_READMS/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  }