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 |