OLD | NEW |
| (Empty) |
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 | |
OLD | NEW |