/* 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: we_extentstripealloc.cpp 4450 2013-01-21 14:13:24Z rdempsey $ * ******************************************************************************/ /** @file * Contains class to allocate a "stripe" of extents for all columns across a tbl */ #include "we_extentstripealloc.h" #include #include #include "we_define.h" #include "we_log.h" #include "we_brm.h" namespace { typedef std::tr1::unordered_multimap AllocExtMap; typedef AllocExtMap::iterator AllocExtMapIter; typedef AllocExtMap::const_iterator ConstAllocExtMapIter; } // namespace namespace WriteEngine { //------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------ ExtentStripeAlloc::ExtentStripeAlloc(OID tableOID, Log* logger) : fTableOID(tableOID), fLog(logger), fStripeCount(0) { } //------------------------------------------------------------------------------ // Destructor // Note: fMap will automatically get cleared by unordered_map destructor, // so no need to explicitly call clear() in "this" destructor. //------------------------------------------------------------------------------ ExtentStripeAlloc::~ExtentStripeAlloc() { } //------------------------------------------------------------------------------ // Add a column to be associated with the "stripe" allocations for "this" // ExtentStripeAlloc object. //------------------------------------------------------------------------------ void ExtentStripeAlloc::addColumn(OID colOID, int colWidth, datatypes::SystemCatalog::ColDataType colDataType) { boost::mutex::scoped_lock lock(fMapMutex); fColOIDs.push_back(colOID); fColWidths.push_back(colWidth); fColDataTypes.push_back(colDataType); } //------------------------------------------------------------------------------ // Allocate a "stripe" of column extents for the relevant table associated // with the specified column OID, and at the specified DBRoot. The partition // number, segment number, etc associated with the allocated extent are // returned in the output arguments. // // Note that a multimap is used for the internal extent collection, with the // column OID as the key. We would typically expect that we would not have // more than 1 extent entry for a given column OID in our map at the same // time. If this were always true, then a map (and not a multimap) would // suffice. // However, I suppose that if the read buffer were large enough, and the // column widths small enough, then a parsing thread for a read buffer could // end up asking for a "stripe" of extents "before" all the allocated extents // for the previous read buffer have been used. In this case, we may end // up needing to store more than 1 column extent for the same column, in // our internal collection. Thus a multimap is used. To handle this case, // we not only search the internal map for the specified column OID, but we // also: // 1. make sure we have an extent for the requested DBRoot // 2. select the lowest allocated stripe, if there should be more than // one column extent with the same DBRoot. //------------------------------------------------------------------------------ int ExtentStripeAlloc::allocateExtent(OID oid, uint16_t dbRoot, uint32_t& partNum, // used as input for empty DBRoot, else output only uint16_t& segNum, BRM::LBID_t& startLbid, int& allocSize, HWM& hwm, std::string& errMsg) { int retStatus = NO_ERROR; bool bFound = false; AllocExtMapIter extentEntryIter; errMsg.clear(); std::pair iters; boost::mutex::scoped_lock lock(fMapMutex); // Search for an extent matching the requested OID and DBRoot. // We also filter by selecting the lowest stripe number. See // function description that precedes this function for more detail. iters = fMap.equal_range(oid); if (iters.first != iters.second) { for (AllocExtMapIter it = iters.first; it != iters.second; ++it) { if (it->second.fDbRoot == dbRoot) { if ((!bFound) || (it->second.fStripeKey < extentEntryIter->second.fStripeKey)) { extentEntryIter = it; } bFound = true; } } } // Return selected extent if (bFound) { partNum = extentEntryIter->second.fPartNum; segNum = extentEntryIter->second.fSegNum; startLbid = extentEntryIter->second.fStartLbid; allocSize = extentEntryIter->second.fAllocSize; hwm = extentEntryIter->second.fHwm; errMsg = *extentEntryIter->second.fStatusMsg; retStatus = extentEntryIter->second.fStatus; fMap.erase(extentEntryIter); } else // Allocate "stripe" of extents if there's no entry for this column OID { fStripeCount++; std::ostringstream oss1; oss1 << "Allocating next stripe(" << fStripeCount << ") of column extents for table " << fTableOID << "; DBRoot-" << dbRoot; fLog->logMsg(oss1.str(), MSGLVL_INFO2); std::vector cols; std::vector extents; for (unsigned int j = 0; j < fColOIDs.size(); ++j) { BRM::CreateStripeColumnExtentsArgIn colEntry; colEntry.oid = fColOIDs[j]; colEntry.width = fColWidths[j]; colEntry.colDataType = fColDataTypes[j]; cols.push_back(colEntry); } uint32_t allocPartNum = partNum; uint16_t allocSegNum = 0; BRM::LBID_t allocStartLbid = 0; int allocAllocSize = 0; HWM allocHwm = 0; int allocStatus = NO_ERROR; std::string allocStatusMsg; int rc = BRMWrapper::getInstance()->allocateStripeColExtents(cols, dbRoot, allocPartNum, allocSegNum, extents); // If allocation error occurs, we go ahead and store extent entries // with error status, to satisfy subsequent allocations in same stripe. if (rc != NO_ERROR) { for (unsigned int i = 0; i < fColOIDs.size(); ++i) { if (oid != fColOIDs[i]) { allocStatus = rc; std::ostringstream oss; oss << "Previous error allocating extent stripe for " "table " << fTableOID << "; DBRoot: " << dbRoot; allocStatusMsg = oss.str(); // For error case, just store 0 for part#,segnum, etc. AllocExtEntry extentEntry(fColOIDs[i], fColWidths[i], dbRoot, 0, 0, 0, 0, 0, allocStatus, allocStatusMsg, fStripeCount); fMap.insert(AllocExtMap::value_type(fColOIDs[i], extentEntry)); } } std::ostringstream oss; oss << "Error allocating extent stripe for " "table " << fTableOID << "; DBRoot: " << dbRoot; errMsg = oss.str(); return rc; } // Save allocated extents into fMap for later use. For the OID // requested by this function call, we just return the extent info. for (unsigned int i = 0; i < fColOIDs.size(); ++i) { allocStartLbid = extents[i].startLbid; allocAllocSize = extents[i].allocSize; allocHwm = extents[i].startBlkOffset; // Might consider controlling this with debug, but we always // log out for now. // if (fLog->isDebug( DEBUG_1 )) { std::ostringstream oss; oss << "Stripe Allocation: OID-" << fColOIDs[i] << "; DBRoot-" << dbRoot << "; Part#-" << allocPartNum << "; Seg#-" << allocSegNum << "; lbid-" << allocStartLbid << "; fbo-" << allocHwm << "; nblks-" << allocAllocSize; fLog->logMsg(oss.str(), MSGLVL_INFO2); } // Assign output args for requested column OID if (oid == fColOIDs[i]) { partNum = allocPartNum; segNum = allocSegNum; startLbid = allocStartLbid; allocSize = allocAllocSize; hwm = allocHwm; } else // Add all extents in "stripe" (other than requested column) { // to the collection of extents AllocExtEntry extentEntry(fColOIDs[i], fColWidths[i], dbRoot, allocPartNum, allocSegNum, allocStartLbid, allocAllocSize, allocHwm, allocStatus, allocStatusMsg, fStripeCount); fMap.insert(AllocExtMap::value_type(fColOIDs[i], extentEntry)); } } } return retStatus; } //------------------------------------------------------------------------------ // Debug logging function to log contents of the allocated extents that are // pending. //------------------------------------------------------------------------------ void ExtentStripeAlloc::print() { boost::mutex::scoped_lock lock(fMapMutex); std::ostringstream oss; oss << "Current Pending Extents for table " << fTableOID << ":"; if (fMap.size() > 0) { for (ConstAllocExtMapIter iter = fMap.begin(); iter != fMap.end(); ++iter) { oss << std::endl; oss << " oid: " << iter->second.fOid << "; wid: " << iter->second.fColWidth << "; root: " << iter->second.fDbRoot << "; part: " << iter->second.fPartNum << "; seg: " << iter->second.fSegNum << "; lbid: " << iter->second.fStartLbid << "; size: " << iter->second.fAllocSize << "; hwm: " << iter->second.fHwm << "; stripe: " << iter->second.fStripeKey << "; stat: " << iter->second.fStatus << "; msg: " << *iter->second.fStatusMsg; } } else { oss << " "; } fLog->logMsg(oss.str(), MSGLVL_INFO2); } } // namespace WriteEngine