Update test file walprotocol.test to account for the changes in the wal
[sqlite.git] / ext / lsm1 / lsm-test / lsmtest8.c
blobf734ac6aba2a1aaf2b97dc3385728eb8469cb459
2 /*
3 ** This file contains test cases to verify that "live-recovery" following
4 ** a mid-transaction failure of a writer process.
5 */
8 /*
9 ** This test file includes lsmInt.h to get access to the definition of the
10 ** ShmHeader structure. This is required to cause strategic damage to the
11 ** shared memory header as part of recovery testing.
13 #include "lsmInt.h"
15 #include "lsmtest.h"
17 typedef struct SetupStep SetupStep;
18 struct SetupStep {
19 int bFlush; /* Flush to disk and checkpoint */
20 int iInsStart; /* First key-value from ds to insert */
21 int nIns; /* Number of rows to insert */
22 int iDelStart; /* First key from ds to delete */
23 int nDel; /* Number of rows to delete */
26 static void doSetupStep(
27 TestDb *pDb,
28 Datasource *pData,
29 const SetupStep *pStep,
30 int *pRc
32 testWriteDatasourceRange(pDb, pData, pStep->iInsStart, pStep->nIns, pRc);
33 testDeleteDatasourceRange(pDb, pData, pStep->iDelStart, pStep->nDel, pRc);
34 if( *pRc==0 ){
35 int nSave = -1;
36 int nBuf = 64;
37 lsm_db *db = tdb_lsm(pDb);
39 lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave);
40 lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf);
41 lsm_begin(db, 1);
42 lsm_commit(db, 0);
43 lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave);
45 *pRc = lsm_work(db, 0, 0, 0);
46 if( *pRc==0 ){
47 *pRc = lsm_checkpoint(db, 0);
52 static void doSetupStepArray(
53 TestDb *pDb,
54 Datasource *pData,
55 const SetupStep *aStep,
56 int nStep
58 int i;
59 for(i=0; i<nStep; i++){
60 int rc = 0;
61 doSetupStep(pDb, pData, &aStep[i], &rc);
62 assert( rc==0 );
66 static void setupDatabase1(TestDb *pDb, Datasource **ppData){
67 const SetupStep aStep[] = {
68 { 0, 1, 2000, 0, 0 },
69 { 1, 0, 0, 0, 0 },
70 { 0, 10001, 1000, 0, 0 },
72 const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 100, 500};
73 Datasource *pData;
75 pData = testDatasourceNew(&defn);
76 doSetupStepArray(pDb, pData, aStep, ArraySize(aStep));
77 if( ppData ){
78 *ppData = pData;
79 }else{
80 testDatasourceFree(pData);
84 #include <stdio.h>
85 void testReadFile(const char *zFile, int iOff, void *pOut, int nByte, int *pRc){
86 if( *pRc==0 ){
87 FILE *fd;
88 fd = fopen(zFile, "rb");
89 if( fd==0 ){
90 *pRc = 1;
91 }else{
92 if( 0!=fseek(fd, iOff, SEEK_SET) ){
93 *pRc = 1;
94 }else{
95 assert( nByte>=0 );
96 if( (size_t)nByte!=fread(pOut, 1, nByte, fd) ){
97 *pRc = 1;
100 fclose(fd);
105 void testWriteFile(
106 const char *zFile,
107 int iOff,
108 void *pOut,
109 int nByte,
110 int *pRc
112 if( *pRc==0 ){
113 FILE *fd;
114 fd = fopen(zFile, "r+b");
115 if( fd==0 ){
116 *pRc = 1;
117 }else{
118 if( 0!=fseek(fd, iOff, SEEK_SET) ){
119 *pRc = 1;
120 }else{
121 assert( nByte>=0 );
122 if( (size_t)nByte!=fwrite(pOut, 1, nByte, fd) ){
123 *pRc = 1;
126 fclose(fd);
131 static ShmHeader *getShmHeader(const char *zDb){
132 int rc = 0;
133 char *zShm = testMallocPrintf("%s-shm", zDb);
134 ShmHeader *pHdr;
136 pHdr = testMalloc(sizeof(ShmHeader));
137 testReadFile(zShm, 0, (void *)pHdr, sizeof(ShmHeader), &rc);
138 assert( rc==0 );
140 return pHdr;
144 ** This function makes a copy of the three files associated with LSM
145 ** database zDb (i.e. if zDb is "test.db", it makes copies of "test.db",
146 ** "test.db-log" and "test.db-shm").
148 ** It then opens a new database connection to the copy with the xLock() call
149 ** instrumented so that it appears that some other process already connected
150 ** to the db (holding a shared lock on DMS2). This prevents recovery from
151 ** running. Then:
153 ** 1) Check that the checksum of the database is zCksum.
154 ** 2) Write a few keys to the database. Then delete the same keys.
155 ** 3) Check that the checksum is zCksum.
156 ** 4) Flush the db to disk and run a checkpoint.
157 ** 5) Check once more that the checksum is still zCksum.
159 static void doLiveRecovery(const char *zDb, const char *zCksum, int *pRc){
160 if( *pRc==LSM_OK ){
161 const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 20, 25, 100, 500};
162 Datasource *pData;
163 const char *zCopy = "testcopy.lsm";
164 char zCksum2[TEST_CKSUM_BYTES];
165 TestDb *pDb = 0;
166 int rc;
168 pData = testDatasourceNew(&defn);
170 testCopyLsmdb(zDb, zCopy);
171 rc = tdb_lsm_open("test_no_recovery=1", zCopy, 0, &pDb);
172 if( rc==0 ){
173 ShmHeader *pHdr;
174 lsm_db *db;
175 testCksumDatabase(pDb, zCksum2);
176 testCompareStr(zCksum, zCksum2, &rc);
178 testWriteDatasourceRange(pDb, pData, 1, 10, &rc);
179 testDeleteDatasourceRange(pDb, pData, 1, 10, &rc);
181 /* Test that the two tree-headers are now consistent. */
182 pHdr = getShmHeader(zCopy);
183 if( rc==0 && memcmp(&pHdr->hdr1, &pHdr->hdr2, sizeof(pHdr->hdr1)) ){
184 rc = 1;
186 testFree(pHdr);
188 if( rc==0 ){
189 int nBuf = 64;
190 db = tdb_lsm(pDb);
191 lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf);
192 lsm_begin(db, 1);
193 lsm_commit(db, 0);
194 rc = lsm_work(db, 0, 0, 0);
197 testCksumDatabase(pDb, zCksum2);
198 testCompareStr(zCksum, zCksum2, &rc);
201 testDatasourceFree(pData);
202 testClose(&pDb);
203 testDeleteLsmdb(zCopy);
204 *pRc = rc;
208 static void doWriterCrash1(int *pRc){
209 const int nWrite = 2000;
210 const int nStep = 10;
211 const int iWriteStart = 20000;
212 int rc = 0;
213 TestDb *pDb = 0;
214 Datasource *pData = 0;
216 rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb);
217 if( rc==0 ){
218 int iDot = 0;
219 char zCksum[TEST_CKSUM_BYTES];
220 int i;
221 setupDatabase1(pDb, &pData);
222 testCksumDatabase(pDb, zCksum);
223 testBegin(pDb, 2, &rc);
224 for(i=0; rc==0 && i<nWrite; i+=nStep){
225 testCaseProgress(i, nWrite, testCaseNDot(), &iDot);
226 testWriteDatasourceRange(pDb, pData, iWriteStart+i, nStep, &rc);
227 doLiveRecovery("testdb.lsm", zCksum, &rc);
230 testCommit(pDb, 0, &rc);
231 testClose(&pDb);
232 testDatasourceFree(pData);
233 *pRc = rc;
237 ** This test case verifies that inconsistent tree-headers in shared-memory
238 ** are resolved correctly.
240 static void doWriterCrash2(int *pRc){
241 int rc = 0;
242 TestDb *pDb = 0;
243 Datasource *pData = 0;
245 rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb);
246 if( rc==0 ){
247 ShmHeader *pHdr1;
248 ShmHeader *pHdr2;
249 char zCksum1[TEST_CKSUM_BYTES];
250 char zCksum2[TEST_CKSUM_BYTES];
252 pHdr1 = testMalloc(sizeof(ShmHeader));
253 pHdr2 = testMalloc(sizeof(ShmHeader));
254 setupDatabase1(pDb, &pData);
256 /* Grab a copy of the shared-memory header. And the db checksum */
257 testReadFile("testdb.lsm-shm", 0, (void *)pHdr1, sizeof(ShmHeader), &rc);
258 testCksumDatabase(pDb, zCksum1);
260 /* Modify the database */
261 testBegin(pDb, 2, &rc);
262 testWriteDatasourceRange(pDb, pData, 30000, 200, &rc);
263 testCommit(pDb, 0, &rc);
265 /* Grab a second copy of the shared-memory header. And the db checksum */
266 testReadFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
267 testCksumDatabase(pDb, zCksum2);
268 doLiveRecovery("testdb.lsm", zCksum2, &rc);
270 /* If both tree-headers are valid, tree-header-1 is used. */
271 memcpy(&pHdr2->hdr1, &pHdr1->hdr1, sizeof(pHdr1->hdr1));
272 pHdr2->bWriter = 1;
273 testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
274 doLiveRecovery("testdb.lsm", zCksum1, &rc);
276 /* If both tree-headers are valid, tree-header-1 is used. */
277 memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1));
278 memcpy(&pHdr2->hdr2, &pHdr1->hdr1, sizeof(pHdr1->hdr1));
279 pHdr2->bWriter = 1;
280 testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
281 doLiveRecovery("testdb.lsm", zCksum2, &rc);
283 /* If tree-header 1 is invalid, tree-header-2 is used */
284 memcpy(&pHdr2->hdr2, &pHdr2->hdr1, sizeof(pHdr1->hdr1));
285 pHdr2->hdr1.aCksum[0] = 5;
286 pHdr2->hdr1.aCksum[0] = 6;
287 pHdr2->bWriter = 1;
288 testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
289 doLiveRecovery("testdb.lsm", zCksum2, &rc);
291 /* If tree-header 2 is invalid, tree-header-1 is used */
292 memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1));
293 pHdr2->hdr2.aCksum[0] = 5;
294 pHdr2->hdr2.aCksum[0] = 6;
295 pHdr2->bWriter = 1;
296 testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
297 doLiveRecovery("testdb.lsm", zCksum2, &rc);
299 testFree(pHdr1);
300 testFree(pHdr2);
301 testClose(&pDb);
304 *pRc = rc;
307 void do_writer_crash_test(const char *zPattern, int *pRc){
308 struct Test {
309 const char *zName;
310 void (*xFunc)(int *);
311 } aTest[] = {
312 { "writercrash1.lsm", doWriterCrash1 },
313 { "writercrash2.lsm", doWriterCrash2 },
315 int i;
316 for(i=0; i<ArraySize(aTest); i++){
317 struct Test *p = &aTest[i];
318 if( testCaseBegin(pRc, zPattern, p->zName) ){
319 p->xFunc(pRc);
320 testCaseFinish(*pRc);