1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-04-20 09:07:44 +03:00
mariadb-columnstore-engine/writeengine/bulk/we_extentstripealloc.cpp
Sergey Zefirov 3bcc2e2fda
fix(memory leaks): MCOL-5791 - get rid of memory leaks in plugin code (#3365)
There were numerous memory leaks in plugin's code and associated code.
During typical run of MTR tests it leaked around 65 megabytes of
objects. As a result they may severely affect long-lived connections.

This patch fixes (almost) all leaks found in the plugin. The exceptions
are two leaks associated with SHOW CREATE TABLE columnstore_table and
getting information of columns of columnstore-handled table. These
should be fixed on the server side and work is on the way.
2024-12-06 09:04:55 +00:00

289 lines
10 KiB
C++

/* 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 <iostream>
#include <sstream>
#include "we_define.h"
#include "we_log.h"
#include "we_brm.h"
namespace
{
typedef std::tr1::unordered_multimap<WriteEngine::OID, WriteEngine::AllocExtEntry,
WriteEngine::AllocExtHasher>
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<AllocExtMapIter, AllocExtMapIter> 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<BRM::CreateStripeColumnExtentsArgIn> cols;
std::vector<BRM::CreateStripeColumnExtentsArgOut> 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 << " <EMPTY>";
}
fLog->logMsg(oss.str(), MSGLVL_INFO2);
}
} // namespace WriteEngine