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