| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 ** 2011-02-02 | |
| 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, void *pArg){ | |
| 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 } | |
| OLD | NEW |