00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "fennel/common/CommonPreamble.h"
00025 #include "fennel/common/AbortExcn.h"
00026 #include "fennel/db/Database.h"
00027 #include "fennel/db/CheckpointThread.h"
00028 #include "fennel/db/DataFormatExcn.h"
00029 #include "fennel/common/ConfigMap.h"
00030 #include "fennel/common/FileSystem.h"
00031 #include "fennel/common/FennelResource.h"
00032 #include "fennel/device/RandomAccessFileDevice.h"
00033 #include "fennel/cache/Cache.h"
00034 #include "fennel/cache/PagePredicate.h"
00035 #include "fennel/segment/SegmentFactory.h"
00036 #include "fennel/segment/LinearDeviceSegment.h"
00037 #include "fennel/segment/Segment.h"
00038 #include "fennel/segment/VersionedSegment.h"
00039 #include "fennel/segment/VersionedRandomAllocationSegment.h"
00040 #include "fennel/common/CompoundId.h"
00041 #include "fennel/txn/LogicalTxnLog.h"
00042 #include "fennel/txn/LogicalRecoveryLog.h"
00043 #include "fennel/common/StatsTarget.h"
00044 #include "fennel/common/FennelResource.h"
00045
00046 #include <boost/filesystem/operations.hpp>
00047
00048 #ifdef __MSVC__
00049 #include <process.h>
00050 #endif
00051
00052 FENNEL_BEGIN_CPPFILE("$Id: //open/dev/fennel/db/Database.cpp#43 $");
00053
00054 using namespace boost::filesystem;
00055
00056 ParamName Database::paramDatabaseDir = "databaseDir";
00057 ParamName Database::paramResourceDir = "resourceDir";
00058 ParamName Database::paramForceTxns = "forceTxns";
00059 ParamName Database::paramDisableSnapshots = "disableSnapshots";
00060 ParamName Database::paramDatabasePrefix = "database";
00061 ParamName Database::paramTempPrefix = "temp";
00062 ParamName Database::paramShadowLogPrefix = "databaseShadowLog";
00063 ParamName Database::paramTxnLogPrefix = "databaseTxnLog";
00064 ParamName Database::paramInitSizeSuffix = "InitSize";
00065 ParamName Database::paramMaxSizeSuffix = "MaxSize";
00066 ParamName Database::paramIncSizeSuffix = "IncrementSize";
00067
00068 ParamVal Database::valLogAllocLinear = "linear";
00069 ParamVal Database::valLogAllocCircular = "circular";
00070
00071 const SegmentId Database::DEFAULT_DATA_SEGMENT_ID = SegmentId(1);
00072 const SegmentId Database::TEMP_SEGMENT_ID = SegmentId(2);
00073
00074
00075
00076 SharedDatabase Database::newDatabase(
00077 SharedCache pCacheInit,
00078 ConfigMap const &configMapInit,
00079 DeviceMode openModeInit,
00080 SharedTraceTarget pTraceTarget,
00081 SharedPseudoUuidGenerator pUuidGenerator)
00082 {
00083 if (!pUuidGenerator) {
00084 pUuidGenerator.reset(new PseudoUuidGenerator());
00085 }
00086 SharedDatabase pDb =
00087 SharedDatabase(
00088 new Database(
00089 pCacheInit, configMapInit, openModeInit, pTraceTarget,
00090 pUuidGenerator),
00091 ClosableObjectDestructor());
00092 pDb->init();
00093 return pDb;
00094 }
00095
00096 Database::Database(
00097 SharedCache pCacheInit,
00098 ConfigMap const &configMapInit,
00099 DeviceMode openModeInit,
00100 SharedTraceTarget pTraceTarget,
00101 SharedPseudoUuidGenerator pUuidGeneratorInit)
00102 : TraceSource(pTraceTarget,"database"),
00103 pCache(pCacheInit),
00104 configMap(configMapInit),
00105 pUuidGenerator(pUuidGeneratorInit)
00106 {
00107 openMode = openModeInit;
00108 disableDeallocateOld = false;
00109 }
00110
00111 void Database::init()
00112 {
00113 forceTxns = configMap.getBoolParam(paramForceTxns);
00114 disableSnapshots = configMap.getBoolParam(paramDisableSnapshots);
00115
00116
00117
00118 std::string resourceDir = configMap.getStringParam(paramResourceDir);
00119 if (resourceDir == "") {
00120
00121
00122
00123
00124 assert(getenv("FENNEL_HOME"));
00125 } else {
00126 FennelResource::setResourceFileLocation(resourceDir);
00127 }
00128 FennelResource::instance();
00129
00130 dataDeviceId = DeviceId(1);
00131 shadowDeviceId = DeviceId(2);
00132 txnLogDeviceId = DeviceId(3);
00133 tempDeviceId = DeviceId(4);
00134
00135 headerPageId1 = Segment::getLinearPageId(0);
00136 headerPageId2 = Segment::getLinearPageId(1);
00137
00138 header.versionNumber = SegVersionNum(0);
00139 recoveryRequired = false;
00140
00141
00142
00143 header.magicNumber = DatabaseHeader::MAGIC_NUMBER;
00144
00145
00146
00147 std::string databaseDir = configMap.getStringParam(paramDatabaseDir);
00148
00149 assert(databaseDir != "");
00150
00151 dataDeviceName = databaseDir + "/db.dat";
00152 shadowDeviceName = databaseDir + "/shadowlog.dat";
00153 txnLogDeviceName = databaseDir + "/txnlog.dat";
00154 tempDeviceName = databaseDir + "/temp.dat";
00155
00156 nCheckpoints = nCheckpointsStat = 0;
00157
00158 pSegmentFactory = SegmentFactory::newSegmentFactory(
00159 configMap,getSharedTraceTarget());
00160
00161 if (!openMode.create) {
00162
00163 assert(FileSystem::doesFileExist(dataDeviceName.c_str()));
00164 if (FileSystem::doesFileExist(shadowDeviceName.c_str())) {
00165 prepareForRecovery();
00166 return;
00167 }
00168 }
00169
00170 openSegments();
00171 }
00172
00173 void Database::prepareForRecovery()
00174 {
00175 FENNEL_TRACE(TRACE_WARNING, "recovery required");
00176 recoveryRequired = true;
00177 createTempSegment();
00178 LinearDeviceSegmentParams dataDeviceParams;
00179 createDataDevice(dataDeviceParams);
00180 loadHeader(true);
00181 writeHeader();
00182 SharedSegment pShadowLogSegment = createShadowLog(openMode);
00183 createDataSegment(pShadowLogSegment,dataDeviceParams);
00184 }
00185
00186 void Database::openSegments()
00187 {
00188 #ifdef NDEBUG
00189 FENNEL_TRACE(TRACE_INFO, "Fennel build: --with-optimization");
00190 #else
00191 FENNEL_TRACE(TRACE_INFO, "Fennel build: --without-optimization");
00192 #endif
00193 FENNEL_TRACE(TRACE_INFO, "opening database; process ID = " << getpid());
00194
00195 pCheckpointThread = SharedCheckpointThread(
00196 new CheckpointThread(*this),
00197 ClosableObjectDestructor());
00198
00199 createTempSegment();
00200
00201 pUuidGenerator->generateUuid(header.onlineUuid);
00202 FENNEL_TRACE(TRACE_INFO, "online UUID = " << header.onlineUuid);
00203
00204 DeviceMode txnLogMode = openMode;
00205 txnLogMode.create = true;
00206 txnLogMode.direct = true;
00207 createTxnLog(txnLogMode);
00208
00209 LinearDeviceSegmentParams dataDeviceParams;
00210 createDataDevice(dataDeviceParams);
00211
00212 if (openMode.create) {
00213
00214 allocateHeader();
00215 } else {
00216 PseudoUuid newOnlineUuid = header.onlineUuid;
00217 loadHeader(false);
00218
00219
00220 header.onlineUuid = newOnlineUuid;
00221 }
00222
00223 DeviceMode shadowMode = openMode;
00224 shadowMode.create = true;
00225 shadowMode.direct = true;
00226
00227 SharedSegment pShadowLogSegment = createShadowLog(shadowMode);
00228 createDataSegment(pShadowLogSegment, dataDeviceParams);
00229
00230 FENNEL_TRACE(
00231 TRACE_INFO,
00232 "database opened; page version = "
00233 << header.versionNumber);
00234
00235 if (!openMode.create) {
00236 checkpointImpl();
00237 }
00238
00239 pCheckpointThread->start();
00240 }
00241
00242 Database::~Database()
00243 {
00244 }
00245
00246 void Database::closeImpl()
00247 {
00248 FENNEL_TRACE(
00249 TRACE_INFO,
00250 "closing database");
00251 if (pCheckpointThread) {
00252 pCheckpointThread->close();
00253 }
00254
00255
00256 if (pDataSegment && areSnapshotsEnabled()) {
00257 VersionedRandomAllocationSegment *pVersionedRandomSegment =
00258 SegmentFactory::dynamicCast<VersionedRandomAllocationSegment *>(
00259 pDataSegment);
00260 pVersionedRandomSegment->freeTempPages();
00261 }
00262
00263
00264 if (pTempSegment) {
00265 assert(pTempSegment->getAllocatedSizeInPages() == 0);
00266 }
00267
00268 if (isRecoveryRequired()) {
00269 closeDevices();
00270 } else {
00271
00272
00273
00274 if (pTxnLog && pDataSegment) {
00275 checkpointImpl();
00276 }
00277 closeDevices();
00278 deleteLogs();
00279 }
00280 FENNEL_TRACE(
00281 TRACE_INFO,
00282 "database closed; page version = "
00283 << header.versionNumber);
00284 }
00285
00286 void Database::closeDevices()
00287 {
00288 pVersionedSegment = NULL;
00289 pTxnLog.reset();
00290
00291
00292
00293 if (pDataSegment) {
00294 pDataSegment->close();
00295 pDataSegment.reset();
00296 }
00297 pHeaderSegment.reset();
00298 if (pTempSegment) {
00299 pTempSegment->close();
00300 pTempSegment.reset();
00301 }
00302
00303
00304
00305 if (pCache) {
00306 if (pCache->getDevice(txnLogDeviceId)) {
00307 pCache->unregisterDevice(txnLogDeviceId);
00308 }
00309 if (pCache->getDevice(shadowDeviceId)) {
00310 pCache->unregisterDevice(shadowDeviceId);
00311 }
00312 if (pCache->getDevice(dataDeviceId)) {
00313 pCache->unregisterDevice(dataDeviceId);
00314 }
00315 if (pCache->getDevice(tempDeviceId)) {
00316 pCache->unregisterDevice(tempDeviceId);
00317 }
00318 }
00319 }
00320
00321 void Database::deleteLogs()
00322 {
00323 FileSystem::remove(shadowDeviceName.c_str());
00324 FileSystem::remove(txnLogDeviceName.c_str());
00325
00326
00327
00328
00329 directory_iterator end_itr;
00330 for (directory_iterator itr(current_path()); itr != end_itr; ++itr) {
00331 std::string filename = itr->path().filename();
00332
00333
00334 if (filename.length() < 4) {
00335 continue;
00336 }
00337 if (filename.substr(0, 3) != "txn") {
00338 continue;
00339 }
00340 if (filename.substr(filename.length() - 4, 4) != ".dat") {
00341 continue;
00342 }
00343 FileSystem::remove(filename.c_str());
00344 }
00345 }
00346
00347 SharedSegment Database::createTxnLogSegment(
00348 DeviceMode txnLogMode,PageId oldestPageId)
00349 {
00350 SharedRandomAccessDevice pTxnLogDevice(
00351 new RandomAccessFileDevice(txnLogDeviceName,txnLogMode));
00352 pCache->registerDevice(txnLogDeviceId,pTxnLogDevice);
00353
00354 LinearDeviceSegmentParams deviceParams;
00355 readDeviceParams(paramTxnLogPrefix,txnLogMode,deviceParams);
00356 CompoundId::setDeviceId(deviceParams.firstBlockId,txnLogDeviceId);
00357 CompoundId::setBlockNum(deviceParams.firstBlockId,0);
00358 if (forceTxns) {
00359 deviceParams.nPagesAllocated = 0;
00360 } else {
00361 deviceParams.nPagesAllocated = MAXU;
00362 deviceParams.nPagesIncrement = 0;
00363 deviceParams.nPagesMax = deviceParams.nPagesMin;
00364 }
00365
00366 SharedSegment pLinearSegment =
00367 pSegmentFactory->newLinearDeviceSegment(
00368 pCache,
00369 deviceParams);
00370
00371 SharedSegment pTxnLogSegment;
00372 if (forceTxns) {
00373 pTxnLogSegment = pLinearSegment;
00374 } else {
00375 pTxnLogSegment = pSegmentFactory->newCircularSegment(
00376 pLinearSegment,pCheckpointThread,oldestPageId);
00377 }
00378
00379 return pTxnLogSegment;
00380 }
00381
00382 void Database::createTxnLog(DeviceMode txnLogMode)
00383 {
00384 SharedSegment pTxnLogSegment = createTxnLogSegment(
00385 txnLogMode,NULL_PAGE_ID);
00386 SegmentAccessor segmentAccessor(pTxnLogSegment,pCache);
00387 pTxnLog = LogicalTxnLog::newLogicalTxnLog(
00388 segmentAccessor,
00389 header.onlineUuid,
00390 pSegmentFactory);
00391 pTxnLog->checkpoint(header.txnLogCheckpointMemento);
00392 }
00393
00394 SharedSegment Database::createShadowLog(DeviceMode shadowLogMode)
00395 {
00396 SharedRandomAccessDevice pShadowDevice(
00397 new RandomAccessFileDevice(shadowDeviceName,shadowLogMode));
00398 pCache->registerDevice(shadowDeviceId,pShadowDevice);
00399
00400 LinearDeviceSegmentParams deviceParams;
00401 readDeviceParams(paramShadowLogPrefix,shadowLogMode,deviceParams);
00402 CompoundId::setDeviceId(deviceParams.firstBlockId,shadowDeviceId);
00403 CompoundId::setBlockNum(deviceParams.firstBlockId,0);
00404
00405 if (forceTxns) {
00406 if (shadowLogMode.create) {
00407
00408 deviceParams.nPagesAllocated = 0;
00409 } else {
00410
00411
00412 deviceParams.nPagesAllocated = MAXU;
00413 }
00414 } else {
00415 deviceParams.nPagesAllocated = MAXU;
00416 deviceParams.nPagesIncrement = 0;
00417 deviceParams.nPagesMax = deviceParams.nPagesMin;
00418 }
00419
00420 SharedSegment pShadowSegment =
00421 pSegmentFactory->newLinearDeviceSegment(
00422 pCache,
00423 deviceParams);
00424
00425 PageId oldestPageId = NULL_PAGE_ID;
00426 if (!shadowLogMode.create) {
00427 oldestPageId = header.shadowRecoveryPageId;
00428 }
00429
00430 if (!forceTxns) {
00431 pShadowSegment = pSegmentFactory->newCircularSegment(
00432 pShadowSegment,
00433 pCheckpointThread,
00434 oldestPageId);
00435 }
00436
00437 return pSegmentFactory->newWALSegment(pShadowSegment);
00438 }
00439
00440 void Database::createDataDevice(LinearDeviceSegmentParams &deviceParams)
00441 {
00442 readDeviceParams(paramDatabasePrefix,openMode,deviceParams);
00443
00444 FileSize initialSize = FileSize(0);
00445 if (shouldForceTxns()) {
00446
00447 initialSize = (deviceParams.nPagesMin + 2) * pCache->getPageSize();
00448 }
00449
00450 pDataDevice =
00451 SharedRandomAccessDevice(
00452 new RandomAccessFileDevice(
00453 dataDeviceName,
00454 openMode,
00455 initialSize));
00456 pCache->registerDevice(dataDeviceId,pDataDevice);
00457 }
00458
00459 void Database::createDataSegment(
00460 SharedSegment pShadowLogSegment,
00461 LinearDeviceSegmentParams &deviceParams)
00462 {
00463
00464 CompoundId::setDeviceId(deviceParams.firstBlockId,dataDeviceId);
00465 CompoundId::setBlockNum(deviceParams.firstBlockId,2);
00466
00467 deviceParams.nPagesAllocated = MAXU;
00468
00469 SharedSegment pDataDeviceSegment =
00470 pSegmentFactory->newLinearDeviceSegment(
00471 pCache,
00472 deviceParams);
00473
00474 SharedSegment pVersionedDataSegment =
00475 pSegmentFactory->newVersionedSegment(
00476 pDataDeviceSegment,
00477 pShadowLogSegment,
00478 header.onlineUuid,
00479 header.versionNumber + (recoveryRequired ? 0 : 1));
00480
00481 pVersionedSegment = SegmentFactory::dynamicCast<VersionedSegment *>(
00482 pVersionedDataSegment);
00483
00484
00485
00486 if (areSnapshotsEnabled()) {
00487 pDataSegment =
00488 pSegmentFactory->newVersionedRandomAllocationSegment(
00489 pVersionedDataSegment,
00490 pTempSegment,
00491 openMode.create,
00492 recoveryRequired);
00493 } else {
00494 pDataSegment =
00495 pSegmentFactory->newRandomAllocationSegment(
00496 pVersionedDataSegment,
00497 openMode.create,
00498 recoveryRequired);
00499 }
00500 }
00501
00502 void Database::createTempSegment()
00503 {
00504
00505
00506
00507 DeviceMode tempMode = openMode;
00508 tempMode.create = !FileSystem::doesFileExist(tempDeviceName.c_str());
00509
00510 LinearDeviceSegmentParams deviceParams;
00511 readDeviceParams(paramTempPrefix,tempMode,deviceParams);
00512 FileSize initialSize = FileSize(0);
00513 if (shouldForceTxns()) {
00514 initialSize = deviceParams.nPagesMin * pCache->getPageSize();
00515 }
00516
00517 SharedRandomAccessDevice pTempDevice(
00518 new RandomAccessFileDevice(tempDeviceName,tempMode,initialSize));
00519 pCache->registerDevice(tempDeviceId,pTempDevice);
00520
00521
00522 tempMode.create = false;
00523
00524
00525 CompoundId::setDeviceId(deviceParams.firstBlockId,tempDeviceId);
00526 CompoundId::setBlockNum(deviceParams.firstBlockId,0);
00527
00528 SharedSegment pTempDeviceSegment =
00529 pSegmentFactory->newLinearDeviceSegment(
00530 pCache,
00531 deviceParams);
00532
00533
00534 pTempSegment =
00535 pSegmentFactory->newRandomAllocationSegment(
00536 pTempDeviceSegment,
00537 true);
00538 }
00539
00540 const ConfigMap& Database::getConfigMap() const
00541 {
00542 return configMap;
00543 }
00544
00545 SharedCache Database::getCache() const
00546 {
00547 return pCache;
00548 }
00549
00550 SharedSegment Database::getDataSegment() const
00551 {
00552 return pDataSegment;
00553 }
00554
00555 SharedSegment Database::getTempSegment() const
00556 {
00557 return pTempSegment;
00558 }
00559
00560 SharedCheckpointThread Database::getCheckpointThread() const
00561 {
00562 return pCheckpointThread;
00563 }
00564
00565 SharedSegment Database::getSegmentById(
00566 SegmentId segmentId,
00567 SharedSegment pDataSegment)
00568 {
00569 if (segmentId == TEMP_SEGMENT_ID) {
00570 return getTempSegment();
00571 } else {
00572 assert(segmentId == DEFAULT_DATA_SEGMENT_ID);
00573 if (pDataSegment) {
00574 return pDataSegment;
00575 } else {
00576 return getDataSegment();
00577 }
00578 }
00579 }
00580
00581 SharedSegmentFactory Database::getSegmentFactory() const
00582 {
00583 return pSegmentFactory;
00584 }
00585
00586 SharedLogicalTxnLog Database::getTxnLog() const
00587 {
00588 return pTxnLog;
00589 }
00590
00591 void Database::allocateHeader()
00592 {
00593 LinearDeviceSegmentParams deviceParams;
00594 CompoundId::setDeviceId(deviceParams.firstBlockId,dataDeviceId);
00595 CompoundId::setBlockNum(deviceParams.firstBlockId,0);
00596 pHeaderSegment =
00597 pSegmentFactory->newLinearDeviceSegment(
00598 pCache,
00599 deviceParams);
00600
00601 SegmentAccessor segmentAccessor(pHeaderSegment,pCache);
00602 DatabaseHeaderPageLock headerPageLock(segmentAccessor);
00603
00604 PageId pageId;
00605 pTxnLog->setNextTxnId(FIRST_TXN_ID);
00606 pageId = headerPageLock.allocatePage();
00607 assert(pageId == headerPageId1);
00608 headerPageLock.getNodeForWrite() = header;
00609 pageId = headerPageLock.allocatePage();
00610 assert(pageId == headerPageId2);
00611 headerPageLock.getNodeForWrite() = header;
00612 headerPageLock.unlock();
00613 pHeaderSegment->checkpoint();
00614 }
00615
00616 void Database::loadHeader(bool recovery)
00617 {
00618 LinearDeviceSegmentParams deviceParams;
00619 CompoundId::setDeviceId(deviceParams.firstBlockId,dataDeviceId);
00620 CompoundId::setBlockNum(deviceParams.firstBlockId,0);
00621 deviceParams.nPagesAllocated = 2;
00622 deviceParams.nPagesMax = 2;
00623 pHeaderSegment =
00624 pSegmentFactory->newLinearDeviceSegment(
00625 pCache,
00626 deviceParams);
00627
00628 SegmentAccessor segmentAccessor(pHeaderSegment,pCache);
00629 DatabaseHeaderPageLock headerPageLock1(segmentAccessor);
00630 headerPageLock1.lockShared(headerPageId1);
00631 if (!headerPageLock1.checkMagicNumber()) {
00632 throw DataFormatExcn();
00633 }
00634
00635 DatabaseHeaderPageLock headerPageLock2(segmentAccessor);
00636 headerPageLock2.lockShared(headerPageId2);
00637 if (!headerPageLock2.checkMagicNumber()) {
00638 throw DataFormatExcn();
00639 }
00640
00641 DatabaseHeader const &header1 = headerPageLock1.getNodeForRead();
00642 DatabaseHeader const &header2 = headerPageLock2.getNodeForRead();
00643 if (recovery) {
00644
00645 if (header2.versionNumber < header1.versionNumber) {
00646 header = header2;
00647 } else {
00648 header = header1;
00649 }
00650 } else {
00651 assert(header1.versionNumber == header2.versionNumber);
00652
00653 header = header1;
00654 }
00655 if (pTxnLog) {
00656 TxnId nextTxnId = header.txnLogCheckpointMemento.nextTxnId;
00657 pTxnLog->setNextTxnId(nextTxnId);
00658 }
00659 }
00660
00661 TxnId Database::getLastCommittedTxnId()
00662 {
00663 return header.txnLogCheckpointMemento.nextTxnId - 1;
00664 }
00665
00666 void Database::checkpointImpl(CheckpointType checkpointType)
00667 {
00668 assert(!isRecoveryRequired());
00669
00670 if (checkpointType == CHECKPOINT_DISCARD) {
00671 recoveryRequired = true;
00672
00673
00674
00675 header.shadowRecoveryPageId =
00676 pVersionedSegment->getOnlineRecoveryPageId();
00677 pDataSegment->checkpoint(checkpointType);
00678 if (!forceTxns) {
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689 LogicalTxnLogCheckpointMemento crashMemento;
00690 pTxnLog->checkpoint(crashMemento,checkpointType);
00691 }
00692 return;
00693 }
00694
00695 header.versionNumber = pVersionedSegment->getVersionNumber();
00696 if (checkpointType == CHECKPOINT_FLUSH_FUZZY) {
00697 header.versionNumber--;
00698 }
00699 pDataSegment->checkpoint(checkpointType);
00700 header.shadowRecoveryPageId = pVersionedSegment->getRecoveryPageId();
00701 pTxnLog->checkpoint(
00702 header.txnLogCheckpointMemento,
00703 checkpointType);
00704 writeHeader();
00705 pVersionedSegment->deallocateCheckpointedLog(checkpointType);
00706 pTxnLog->deallocateCheckpointedLog(
00707 header.txnLogCheckpointMemento,checkpointType);
00708
00709 StrictMutexGuard mutexGuard(mutex);
00710
00711
00712 ++nCheckpointsStat;
00713 ++nCheckpoints;
00714 condition.notify_all();
00715 }
00716
00717 void Database::requestCheckpoint(CheckpointType checkpointType,bool async)
00718 {
00719 StrictMutexGuard mutexGuard(mutex);
00720 uint nCheckpointsBefore = nCheckpoints;
00721 mutexGuard.unlock();
00722
00723 if (forceTxns && (checkpointType == CHECKPOINT_FLUSH_FUZZY)) {
00724
00725
00726 checkpointType = CHECKPOINT_FLUSH_ALL;
00727 }
00728
00729 pCheckpointThread->requestCheckpoint(checkpointType);
00730
00731 if (async) {
00732 return;
00733 }
00734
00735 mutexGuard.lock();
00736 while (nCheckpoints == nCheckpointsBefore) {
00737 condition.wait(mutexGuard);
00738 }
00739 }
00740
00741 void Database::writeHeader()
00742 {
00743
00744
00745
00746
00747
00748
00749 SegmentAccessor segmentAccessor(pHeaderSegment,pCache);
00750 DatabaseHeaderPageLock headerPageLock(segmentAccessor);
00751
00752 headerPageLock.lockExclusive(headerPageId1);
00753 headerPageLock.getNodeForWrite() = header;
00754 pCache->flushPage(headerPageLock.getPage(),false);
00755
00756 headerPageLock.lockExclusive(headerPageId2);
00757 headerPageLock.getNodeForWrite() = header;
00758 pCache->flushPage(headerPageLock.getPage(),false);
00759 }
00760
00761 void Database::recoverOnline()
00762 {
00763
00764
00765
00766
00767
00768
00769 assert(forceTxns);
00770 header.shadowRecoveryPageId =
00771 pVersionedSegment->getOnlineRecoveryPageId();
00772 pVersionedSegment->prepareOnlineRecovery();
00773 recoveryRequired = true;
00774
00775
00776 recoverPhysical(CHECKPOINT_FLUSH_ALL);
00777
00778
00779 checkpointImpl(CHECKPOINT_FLUSH_ALL);
00780 }
00781
00782 void Database::recover(
00783 LogicalTxnParticipantFactory &txnParticipantFactory)
00784 {
00785 recoverPhysical(CHECKPOINT_FLUSH_AND_UNMAP);
00786
00787
00788
00789
00790
00791 SharedSegment pTxnLogSegment = createTxnLogSegment(
00792 openMode,
00793 CompoundId::getPageId(
00794 header.txnLogCheckpointMemento.logPosition.segByteId));
00795
00796 SegmentAccessor logSegmentAccessor(pTxnLogSegment,pCache);
00797
00798 SharedLogicalRecoveryLog pRecoveryLog =
00799 LogicalRecoveryLog::newLogicalRecoveryLog(
00800 txnParticipantFactory,
00801 logSegmentAccessor,
00802 header.onlineUuid,
00803 pSegmentFactory);
00804 logSegmentAccessor.reset();
00805
00806 pRecoveryLog->recover(header.txnLogCheckpointMemento);
00807 assert(pRecoveryLog.unique());
00808 pRecoveryLog.reset();
00809 assert(pTxnLogSegment.unique());
00810 pTxnLogSegment.reset();
00811
00812 closeDevices();
00813 deleteLogs();
00814 FENNEL_TRACE(TRACE_INFO, "recovery completed");
00815
00816 openSegments();
00817 }
00818
00819 void Database::recoverPhysical(CheckpointType checkpointType)
00820 {
00821 assert(!openMode.create);
00822 assert(isRecoveryRequired());
00823 recoveryRequired = false;
00824
00825 FENNEL_TRACE(
00826 TRACE_INFO,
00827 "recovery beginning; page version = "
00828 << header.versionNumber);
00829
00830 if (header.shadowRecoveryPageId != NULL_PAGE_ID) {
00831 pVersionedSegment->recover(
00832 pDataSegment,
00833 header.shadowRecoveryPageId,
00834 header.versionNumber);
00835 pDataSegment->checkpoint(checkpointType);
00836 header.versionNumber = pVersionedSegment->getVersionNumber();
00837 header.shadowRecoveryPageId = NULL_PAGE_ID;
00838 writeHeader();
00839 pVersionedSegment->deallocateCheckpointedLog(
00840 CHECKPOINT_FLUSH_AND_UNMAP);
00841 }
00842 }
00843
00844 bool Database::isRecoveryRequired() const
00845 {
00846 return recoveryRequired;
00847 }
00848
00849 void Database::readDeviceParams(
00850 std::string paramNamePrefix,
00851 DeviceMode deviceMode,
00852 LinearDeviceSegmentParams &deviceParams)
00853 {
00854 deviceParams.nPagesMin = configMap.getIntParam(
00855 paramNamePrefix + paramInitSizeSuffix);
00856 if (configMap.isParamSet(paramNamePrefix + paramIncSizeSuffix)) {
00857 deviceParams.nPagesIncrement = configMap.getIntParam(
00858 paramNamePrefix + paramIncSizeSuffix);
00859 }
00860 deviceParams.nPagesMax = configMap.getIntParam(
00861 paramNamePrefix + paramMaxSizeSuffix);
00862 if (!deviceParams.nPagesMax) {
00863 deviceParams.nPagesMax = MAXU;
00864 }
00865 if (deviceMode.create) {
00866 deviceParams.nPagesAllocated = 0;
00867 } else {
00868 deviceParams.nPagesAllocated = MAXU;
00869 }
00870 }
00871
00872 StoredTypeDescriptorFactory const &Database::getTypeFactory() const
00873 {
00874 return typeFactory;
00875 }
00876
00877 void Database::writeStats(StatsTarget &target)
00878 {
00879 pCache->writeStats(target);
00880
00881 StrictMutexGuard mutexGuard(mutex);
00882 target.writeCounter(
00883 "DatabaseCheckpoints", nCheckpointsStat);
00884 target.writeCounter(
00885 "DatabaseCheckpointsSinceInit", nCheckpoints);
00886 if (pDataSegment) {
00887 target.writeCounter(
00888 "DatabasePagesAllocated", pDataSegment->getAllocatedSizeInPages());
00889
00890 target.writeCounter(
00891 "DatabasePagesOccupiedHighWaterSinceInit",
00892 pDataSegment->getNumPagesOccupiedHighWater() + 2);
00893 target.writeCounter(
00894 "DatabasePagesExtendedSinceInit",
00895 pDataSegment->getNumPagesExtended());
00896 }
00897 if (pTempSegment) {
00898 target.writeCounter(
00899 "TempPagesAllocated", pTempSegment->getAllocatedSizeInPages());
00900 target.writeCounter(
00901 "TempPagesOccupiedHighWaterSinceInit",
00902 pTempSegment->getNumPagesOccupiedHighWater());
00903 target.writeCounter(
00904 "TempPagesExtendedSinceInit",
00905 pTempSegment->getNumPagesExtended());
00906 }
00907 nCheckpointsStat = 0;
00908 }
00909
00910 bool Database::shouldForceTxns() const
00911 {
00912 return forceTxns;
00913 }
00914
00915 bool Database::areSnapshotsEnabled() const
00916 {
00917 return (forceTxns && !disableSnapshots);
00918 }
00919
00920 void Database::deallocateOldPages(TxnId oldestLabelCsn)
00921 {
00922 uint iSegAlloc = 0;
00923 ExtentNum extentNum = 0;
00924
00925
00926
00927
00928
00929
00930
00931 uint numPages = 100;
00932
00933
00934 TxnId oldestActiveTxnId = pTxnLog->getOldestActiveTxnId();
00935
00936
00937
00938
00939
00940 TxnId oldestTxnId;
00941 if (oldestLabelCsn == NULL_TXN_ID) {
00942 oldestTxnId = oldestActiveTxnId;
00943 } else {
00944 oldestTxnId = std::min(oldestActiveTxnId, oldestLabelCsn + 1);
00945 }
00946
00947
00948
00949
00950
00951
00952 PageSet oldPageSet;
00953 VersionedRandomAllocationSegment *pVersionedRandomSegment =
00954 SegmentFactory::dynamicCast<VersionedRandomAllocationSegment *>(
00955 pDataSegment);
00956 bool morePages = true;
00957 do {
00958 morePages =
00959 pVersionedRandomSegment->getOldPageIds(
00960 iSegAlloc,
00961 extentNum,
00962 oldestTxnId,
00963 numPages,
00964 oldPageSet);
00965
00966
00967
00968 if (!oldPageSet.empty()) {
00969 SXMutexSharedGuard actionMutexGuard(
00970 pCheckpointThread->getActionMutex());
00971 if (disableDeallocateOld) {
00972 return;
00973 }
00974 pVersionedRandomSegment->deallocateOldPages(
00975 oldPageSet,
00976 oldestTxnId);
00977
00978 actionMutexGuard.unlock();
00979 requestCheckpoint(CHECKPOINT_FLUSH_ALL, false);
00980 oldPageSet.clear();
00981 }
00982 } while (morePages);
00983 }
00984
00985 TxnId Database::initiateBackup(
00986 const std::string &backupFilePathname,
00987 bool checkSpaceRequirements,
00988 FileSize spacePadding,
00989 TxnId lowerBoundCsn,
00990 const std::string &compressionProgram,
00991 FileSize &dataDeviceSize,
00992 const volatile bool &aborted)
00993 {
00994 FENNEL_TRACE(TRACE_FINE, "Started Fennel metadata backup");
00995
00996
00997 if (!areSnapshotsEnabled()) {
00998 throw FennelExcn(
00999 FennelResource::instance().unsupportedOperation("System backup"));
01000 }
01001
01002
01003
01004 SXMutexSharedGuard actionMutexGuard(pCheckpointThread->getActionMutex());
01005
01006
01007 assert(!disableDeallocateOld);
01008 assert(pBackupRestoreDevice == NULL);
01009
01010
01011
01012
01013
01014
01015 TxnId upperBoundCsn = getLastCommittedTxnId();
01016
01017 disableDeallocateOld = true;
01018 dataDeviceSize = pDataDevice->getSizeInBytes();
01019
01020
01021
01022
01023
01024 uint nScratchPages, rate;
01025 pCache->getPrefetchParams(nScratchPages, rate);
01026
01027 scratchAccessor = pSegmentFactory->newScratchSegment(pCache);
01028 pBackupRestoreDevice =
01029 SegPageBackupRestoreDevice::newSegPageBackupRestoreDevice(
01030 backupFilePathname,
01031 #ifdef __MSVC__
01032 "wb",
01033 #else
01034 "w",
01035 #endif
01036 compressionProgram,
01037 nScratchPages,
01038 2,
01039 scratchAccessor,
01040 pCache->getDeviceAccessScheduler(*pDataDevice),
01041 pDataDevice);
01042 VersionedRandomAllocationSegment *pVRSegment =
01043 SegmentFactory::dynamicCast<VersionedRandomAllocationSegment *>(
01044 pDataSegment);
01045
01046 try {
01047 pBackupRestoreDevice->backupPage(
01048 pHeaderSegment->translatePageId(headerPageId1));
01049 pBackupRestoreDevice->backupPage(
01050 pHeaderSegment->translatePageId(headerPageId2));
01051
01052
01053 pBackupRestoreDevice->waitForPendingWrites();
01054 BlockNum nDataPages =
01055 pVRSegment->backupAllocationNodes(
01056 pBackupRestoreDevice,
01057 checkSpaceRequirements,
01058 lowerBoundCsn,
01059 upperBoundCsn,
01060 aborted);
01061
01062
01063
01064 if (checkSpaceRequirements) {
01065 FileSize spaceAvailable;
01066 FileSystem::getDiskFreeSpace(
01067 backupFilePathname.c_str(),
01068 spaceAvailable);
01069 FileSize spaceRequired =
01070 nDataPages * pDataSegment->getFullPageSize();
01071
01072
01073 if (compressionProgram.length() != 0) {
01074 spaceRequired /= 5;
01075 }
01076 spaceRequired += spacePadding;
01077 if (spaceAvailable < spaceRequired) {
01078 throw FennelExcn(FennelResource::instance().outOfBackupSpace());
01079 }
01080 }
01081 } catch (...) {
01082 cleanupBackupRestore(true);
01083
01084 if (aborted) {
01085 FENNEL_TRACE(TRACE_FINE, "abort detected");
01086 throw AbortExcn();
01087 } else {
01088 throw;
01089 }
01090 }
01091
01092 FENNEL_TRACE(TRACE_FINE, "Finished Fennel metadata backup");
01093 return upperBoundCsn;
01094 }
01095
01096 void Database::completeBackup(
01097 TxnId lowerBoundCsn,
01098 TxnId upperBoundCsn,
01099 const volatile bool &aborted)
01100 {
01101 FENNEL_TRACE(TRACE_FINE, "Started Fennel data page backup");
01102 assert(disableDeallocateOld);
01103 assert(pBackupRestoreDevice != NULL);
01104
01105 VersionedRandomAllocationSegment *pVRSegment =
01106 SegmentFactory::dynamicCast<VersionedRandomAllocationSegment *>(
01107 pDataSegment);
01108 try {
01109 pVRSegment->backupDataPages(
01110 pBackupRestoreDevice,
01111 lowerBoundCsn,
01112 upperBoundCsn,
01113 aborted);
01114 cleanupBackupRestore(true);
01115 } catch (...) {
01116 cleanupBackupRestore(true);
01117
01118 if (aborted) {
01119 FENNEL_TRACE(TRACE_FINE, "abort detected");
01120 throw AbortExcn();
01121 } else {
01122 throw;
01123 }
01124 }
01125
01126 FENNEL_TRACE(TRACE_FINE, "Finished Fennel data page backup");
01127 }
01128
01129 void Database::abortBackup()
01130 {
01131 FENNEL_TRACE(TRACE_FINE, "Aborting Fennel backup");
01132 cleanupBackupRestore(true);
01133 }
01134
01135 void Database::restoreFromBackup(
01136 const std::string &backupFilePathname,
01137 FileSize newSize,
01138 const std::string &compressionProgram,
01139 TxnId lowerBoundCsn,
01140 TxnId upperBoundCsn,
01141 const volatile bool &aborted)
01142 {
01143 FENNEL_TRACE(TRACE_FINE, "Started Fennel restore");
01144
01145
01146 if (!areSnapshotsEnabled()) {
01147 throw FennelExcn(
01148 FennelResource::instance().unsupportedOperation("System restore"));
01149 }
01150
01151
01152
01153 if (lowerBoundCsn != NULL_TXN_ID) {
01154 TxnId headerTxnId = getLastCommittedTxnId();
01155 if (headerTxnId != lowerBoundCsn) {
01156 throw FennelExcn(
01157 FennelResource::instance().mismatchedRestore());
01158 }
01159 }
01160
01161 pDataDevice->setSizeInBytes(newSize);
01162
01163 VersionedRandomAllocationSegment *pVRSegment =
01164 SegmentFactory::dynamicCast<VersionedRandomAllocationSegment *>(
01165 pDataSegment);
01166
01167 uint nScratchPages, rate;
01168 pCache->getPrefetchParams(nScratchPages, rate);
01169
01170 scratchAccessor =
01171 pSegmentFactory->newScratchSegment(pCache);
01172 pBackupRestoreDevice =
01173 SegPageBackupRestoreDevice::newSegPageBackupRestoreDevice(
01174 backupFilePathname,
01175 #ifdef __MSVC__
01176 "rb",
01177 #else
01178 "r",
01179 #endif
01180 compressionProgram,
01181 nScratchPages,
01182 0,
01183 scratchAccessor,
01184 pCache->getDeviceAccessScheduler(*pDataDevice),
01185 pDataDevice);
01186
01187
01188
01189
01190
01191 MappedPageListenerPredicate dataPredicate(*pVRSegment);
01192 pCache->checkpointPages(dataPredicate, CHECKPOINT_FLUSH_AND_UNMAP);
01193 MappedPageListenerPredicate headerPredicate(*pHeaderSegment);
01194 pCache->checkpointPages(headerPredicate, CHECKPOINT_FLUSH_AND_UNMAP);
01195
01196 try {
01197
01198 pBackupRestoreDevice->restorePage(
01199 pHeaderSegment->translatePageId(headerPageId1));
01200 pBackupRestoreDevice->restorePage(
01201 pHeaderSegment->translatePageId(headerPageId2));
01202 pVRSegment->restoreFromBackup(
01203 pBackupRestoreDevice,
01204 lowerBoundCsn,
01205 upperBoundCsn,
01206 aborted);
01207 cleanupBackupRestore(false);
01208 } catch (...) {
01209 cleanupBackupRestore(false);
01210
01211 if (aborted) {
01212 FENNEL_TRACE(TRACE_FINE, "abort detected");
01213 throw AbortExcn();
01214 } else {
01215 throw;
01216 }
01217 }
01218
01219
01220
01221
01222
01223 loadHeader(false);
01224 pVersionedSegment->recover(
01225 pDataSegment,
01226 NULL_PAGE_ID,
01227 header.versionNumber,
01228 header.onlineUuid);
01229
01230 FENNEL_TRACE(TRACE_FINE, "Finished Fennel restore");
01231 }
01232
01233 void Database::cleanupBackupRestore(bool isBackup)
01234 {
01235 if (pBackupRestoreDevice) {
01236 pBackupRestoreDevice.reset();
01237 }
01238 if (scratchAccessor.pSegment) {
01239 scratchAccessor.reset();
01240 }
01241 if (isBackup) {
01242 SXMutexSharedGuard actionMutexGuard(
01243 pCheckpointThread->getActionMutex());
01244 disableDeallocateOld = false;
01245 }
01246 }
01247
01248 FENNEL_END_CPPFILE("$Id: //open/dev/fennel/db/Database.cpp#43 $");
01249
01250