• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
checkpoint_starvation_walhook(void * pCtx,sqlite3 * db,const char * zDb,int nFrame)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 
checkpoint_starvation_reader(int iTid,int iArg)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 
checkpoint_starvation_main(int nMs,CheckpointStarvationCtx * p)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 
checkpoint_starvation_1(int nMs)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 
checkpoint_starvation_2(int nMs)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