1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
/*
** 2011-02-02
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This file is part of the test program "threadtest3". Despite being a C
** file it is not compiled separately, but included by threadtest3.c using
** the #include directive normally used with header files.
**
** This file contains the implementation of test cases:
**
** checkpoint_starvation_1
** checkpoint_starvation_2
*/
/*
** Both test cases involve 1 writer/checkpointer thread and N reader threads.
**
** Each reader thread performs a series of read transactions, one after
** another. Each read transaction lasts for 100 ms.
**
** The writer writes transactions as fast as possible. It uses a callback
** registered with sqlite3_wal_hook() to try to keep the WAL-size limited to
** around 50 pages.
**
** In test case checkpoint_starvation_1, the auto-checkpoint uses
** SQLITE_CHECKPOINT_PASSIVE. In checkpoint_starvation_2, it uses RESTART.
** The expectation is that in the first case the WAL file will grow very
** large, and in the second will be limited to the 50 pages or thereabouts.
** However, the overall transaction throughput will be lower for
** checkpoint_starvation_2, as every checkpoint will block for up to 200 ms
** waiting for readers to clear.
*/
/* Frame limit used by the WAL hook for these tests. */
#define CHECKPOINT_STARVATION_FRAMELIMIT 50
/* Duration in ms of each read transaction */
#define CHECKPOINT_STARVATION_READMS 100
struct CheckpointStarvationCtx {
int eMode;
int nMaxFrame;
};
typedef struct CheckpointStarvationCtx CheckpointStarvationCtx;
static int checkpoint_starvation_walhook(
void *pCtx,
sqlite3 *db,
const char *zDb,
int nFrame
){
CheckpointStarvationCtx *p = (CheckpointStarvationCtx *)pCtx;
if( nFrame>p->nMaxFrame ){
p->nMaxFrame = nFrame;
}
if( nFrame>=CHECKPOINT_STARVATION_FRAMELIMIT ){
sqlite3_wal_checkpoint_v2(db, zDb, p->eMode, 0, 0);
}
return SQLITE_OK;
}
static char *checkpoint_starvation_reader(int iTid, void *pArg){
Error err = {0};
Sqlite db = {0};
opendb(&err, &db, "test.db", 0);
while( !timetostop(&err) ){
i64 iCount1, iCount2;
sql_script(&err, &db, "BEGIN");
iCount1 = execsql_i64(&err, &db, "SELECT count(x) FROM t1");
sqlite3_sleep(CHECKPOINT_STARVATION_READMS);
iCount2 = execsql_i64(&err, &db, "SELECT count(x) FROM t1");
sql_script(&err, &db, "COMMIT");
if( iCount1!=iCount2 ){
test_error(&err, "Isolation failure - %lld %lld", iCount1, iCount2);
}
}
closedb(&err, &db);
print_and_free_err(&err);
return 0;
}
static void checkpoint_starvation_main(int nMs, CheckpointStarvationCtx *p){
Error err = {0};
Sqlite db = {0};
Threadset threads = {0};
int nInsert = 0;
int i;
opendb(&err, &db, "test.db", 1);
sql_script(&err, &db,
"PRAGMA page_size = 1024;"
"PRAGMA journal_mode = WAL;"
"CREATE TABLE t1(x);"
);
setstoptime(&err, nMs);
for(i=0; i<4; i++){
launch_thread(&err, &threads, checkpoint_starvation_reader, 0);
sqlite3_sleep(CHECKPOINT_STARVATION_READMS/4);
}
sqlite3_wal_hook(db.db, checkpoint_starvation_walhook, (void *)p);
while( !timetostop(&err) ){
sql_script(&err, &db, "INSERT INTO t1 VALUES(randomblob(1200))");
nInsert++;
}
printf(" Checkpoint mode : %s\n",
p->eMode==SQLITE_CHECKPOINT_PASSIVE ? "PASSIVE" : "RESTART"
);
printf(" Peak WAL : %d frames\n", p->nMaxFrame);
printf(" Transaction count: %d transactions\n", nInsert);
join_all_threads(&err, &threads);
closedb(&err, &db);
print_and_free_err(&err);
}
static void checkpoint_starvation_1(int nMs){
Error err = {0};
CheckpointStarvationCtx ctx = { SQLITE_CHECKPOINT_PASSIVE, 0 };
checkpoint_starvation_main(nMs, &ctx);
if( ctx.nMaxFrame<(CHECKPOINT_STARVATION_FRAMELIMIT*10) ){
test_error(&err, "WAL failed to grow - %d frames", ctx.nMaxFrame);
}
print_and_free_err(&err);
}
static void checkpoint_starvation_2(int nMs){
Error err = {0};
CheckpointStarvationCtx ctx = { SQLITE_CHECKPOINT_RESTART, 0 };
checkpoint_starvation_main(nMs, &ctx);
if( ctx.nMaxFrame>CHECKPOINT_STARVATION_FRAMELIMIT+10 ){
test_error(&err, "WAL grew too large - %d frames", ctx.nMaxFrame);
}
print_and_free_err(&err);
}
|