DatabaseTest.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/test/DatabaseTest.cpp#16 $
00003 // Fennel is a library of data storage and processing components.
00004 // Copyright (C) 2005-2009 The Eigenbase Project
00005 // Copyright (C) 2005-2009 SQLstream, Inc.
00006 // Copyright (C) 2005-2009 LucidEra, Inc.
00007 // Portions Copyright (C) 1999-2009 John V. Sichi
00008 //
00009 // This program is free software; you can redistribute it and/or modify it
00010 // under the terms of the GNU General Public License as published by the Free
00011 // Software Foundation; either version 2 of the License, or (at your option)
00012 // any later version approved by The Eigenbase Project.
00013 //
00014 // This program is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 // GNU General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU General Public License
00020 // along with this program; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 */
00023 
00024 #include "fennel/common/CommonPreamble.h"
00025 #include "fennel/test/TestBase.h"
00026 #include "fennel/db/Database.h"
00027 #include "fennel/cache/Cache.h"
00028 #include "fennel/cache/CacheParams.h"
00029 #include "fennel/tuple/StandardTypeDescriptor.h"
00030 #include "fennel/segment/Segment.h"
00031 #include "fennel/txn/LogicalTxn.h"
00032 #include "fennel/txn/LogicalTxnLog.h"
00033 #include "fennel/txn/LogicalTxnParticipant.h"
00034 #include "fennel/txn/LogicalTxnParticipantFactory.h"
00035 #include "fennel/common/ByteInputStream.h"
00036 #include "fennel/common/ByteOutputStream.h"
00037 #include "fennel/segment/SegPageLock.h"
00038 
00039 #include <boost/test/test_tools.hpp>
00040 
00041 using namespace fennel;
00042 
00043 class DatabaseTest
00044     : virtual public TestBase,
00045         public LogicalTxnParticipant,
00046         public LogicalTxnParticipantFactory
00047 {
00048     static const LogicalActionType ACTION_INCREMENT;
00049 
00050     static const LogicalActionType ACTION_INCREMENT_FORCE;
00051 
00052     struct TestNode : public StoredNode
00053     {
00054         static const MagicNumber MAGIC_NUMBER = 0xa496c71bff0d41bdLL;
00055 
00056         uint x;
00057     };
00058 
00059     typedef SegNodeLock<TestNode> TestPageLock;
00060 
00061     SharedCache pCache;
00062     SharedDatabase pDatabase;
00063     PageId persistentPageId;
00064 
00065     void loadDatabase();
00066     void executeIncrementAction(int i);
00067     void executeIncrementAction(int i, LogicalActionType action);
00068     void executeIncrementTxn(int i);
00069     void executeCheckpointedTxn(
00070         int i,int j,bool commit,CheckpointType = CHECKPOINT_FLUSH_ALL);
00071     void verifyData(uint x);
00072     PageId writeData(uint x);
00073     void addTxnParticipant(SharedLogicalTxn);
00074 
00075 public:
00076     explicit DatabaseTest()
00077     {
00078         configMap.setStringParam(
00079             Database::paramDatabaseDir,".");
00080         configMap.setStringParam(
00081             "databaseInitSize","1000");
00082         configMap.setStringParam(
00083             "tempInitSize","1000");
00084         configMap.setStringParam(
00085             "databaseShadowLogInitSize","1000");
00086         configMap.setStringParam(
00087             "databaseTxnLogInitSize","1000");
00088 
00089         CacheParams cacheParams;
00090         cacheParams.readConfig(configMap);
00091         pCache = Cache::newCache(cacheParams);
00092 
00093         // FIXME jvs 6-Mar-2006:  some of these tests depend on
00094         // being run in this sequence; make each test self-contained
00095 
00096         FENNEL_UNIT_TEST_CASE(DatabaseTest,testCreateEmpty);
00097         FENNEL_UNIT_TEST_CASE(DatabaseTest,testLoadEmpty);
00098         FENNEL_UNIT_TEST_CASE(DatabaseTest,testRecoverEmpty);
00099         FENNEL_UNIT_TEST_CASE(DatabaseTest,testCreateData);
00100         FENNEL_UNIT_TEST_CASE(DatabaseTest,testLoadData);
00101         FENNEL_UNIT_TEST_CASE(DatabaseTest,testRecoverDataWithFlush);
00102         FENNEL_UNIT_TEST_CASE(DatabaseTest,testRecoverDataWithoutFlush);
00103         FENNEL_UNIT_TEST_CASE(DatabaseTest,testRecoverDataFromCheckpoint);
00104         FENNEL_UNIT_TEST_CASE(DatabaseTest,testRecoverDataFromFuzzyCheckpoint);
00105         FENNEL_UNIT_TEST_CASE(DatabaseTest,testRecoverDataFromRollback);
00106 
00107         FENNEL_UNIT_TEST_CASE(DatabaseTest,testForceTxns);
00108     }
00109 
00110     virtual ~DatabaseTest()
00111     {
00112     }
00113 
00114     virtual void testCaseTearDown()
00115     {
00116         pDatabase.reset();
00117     }
00118 
00119     void testCreateEmpty();
00120     void testLoadEmpty();
00121     void testRecoverEmpty();
00122 
00123     void testCreateData();
00124     void testLoadData();
00125     void testRecoverData(bool);
00126     void testRecoverDataFromCheckpoint(CheckpointType);
00127     void testRecoverDataFromCheckpoint();
00128     void testRecoverDataFromFuzzyCheckpoint();
00129     void testRecoverDataFromRollback();
00130     void testRecoverDataWithFlush();
00131     void testRecoverDataWithoutFlush();
00132     void testForceTxns();
00133 
00134     void executeForceTxn();
00135 
00136     // implement LogicalTxnParticipant
00137     virtual LogicalTxnClassId getParticipantClassId() const;
00138     virtual void describeParticipant(ByteOutputStream &logStream);
00139     virtual void redoLogicalAction(
00140         LogicalActionType actionType,
00141         ByteInputStream &logStream);
00142     virtual void undoLogicalAction(
00143         LogicalActionType actionType,
00144         ByteInputStream &logStream);
00145 
00146     // implement LogicalTxnParticipantFactory
00147     virtual SharedLogicalTxnParticipant loadParticipant(
00148         LogicalTxnClassId classId,
00149         ByteInputStream &logStream);
00150 };
00151 
00152 const LogicalActionType DatabaseTest::ACTION_INCREMENT = 1;
00153 
00154 const LogicalActionType DatabaseTest::ACTION_INCREMENT_FORCE = 2;
00155 
00156 void DatabaseTest::testCreateEmpty()
00157 {
00158     pDatabase = Database::newDatabase(
00159         pCache,
00160         configMap,
00161         DeviceMode::createNew,
00162         shared_from_this());
00163     BOOST_CHECK(!pDatabase->isRecoveryRequired());
00164 }
00165 
00166 void DatabaseTest::testCreateData()
00167 {
00168     testCreateEmpty();
00169     persistentPageId = writeData(0);
00170     pDatabase->checkpointImpl();
00171     executeIncrementTxn(5);
00172 }
00173 
00174 void DatabaseTest::testLoadEmpty()
00175 {
00176     loadDatabase();
00177     BOOST_CHECK(!pDatabase->isRecoveryRequired());
00178 }
00179 
00180 void DatabaseTest::testLoadData()
00181 {
00182     testLoadEmpty();
00183     verifyData(5);
00184 }
00185 
00186 void DatabaseTest::testRecoverEmpty()
00187 {
00188     testCreateEmpty();
00189     // Flush the pages that have been created in the empty db,
00190     // then discard the checkpoint to simulate a crash
00191     pDatabase->checkpointImpl();
00192     pDatabase->checkpointImpl(CHECKPOINT_DISCARD);
00193     BOOST_CHECK(pDatabase->isRecoveryRequired());
00194     pDatabase.reset();
00195     loadDatabase();
00196     BOOST_CHECK(pDatabase->isRecoveryRequired());
00197     pDatabase->recover(*this);
00198 }
00199 
00200 void DatabaseTest::testRecoverDataWithoutFlush()
00201 {
00202     testRecoverData(false);
00203 }
00204 
00205 void DatabaseTest::testRecoverDataWithFlush()
00206 {
00207     testRecoverData(true);
00208 }
00209 
00210 void DatabaseTest::testRecoverData(bool flush)
00211 {
00212     testCreateData();
00213     executeIncrementTxn(10);
00214     executeIncrementTxn(30);
00215     if (flush) {
00216         pDatabase->getDataSegment()->checkpoint();
00217     }
00218     pDatabase->checkpointImpl(CHECKPOINT_DISCARD);
00219     BOOST_CHECK(pDatabase->isRecoveryRequired());
00220     pDatabase.reset();
00221     loadDatabase();
00222     BOOST_CHECK(pDatabase->isRecoveryRequired());
00223     pDatabase->recover(*this);
00224     verifyData(45);
00225 }
00226 
00227 void DatabaseTest::testRecoverDataFromCheckpoint(CheckpointType checkpointType)
00228 {
00229     testCreateData();
00230     executeIncrementTxn(10);
00231     executeCheckpointedTxn(25,70,true,checkpointType);
00232     pDatabase->checkpointImpl(CHECKPOINT_DISCARD);
00233     BOOST_CHECK(pDatabase->isRecoveryRequired());
00234     pDatabase.reset();
00235     loadDatabase();
00236     BOOST_CHECK(pDatabase->isRecoveryRequired());
00237     pDatabase->recover(*this);
00238     verifyData(110);
00239 }
00240 
00241 void DatabaseTest::testRecoverDataFromCheckpoint()
00242 {
00243     testRecoverDataFromCheckpoint(CHECKPOINT_FLUSH_ALL);
00244 }
00245 
00246 void DatabaseTest::testRecoverDataFromFuzzyCheckpoint()
00247 {
00248     testRecoverDataFromCheckpoint(CHECKPOINT_FLUSH_FUZZY);
00249 }
00250 
00251 void DatabaseTest::testRecoverDataFromRollback()
00252 {
00253     testCreateData();
00254     executeIncrementTxn(10);
00255     executeCheckpointedTxn(25,70,false);
00256     pDatabase->checkpointImpl(CHECKPOINT_DISCARD);
00257     BOOST_CHECK(pDatabase->isRecoveryRequired());
00258     pDatabase.reset();
00259     loadDatabase();
00260     BOOST_CHECK(pDatabase->isRecoveryRequired());
00261     pDatabase->recover(*this);
00262     verifyData(15);
00263 }
00264 
00265 void DatabaseTest::testForceTxns()
00266 {
00267     configMap.setStringParam(
00268         "forceTxns","true");
00269     configMap.setStringParam(
00270         "disableSnapshots","true");
00271     testCreateData();
00272     pDatabase->checkpointImpl();
00273     verifyData(5);
00274 
00275     // Allocate an extra page for use below.
00276     PageId extraPageId = writeData(42);
00277 
00278     pDatabase.reset();
00279     loadDatabase();
00280 
00281     // Pin the extra page to make sure that doesn't cause problems
00282     // for rollback on unrelated data.
00283     SegmentAccessor segmentAccessor(pDatabase->getDataSegment(),pCache);
00284     TestPageLock pageLock(segmentAccessor);
00285     pageLock.lockShared(extraPageId);
00286 
00287     executeForceTxn();
00288     executeForceTxn();
00289 
00290     pageLock.unlock();
00291 
00292     pDatabase.reset();
00293 }
00294 
00295 void DatabaseTest::executeForceTxn()
00296 {
00297     SharedLogicalTxn pTxn = pDatabase->getTxnLog()->newLogicalTxn(pCache);
00298     addTxnParticipant(pTxn);
00299     executeIncrementAction(10, ACTION_INCREMENT_FORCE);
00300     verifyData(15);
00301     pTxn->rollback();
00302     pTxn.reset();
00303     // Give the background flush thread time to flush the data page
00304     snooze(3);
00305     pDatabase->recoverOnline();
00306     verifyData(5);
00307 }
00308 
00309 void DatabaseTest::loadDatabase()
00310 {
00311     pDatabase = Database::newDatabase(
00312         pCache,
00313         configMap,
00314         DeviceMode::load,
00315         shared_from_this());
00316 }
00317 
00318 LogicalTxnClassId DatabaseTest::getParticipantClassId() const
00319 {
00320     return LogicalTxnClassId(0xa470573b38dcaa0aLL);
00321 }
00322 
00323 void DatabaseTest::describeParticipant(ByteOutputStream &)
00324 {
00325 }
00326 
00327 void DatabaseTest::undoLogicalAction(
00328     LogicalActionType actionType,
00329     ByteInputStream &logStream)
00330 {
00331     int i;
00332     logStream.readValue(i);
00333     if (actionType == ACTION_INCREMENT_FORCE) {
00334         return;
00335     }
00336     assert(actionType == ACTION_INCREMENT);
00337     SegmentAccessor segmentAccessor(pDatabase->getDataSegment(),pCache);
00338     TestPageLock pageLock(segmentAccessor);
00339     pageLock.lockExclusive(persistentPageId);
00340     pageLock.getNodeForWrite().x -= i;
00341 }
00342 
00343 void DatabaseTest::redoLogicalAction(
00344     LogicalActionType actionType,
00345     ByteInputStream &logStream)
00346 {
00347     int i;
00348     logStream.readValue(i);
00349     if (actionType == ACTION_INCREMENT_FORCE) {
00350         return;
00351     }
00352     assert(actionType == ACTION_INCREMENT);
00353     SegmentAccessor segmentAccessor(pDatabase->getDataSegment(),pCache);
00354     TestPageLock pageLock(segmentAccessor);
00355     pageLock.lockExclusive(persistentPageId);
00356     pageLock.getNodeForWrite().x += i;
00357 }
00358 
00359 SharedLogicalTxnParticipant DatabaseTest::loadParticipant(
00360     LogicalTxnClassId classId,
00361     ByteInputStream &)
00362 {
00363     assert(classId == getParticipantClassId());
00364     return boost::dynamic_pointer_cast<LogicalTxnParticipant>(
00365         shared_from_this());
00366 }
00367 
00368 void DatabaseTest::executeIncrementAction(int i)
00369 {
00370     executeIncrementAction(i, ACTION_INCREMENT);
00371 }
00372 
00373 void DatabaseTest::executeIncrementAction(int i, LogicalActionType action)
00374 {
00375     ByteOutputStream &logStream =
00376         getLogicalTxn()->beginLogicalAction(*this,action);
00377     logStream.writeValue(i);
00378     getLogicalTxn()->endLogicalAction();
00379     SegmentAccessor segmentAccessor(pDatabase->getDataSegment(),pCache);
00380     TestPageLock pageLock(segmentAccessor);
00381     pageLock.lockExclusive(persistentPageId);
00382     pageLock.getNodeForWrite().x += i;
00383 }
00384 
00385 void DatabaseTest::addTxnParticipant(SharedLogicalTxn pTxn)
00386 {
00387     pTxn->addParticipant(
00388         boost::dynamic_pointer_cast<LogicalTxnParticipant>(
00389             shared_from_this()));
00390 }
00391 
00392 void DatabaseTest::executeIncrementTxn(int i)
00393 {
00394     SharedLogicalTxn pTxn = pDatabase->getTxnLog()->newLogicalTxn(pCache);
00395     addTxnParticipant(pTxn);
00396     executeIncrementAction(i);
00397     pTxn->commit();
00398 }
00399 
00400 void DatabaseTest::executeCheckpointedTxn(
00401     int i,int j,bool commit,CheckpointType checkpointType)
00402 {
00403     SharedLogicalTxn pTxn = pDatabase->getTxnLog()->newLogicalTxn(pCache);
00404     addTxnParticipant(pTxn);
00405     executeIncrementAction(i);
00406     pDatabase->checkpointImpl(checkpointType);
00407     executeIncrementAction(j);
00408     if (commit) {
00409         pTxn->commit();
00410     } else {
00411         pTxn->rollback();
00412     }
00413 }
00414 
00415 void DatabaseTest::verifyData(uint x)
00416 {
00417     SegmentAccessor segmentAccessor(pDatabase->getDataSegment(),pCache);
00418     TestPageLock pageLock(segmentAccessor);
00419     pageLock.lockShared(persistentPageId);
00420     BOOST_CHECK_EQUAL(pageLock.getNodeForRead().x,x);
00421 }
00422 
00423 PageId DatabaseTest::writeData(uint x)
00424 {
00425     SegmentAccessor segmentAccessor(pDatabase->getDataSegment(),pCache);
00426     TestPageLock pageLock(segmentAccessor);
00427     PageId pageId = pageLock.allocatePage();
00428     pageLock.getNodeForWrite().x = x;
00429     pageLock.unlock();
00430     return pageId;
00431 }
00432 
00433 FENNEL_UNIT_TEST_SUITE(DatabaseTest);
00434 
00435 // End DatabaseTest.cpp

Generated on Mon Jun 22 04:00:20 2009 for Fennel by  doxygen 1.5.1