#include "lsmInt.h"
#include "lsmtest.h"
typedef struct SetupStep SetupStep;
struct SetupStep {
int bFlush;
int iInsStart;
int nIns;
int iDelStart;
int nDel;
};
static void doSetupStep(
TestDb *pDb,
Datasource *pData,
const SetupStep *pStep,
int *pRc
){
testWriteDatasourceRange(pDb, pData, pStep->iInsStart, pStep->nIns, pRc);
testDeleteDatasourceRange(pDb, pData, pStep->iDelStart, pStep->nDel, pRc);
if( *pRc==0 ){
int nSave = -1;
int nBuf = 64;
lsm_db *db = tdb_lsm(pDb);
lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave);
lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf);
lsm_begin(db, 1);
lsm_commit(db, 0);
lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nSave);
*pRc = lsm_work(db, 0, 0, 0);
if( *pRc==0 ){
*pRc = lsm_checkpoint(db, 0);
}
}
}
static void doSetupStepArray(
TestDb *pDb,
Datasource *pData,
const SetupStep *aStep,
int nStep
){
int i;
for(i=0; i<nStep; i++){
int rc = 0;
doSetupStep(pDb, pData, &aStep[i], &rc);
assert( rc==0 );
}
}
static void setupDatabase1(TestDb *pDb, Datasource **ppData){
const SetupStep aStep[] = {
{ 0, 1, 2000, 0, 0 },
{ 1, 0, 0, 0, 0 },
{ 0, 10001, 1000, 0, 0 },
};
const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 12, 16, 100, 500};
Datasource *pData;
pData = testDatasourceNew(&defn);
doSetupStepArray(pDb, pData, aStep, ArraySize(aStep));
if( ppData ){
*ppData = pData;
}else{
testDatasourceFree(pData);
}
}
#include <stdio.h>
void testReadFile(const char *zFile, int iOff, void *pOut, int nByte, int *pRc){
if( *pRc==0 ){
FILE *fd;
fd = fopen(zFile, "rb");
if( fd==0 ){
*pRc = 1;
}else{
if( 0!=fseek(fd, iOff, SEEK_SET) ){
*pRc = 1;
}else{
assert( nByte>=0 );
if( (size_t)nByte!=fread(pOut, 1, nByte, fd) ){
*pRc = 1;
}
}
fclose(fd);
}
}
}
void testWriteFile(
const char *zFile,
int iOff,
void *pOut,
int nByte,
int *pRc
){
if( *pRc==0 ){
FILE *fd;
fd = fopen(zFile, "r+b");
if( fd==0 ){
*pRc = 1;
}else{
if( 0!=fseek(fd, iOff, SEEK_SET) ){
*pRc = 1;
}else{
assert( nByte>=0 );
if( (size_t)nByte!=fwrite(pOut, 1, nByte, fd) ){
*pRc = 1;
}
}
fclose(fd);
}
}
}
static ShmHeader *getShmHeader(const char *zDb){
int rc = 0;
char *zShm = testMallocPrintf("%s-shm", zDb);
ShmHeader *pHdr;
pHdr = testMalloc(sizeof(ShmHeader));
testReadFile(zShm, 0, (void *)pHdr, sizeof(ShmHeader), &rc);
assert( rc==0 );
return pHdr;
}
static void doLiveRecovery(const char *zDb, const char *zCksum, int *pRc){
if( *pRc==LSM_OK ){
const DatasourceDefn defn = {TEST_DATASOURCE_RANDOM, 20, 25, 100, 500};
Datasource *pData;
const char *zCopy = "testcopy.lsm";
char zCksum2[TEST_CKSUM_BYTES];
TestDb *pDb = 0;
int rc;
pData = testDatasourceNew(&defn);
testCopyLsmdb(zDb, zCopy);
rc = tdb_lsm_open("test_no_recovery=1", zCopy, 0, &pDb);
if( rc==0 ){
ShmHeader *pHdr;
lsm_db *db;
testCksumDatabase(pDb, zCksum2);
testCompareStr(zCksum, zCksum2, &rc);
testWriteDatasourceRange(pDb, pData, 1, 10, &rc);
testDeleteDatasourceRange(pDb, pData, 1, 10, &rc);
pHdr = getShmHeader(zCopy);
if( rc==0 && memcmp(&pHdr->hdr1, &pHdr->hdr2, sizeof(pHdr->hdr1)) ){
rc = 1;
}
testFree(pHdr);
if( rc==0 ){
int nBuf = 64;
db = tdb_lsm(pDb);
lsm_config(db, LSM_CONFIG_AUTOFLUSH, &nBuf);
lsm_begin(db, 1);
lsm_commit(db, 0);
rc = lsm_work(db, 0, 0, 0);
}
testCksumDatabase(pDb, zCksum2);
testCompareStr(zCksum, zCksum2, &rc);
}
testDatasourceFree(pData);
testClose(&pDb);
testDeleteLsmdb(zCopy);
*pRc = rc;
}
}
static void doWriterCrash1(int *pRc){
const int nWrite = 2000;
const int nStep = 10;
const int iWriteStart = 20000;
int rc = 0;
TestDb *pDb = 0;
Datasource *pData = 0;
rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb);
if( rc==0 ){
int iDot = 0;
char zCksum[TEST_CKSUM_BYTES];
int i;
setupDatabase1(pDb, &pData);
testCksumDatabase(pDb, zCksum);
testBegin(pDb, 2, &rc);
for(i=0; rc==0 && i<nWrite; i+=nStep){
testCaseProgress(i, nWrite, testCaseNDot(), &iDot);
testWriteDatasourceRange(pDb, pData, iWriteStart+i, nStep, &rc);
doLiveRecovery("testdb.lsm", zCksum, &rc);
}
}
testCommit(pDb, 0, &rc);
testClose(&pDb);
testDatasourceFree(pData);
*pRc = rc;
}
static void doWriterCrash2(int *pRc){
int rc = 0;
TestDb *pDb = 0;
Datasource *pData = 0;
rc = tdb_lsm_open("autowork=0", "testdb.lsm", 1, &pDb);
if( rc==0 ){
ShmHeader *pHdr1;
ShmHeader *pHdr2;
char zCksum1[TEST_CKSUM_BYTES];
char zCksum2[TEST_CKSUM_BYTES];
pHdr1 = testMalloc(sizeof(ShmHeader));
pHdr2 = testMalloc(sizeof(ShmHeader));
setupDatabase1(pDb, &pData);
testReadFile("testdb.lsm-shm", 0, (void *)pHdr1, sizeof(ShmHeader), &rc);
testCksumDatabase(pDb, zCksum1);
testBegin(pDb, 2, &rc);
testWriteDatasourceRange(pDb, pData, 30000, 200, &rc);
testCommit(pDb, 0, &rc);
testReadFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
testCksumDatabase(pDb, zCksum2);
doLiveRecovery("testdb.lsm", zCksum2, &rc);
memcpy(&pHdr2->hdr1, &pHdr1->hdr1, sizeof(pHdr1->hdr1));
pHdr2->bWriter = 1;
testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
doLiveRecovery("testdb.lsm", zCksum1, &rc);
memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1));
memcpy(&pHdr2->hdr2, &pHdr1->hdr1, sizeof(pHdr1->hdr1));
pHdr2->bWriter = 1;
testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
doLiveRecovery("testdb.lsm", zCksum2, &rc);
memcpy(&pHdr2->hdr2, &pHdr2->hdr1, sizeof(pHdr1->hdr1));
pHdr2->hdr1.aCksum[0] = 5;
pHdr2->hdr1.aCksum[0] = 6;
pHdr2->bWriter = 1;
testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
doLiveRecovery("testdb.lsm", zCksum2, &rc);
memcpy(&pHdr2->hdr1, &pHdr2->hdr2, sizeof(pHdr1->hdr1));
pHdr2->hdr2.aCksum[0] = 5;
pHdr2->hdr2.aCksum[0] = 6;
pHdr2->bWriter = 1;
testWriteFile("testdb.lsm-shm", 0, (void *)pHdr2, sizeof(ShmHeader), &rc);
doLiveRecovery("testdb.lsm", zCksum2, &rc);
testFree(pHdr1);
testFree(pHdr2);
testClose(&pDb);
}
*pRc = rc;
}
void do_writer_crash_test(const char *zPattern, int *pRc){
struct Test {
const char *zName;
void (*xFunc)(int *);
} aTest[] = {
{ "writercrash1.lsm", doWriterCrash1 },
{ "writercrash2.lsm", doWriterCrash2 },
};
int i;
for(i=0; i<ArraySize(aTest); i++){
struct Test *p = &aTest[i];
if( testCaseBegin(pRc, zPattern, p->zName) ){
p->xFunc(pRc);
testCaseFinish(*pRc);
}
}
}