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
47struct CheckpointStarvationCtx {
48  int eMode;
49  int nMaxFrame;
50};
51typedef struct CheckpointStarvationCtx CheckpointStarvationCtx;
52
53static 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
69static 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
92static 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
130static 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
140static 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