beegfs/meta/source/net/msghelpers/MsgHelperTrunc.cpp
2025-08-10 01:34:16 +02:00

280 lines
9.3 KiB
C++

#include <common/net/message/storage/TruncLocalFileMsg.h>
#include <common/net/message/storage/TruncLocalFileRespMsg.h>
#include <components/worker/TruncChunkFileWork.h>
#include <program/Program.h>
#include "MsgHelperTrunc.h"
#include <boost/lexical_cast.hpp>
/**
* Note: Will also update persistent metadata on disk.
*
* @param msgUserID will only be used in msg header info.
*/
FhgfsOpsErr MsgHelperTrunc::truncFile(EntryInfo* entryInfo, int64_t filesize, bool useQuota,
unsigned msgUserID, DynamicFileAttribsVec& dynAttribs)
{
MetaStore* metaStore = Program::getApp()->getMetaStore();
auto [inode, referenceRes] = metaStore->referenceFile(entryInfo);
if(!inode)
return referenceRes;
FhgfsOpsErr localRes = truncChunkFile(*inode, entryInfo, filesize, useQuota, msgUserID,
dynAttribs);
inode->updateInodeOnDisk(entryInfo);
metaStore->releaseFile(entryInfo->getParentEntryID(), inode);
return localRes;
}
/**
* Note: This also updates dynamic file attribs.
* Note: Call this directly (instead of truncFile() ) if you already got the file handle.
* Note: Will NOT update persistent metadata on disk.
*
* @param msgUserID only used for msg header info.
*/
FhgfsOpsErr MsgHelperTrunc::truncChunkFile(FileInode& inode, EntryInfo* entryInfo,
int64_t filesize, bool useQuota, unsigned userIDHint, DynamicFileAttribsVec& dynAttribs)
{
StripePattern* pattern = inode.getStripePattern();
if( (pattern->getStripeTargetIDs()->size() > 1) ||
(pattern->getPatternType() == StripePatternType_BuddyMirror) )
return truncChunkFileParallel(inode, entryInfo, filesize, useQuota, userIDHint, dynAttribs);
else
return truncChunkFileSequential(inode, entryInfo, filesize, useQuota, userIDHint, dynAttribs);
}
/**
* Note: This method does not work for mirrored files; use truncChunkFileParallel() for those.
* Note: This also updates dynamic file attribs.
*
* @param msgUserID only used for msg header info.
*/
FhgfsOpsErr MsgHelperTrunc::truncChunkFileSequential(FileInode& inode, EntryInfo* entryInfo,
int64_t filesize, bool useQuota, unsigned msgUserID, DynamicFileAttribsVec& dynAttribs)
{
const char* logContext = "Trunc chunk file helper S";
StripePattern* pattern = inode.getStripePattern();
const UInt16Vector* targetIDs = pattern->getStripeTargetIDs();
TargetMapper* targetMapper = Program::getApp()->getTargetMapper();
TargetStateStore* targetStates = Program::getApp()->getTargetStateStore();
NodeStore* nodes = Program::getApp()->getStorageNodes();
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
PathInfo pathInfo;
inode.getPathInfo(&pathInfo);
dynAttribs.resize(targetIDs->size());
// send request to each node and receive the response message
unsigned currentNodeIndex = 0;
for(UInt16VectorConstIter iter = targetIDs->begin();
iter != targetIDs->end();
iter++, currentNodeIndex++)
{
uint16_t targetID = *iter;
std::string entryID = entryInfo->getEntryID();
int64_t truncPos = getNodeLocalTruncPos(filesize, *pattern, currentNodeIndex);
TruncLocalFileMsg truncMsg(truncPos, entryID, targetID, &pathInfo);
if (useQuota)
truncMsg.setUserdataForQuota(inode.getUserID(), inode.getGroupID());
truncMsg.setMsgHeaderUserID(msgUserID);
RequestResponseArgs rrArgs(NULL, &truncMsg, NETMSGTYPE_TruncLocalFileResp);
RequestResponseTarget rrTarget(targetID, targetMapper, nodes);
rrTarget.setTargetStates(targetStates);
// send request to node and receive response
FhgfsOpsErr requestRes = MessagingTk::requestResponseTarget(&rrTarget, &rrArgs);
if(requestRes != FhgfsOpsErr_SUCCESS)
{ // communication error
LogContext(logContext).log(Log_WARNING,
"Communication with storage target failed: " + StringTk::uintToStr(targetID) + "; "
"fileID: " + inode.getEntryID());
if(retVal == FhgfsOpsErr_SUCCESS)
retVal = requestRes;
continue;
}
// correct response type received
TruncLocalFileRespMsg* truncRespMsg = (TruncLocalFileRespMsg*)rrArgs.outRespMsg.get();
// set current dynamic attribs (even if result not success, because then storageVersion==0)
DynamicFileAttribs currentDynAttribs(truncRespMsg->getStorageVersion(),
truncRespMsg->getFileSize(), truncRespMsg->getAllocedBlocks(),
truncRespMsg->getModificationTimeSecs(), truncRespMsg->getLastAccessTimeSecs() );
dynAttribs[currentNodeIndex] = currentDynAttribs;
FhgfsOpsErr chunkTruncRes = truncRespMsg->getResult();
if(chunkTruncRes != FhgfsOpsErr_SUCCESS)
{ // error: chunk file not truncated
LogContext(logContext).log(Log_WARNING,
"Storage target failed to truncate chunk file: " + StringTk::uintToStr(targetID) + "; "
"fileID: " + inode.getEntryID() + "; "
"Error: " + boost::lexical_cast<std::string>(chunkTruncRes));
if(retVal == FhgfsOpsErr_SUCCESS)
retVal = truncRespMsg->getResult();
continue;
}
// success: local inode unlinked
LOG_DEBUG(logContext, Log_DEBUG,
"Storage target truncated chunk file: " + StringTk::uintToStr(targetID) + "; "
"fileID: " + inode.getEntryID());
}
inode.setDynAttribs(dynAttribs); // the actual update
if(unlikely( (retVal != FhgfsOpsErr_SUCCESS) && (retVal != FhgfsOpsErr_TOOBIG) ) )
{
LogContext(logContext).log(Log_WARNING,
"Problems occurred during truncation of chunk files. "
"fileID: " + inode.getEntryID());
}
return retVal;
}
/**
* Note: This also updates dynamic file attribs.
*
* @param msgUserID only used for msg header info.
*/
FhgfsOpsErr MsgHelperTrunc::truncChunkFileParallel(FileInode& inode, EntryInfo* entryInfo,
int64_t filesize, bool useQuota, unsigned msgUserID, DynamicFileAttribsVec& dynAttribs)
{
const char* logContext = "Trunc chunk file helper";
App* app = Program::getApp();
MultiWorkQueue* slaveQ = app->getCommSlaveQueue();
StripePattern* pattern = inode.getStripePattern();
const UInt16Vector* targetIDs = pattern->getStripeTargetIDs();
size_t numTargetWorks = targetIDs->size();
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
FhgfsOpsErrVec nodeResults(numTargetWorks);
SynchronizedCounter counter;
// generate work for storage targets...
PathInfo pathInfo;
inode.getPathInfo(&pathInfo);
dynAttribs.resize(numTargetWorks);
for(size_t i=0; i < numTargetWorks; i++)
{
int64_t localTruncPos = getNodeLocalTruncPos(filesize, *pattern, i);
TruncChunkFileWork* work = new TruncChunkFileWork(inode.getEntryID(), localTruncPos,
pattern, (*targetIDs)[i], &pathInfo, &dynAttribs[i], &(nodeResults[i]), &counter);
if (useQuota)
work->setUserdataForQuota(inode.getUserID(), inode.getGroupID());
work->setMsgUserID(msgUserID);
slaveQ->addDirectWork(work);
}
// wait for work completion...
counter.waitForCount(numTargetWorks);
// check target results...
for(size_t i=0; i < numTargetWorks; i++)
{
FhgfsOpsErr nodeResult = nodeResults[i];
if(unlikely(nodeResult != FhgfsOpsErr_SUCCESS) )
{
LogContext(logContext).log(Log_WARNING,
"Problems occurred during truncation of storage server chunk files. "
"File: " + inode.getEntryID());
retVal = nodeResult;
break;
}
}
inode.setDynAttribs(dynAttribs); // the actual update
return retVal;
}
/**
* Note: Makes only sense to call this before opening the file (because afterwards the filesize
* will be unknown).
*
* @param filesize desired trunc filesize
* @return true if current filesize is unknown or not equal to given filesize
*/
bool MsgHelperTrunc::isTruncChunkRequired(EntryInfo* entryInfo, int64_t filesize)
{
MetaStore* metaStore = Program::getApp()->getMetaStore();
StatData statData;
FhgfsOpsErr statRes = metaStore->stat(entryInfo, true, statData);
if( (statRes == FhgfsOpsErr_PATHNOTEXISTS) && (!filesize) )
return false; // does not exist => no zero-trunc necessary
if( (statRes == FhgfsOpsErr_SUCCESS) && (filesize == statData.getFileSize() ) )
return false; // exists and already has desired size
return true;
}
int64_t MsgHelperTrunc::getNodeLocalOffset(int64_t pos, int64_t chunkSize, size_t numNodes,
size_t stripeNodeIndex)
{
// (note: we can use "& ... -1" here instead if "%", because chunkSize is a po wer of two
int64_t posModChunkSize = pos & (chunkSize - 1);
int64_t chunkStart = pos - posModChunkSize - (stripeNodeIndex*chunkSize);
return ( (chunkStart / numNodes) + posModChunkSize);
}
int64_t MsgHelperTrunc::getNodeLocalTruncPos(int64_t pos, StripePattern& pattern,
size_t stripeNodeIndex)
{
int64_t truncPos;
size_t numTargets = pattern.getStripeTargetIDs()->size();
size_t mainNodeIndex = pattern.getStripeTargetIndex(pos);
int64_t mainNodeLocalOffset = getNodeLocalOffset(
pos, pattern.getChunkSize(), numTargets, mainNodeIndex);
if(stripeNodeIndex < mainNodeIndex)
truncPos = pattern.getNextChunkStart(mainNodeLocalOffset);
else
if(stripeNodeIndex == mainNodeIndex)
truncPos = mainNodeLocalOffset;
else
truncPos = pattern.getChunkStart(mainNodeLocalOffset);
truncPos = BEEGFS_MAX(truncPos, 0);
return truncPos;
}