3 ** This file contains test cases to verify that "live-recovery" following
4 ** a mid-transaction failure of a writer process.
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.
17 typedef struct SetupStep 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(
29 const SetupStep
*pStep
,
32 testWriteDatasourceRange(pDb
, pData
, pStep
->iInsStart
, pStep
->nIns
, pRc
);
33 testDeleteDatasourceRange(pDb
, pData
, pStep
->iDelStart
, pStep
->nDel
, pRc
);
37 lsm_db
*db
= tdb_lsm(pDb
);
39 lsm_config(db
, LSM_CONFIG_AUTOFLUSH
, &nSave
);
40 lsm_config(db
, LSM_CONFIG_AUTOFLUSH
, &nBuf
);
43 lsm_config(db
, LSM_CONFIG_AUTOFLUSH
, &nSave
);
45 *pRc
= lsm_work(db
, 0, 0, 0);
47 *pRc
= lsm_checkpoint(db
, 0);
52 static void doSetupStepArray(
55 const SetupStep
*aStep
,
59 for(i
=0; i
<nStep
; i
++){
61 doSetupStep(pDb
, pData
, &aStep
[i
], &rc
);
66 static void setupDatabase1(TestDb
*pDb
, Datasource
**ppData
){
67 const SetupStep aStep
[] = {
70 { 0, 10001, 1000, 0, 0 },
72 const DatasourceDefn defn
= {TEST_DATASOURCE_RANDOM
, 12, 16, 100, 500};
75 pData
= testDatasourceNew(&defn
);
76 doSetupStepArray(pDb
, pData
, aStep
, ArraySize(aStep
));
80 testDatasourceFree(pData
);
85 void testReadFile(const char *zFile
, int iOff
, void *pOut
, int nByte
, int *pRc
){
88 fd
= fopen(zFile
, "rb");
92 if( 0!=fseek(fd
, iOff
, SEEK_SET
) ){
96 if( (size_t)nByte
!=fread(pOut
, 1, nByte
, fd
) ){
114 fd
= fopen(zFile
, "r+b");
118 if( 0!=fseek(fd
, iOff
, SEEK_SET
) ){
122 if( (size_t)nByte
!=fwrite(pOut
, 1, nByte
, fd
) ){
131 static ShmHeader
*getShmHeader(const char *zDb
){
133 char *zShm
= testMallocPrintf("%s-shm", zDb
);
136 pHdr
= testMalloc(sizeof(ShmHeader
));
137 testReadFile(zShm
, 0, (void *)pHdr
, sizeof(ShmHeader
), &rc
);
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
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
){
161 const DatasourceDefn defn
= {TEST_DATASOURCE_RANDOM
, 20, 25, 100, 500};
163 const char *zCopy
= "testcopy.lsm";
164 char zCksum2
[TEST_CKSUM_BYTES
];
168 pData
= testDatasourceNew(&defn
);
170 testCopyLsmdb(zDb
, zCopy
);
171 rc
= tdb_lsm_open("test_no_recovery=1", zCopy
, 0, &pDb
);
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
)) ){
191 lsm_config(db
, LSM_CONFIG_AUTOFLUSH
, &nBuf
);
194 rc
= lsm_work(db
, 0, 0, 0);
197 testCksumDatabase(pDb
, zCksum2
);
198 testCompareStr(zCksum
, zCksum2
, &rc
);
201 testDatasourceFree(pData
);
203 testDeleteLsmdb(zCopy
);
208 static void doWriterCrash1(int *pRc
){
209 const int nWrite
= 2000;
210 const int nStep
= 10;
211 const int iWriteStart
= 20000;
214 Datasource
*pData
= 0;
216 rc
= tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb
);
219 char zCksum
[TEST_CKSUM_BYTES
];
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
);
232 testDatasourceFree(pData
);
237 ** This test case verifies that inconsistent tree-headers in shared-memory
238 ** are resolved correctly.
240 static void doWriterCrash2(int *pRc
){
243 Datasource
*pData
= 0;
245 rc
= tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb
);
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
));
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
));
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;
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;
296 testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2
, sizeof(ShmHeader
), &rc
);
297 doLiveRecovery("testdb.lsm", zCksum2
, &rc
);
307 void do_writer_crash_test(const char *zPattern
, int *pRc
){
310 void (*xFunc
)(int *);
312 { "writercrash1.lsm", doWriterCrash1
},
313 { "writercrash2.lsm", doWriterCrash2
},
316 for(i
=0; i
<ArraySize(aTest
); i
++){
317 struct Test
*p
= &aTest
[i
];
318 if( testCaseBegin(pRc
, zPattern
, p
->zName
) ){
320 testCaseFinish(*pRc
);