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
Serguey Zefirov 38fd96a663 fix(memory leaks): MCOL-5791 - get rid of memory leaks in plugin code
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-04 10:59:12 +03: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