/* Copyright (C) 2014 InfiniDB, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /****************************************************************************** * $Id: vss.h 1926 2013-06-30 21:18:14Z wweeks $ * *****************************************************************************/ /** @file * class XXX interface */ #pragma once #include //#define NDEBUG #include #include #include "brmshmimpl.h" #include "brmtypes.h" #include "undoable.h" #include "mastersegmenttable.h" #include "shmkeys.h" #include "hasher.h" #ifdef NONE #undef NONE #endif #ifdef READ #undef READ #endif #ifdef WRITE #undef WRITE #endif // These config parameters need to be loaded // will get a small hash function performance boost by using powers of 2 #define VSSSTORAGE_INITIAL_COUNT 200000 #define VSSSTORAGE_INITIAL_SIZE (VSSSTORAGE_INITIAL_COUNT * sizeof(VSSEntry)) #define VSSSTORAGE_INCREMENT_COUNT 20000 #define VSSSTORAGE_INCREMENT (VSSSTORAGE_INCREMENT_COUNT * sizeof(VSSEntry)) // (average list length = 4) #define VSSTABLE_INITIAL_SIZE (50000 * sizeof(int)) #define VSSTABLE_INCREMENT (5000 * sizeof(int)) #define VSS_INITIAL_SIZE (sizeof(VSSShmsegHeader) + VSSSTORAGE_INITIAL_SIZE + VSSTABLE_INITIAL_SIZE) #define VSS_INCREMENT (VSSTABLE_INCREMENT + VSSSTORAGE_INCREMENT) #define VSS_SIZE(entries) \ ((entries * sizeof(VSSEntry)) + (entries / 4 * sizeof(int)) + sizeof(VSSShmsegHeader)) #define EXPORT namespace BRM { struct VSSEntry { LBID_t lbid; VER_t verID; bool vbFlag; bool locked; int next; #ifndef __LP64__ uint32_t pad1; #endif EXPORT VSSEntry(); }; struct VSSShmsegHeader { int capacity; int currentSize; int LWM; int numHashBuckets; int lockedEntryCount; // the rest of the overlay looks like this // int hashBuckets[numHashBuckets]; // VSSEntry storage[capacity]; }; class QueryContext_vss { public: QueryContext_vss() : currentScn(0) { txns.reset(new std::set()); } QueryContext_vss(const QueryContext& qc); VER_t currentScn; boost::shared_ptr > txns; }; class VSSImpl { public: static VSSImpl* makeVSSImpl(unsigned key, off_t size, bool readOnly = false); inline void grow(unsigned key, off_t size) #ifndef NDBUG { fVSS.grow(key, size); } #else { int rc = fVSS.grow(key, size); idbassert(rc == 0); } #endif inline void makeReadOnly() { fVSS.setReadOnly(); } inline void clear(unsigned key, off_t size) { fVSS.clear(key, size); } inline void swapout(BRMShmImpl& rhs) { fVSS.swap(rhs); rhs.destroy(); } inline unsigned key() const { return fVSS.key(); } inline VSSShmsegHeader* get() const { return reinterpret_cast(fVSS.fMapreg.get_address()); } private: VSSImpl(unsigned key, off_t size, bool readOnly = false); ~VSSImpl(); VSSImpl(const VSSImpl& rhs); VSSImpl& operator=(const VSSImpl& rhs); BRMShmImpl fVSS; static boost::mutex fInstanceMutex; static VSSImpl* fInstance; }; class VBBM; class ExtentMap; /** @brief The Version Substitution Structure (VSS) * * At a high level, the VSS maintains a table that associates an LBID with * a version number and 2 flags that indicate whether or not the block * identified by the pair exists in the main database files * or the Version Buffer. The VSS's main purpose is to resolve the version of * a specified block the caller can safely use given that there may be concurrent * writes to that block. * * As implemented, it is a hash table and a set of lists that exist in * shared memory. The hash table is keyed by LBID, and * each valid entry points to the head of a unique list. Each list element * contains the LBID, VerID, & the two flags that encapsulate "an entry in the * VSS table". Every list contains all elements that collide on that hash table * entry that points to it, "load factor" has no bearing on performance, * and lists can grow arbitrarily large. * Technically lookups are O(n), but in normal circumstances it'll * be constant time. As things are right now, we expect there to be about * 200k VSS entries. The hash table is sized such that on average there will be 4 * entries per list when it's at capacity. * * The memory management & structure manipulation code is nearly identical * to that in the VBBM, so any bugs found here are likely there as well. * * Shared memory is managed using code similar to the ExtentMap & VBBM. When * the shared memory segment needs to grow, it is write-locked, a new one * is created, the contents are reinserted to the new one, the key is * registered, and the old segment is destroyed when the last reference to it * is detached. */ class VSS : public Undoable { public: enum OPS { NONE, READ, WRITE }; EXPORT VSS(); EXPORT ~VSS(); EXPORT bool isLocked(const LBIDRange& l, VER_t txnID = -1) const; EXPORT void removeEntry(LBID_t lbid, VER_t verID, std::vector* flushList); // Note, the use_vbbm switch should be used for unit testing the VSS only EXPORT void removeEntriesFromDB(const LBIDRange& range, VBBM& vbbm, bool use_vbbm = true); EXPORT int lookup(LBID_t lbid, const QueryContext_vss&, VER_t txnID, VER_t* outVer, bool* vbFlag, bool vbOnly = false) const; /// Returns the version in the main DB files EXPORT VER_t getCurrentVersion(LBID_t lbid, bool* isLocked) const; // returns the ver in the main DB files /// Returns the highest version in the version buffer, less than max EXPORT VER_t getHighestVerInVB(LBID_t lbid, VER_t max) const; /// returns true if that block is in the version buffer, false otherwise EXPORT bool isVersioned(LBID_t lbid, VER_t version) const; EXPORT void setVBFlag(LBID_t lbid, VER_t verID, bool vbFlag); EXPORT void insert(LBID_t, VER_t, bool vbFlag, bool locked, bool loading = false); EXPORT void commit(VER_t txnID); EXPORT void getUncommittedLBIDs(VER_t txnID, std::vector& lbids); EXPORT void getUnlockedLBIDs(BlockList_t& lbids); EXPORT void getLockedLBIDs(BlockList_t& lbids); EXPORT void lock(OPS op); EXPORT void release(OPS op); EXPORT void setReadOnly(); EXPORT int checkConsistency(const VBBM& vbbm, ExtentMap& em) const; EXPORT int size() const; EXPORT bool hashEmpty() const; EXPORT void getCurrentTxnIDs(std::set& txnList) const; EXPORT void clear(); EXPORT void load(std::string filename); EXPORT void save(std::string filename); #ifdef BRM_DEBUG EXPORT int getShmid() const; #endif EXPORT bool isEmpty(bool doLock = true); /* Bug 2293. VBBM will use this fcn to determine whether a block is * currently in use. */ EXPORT bool isEntryLocked(LBID_t lbid, VER_t verID) const; EXPORT bool isTooOld(LBID_t lbid, VER_t verID) const; private: VSS(const VSS&); VSS& operator=(const VSS&); struct VSSShmsegHeader* vss; int* hashBuckets; VSSEntry* storage; bool r_only; static boost::mutex mutex; // @bug5355 - made mutex static key_t currentVSSShmkey; int vssShmid; MSTEntry* vssShminfo; MasterSegmentTable mst; static const int MAX_IO_RETRIES = 10; key_t chooseShmkey() const; void growVSS(); void growForLoad(int count); void initShmseg(); void copyVSS(VSSShmsegHeader* dest); int getIndex(LBID_t lbid, VER_t verID, int& prev, int& bucket) const; void _insert(VSSEntry& e, VSSShmsegHeader* dest, int* destTable, VSSEntry* destStorage, bool loading = false); ShmKeys fShmKeys; VSSImpl* fPVSSImpl; utils::Hasher hasher; }; } // namespace BRM #undef EXPORT