beegfs/storage/source/net/message/session/opening/CloseChunkFileMsgEx.cpp
2025-08-10 01:34:16 +02:00

253 lines
9.3 KiB
C++

#include <common/net/message/control/GenericResponseMsg.h>
#include <common/net/message/session/opening/CloseChunkFileRespMsg.h>
#include <common/toolkit/SessionTk.h>
#include <net/msghelpers/MsgHelperIO.h>
#include <program/Program.h>
#include <toolkit/StorageTkEx.h>
#include "CloseChunkFileMsgEx.h"
#include <boost/lexical_cast.hpp>
bool CloseChunkFileMsgEx::processIncoming(ResponseContext& ctx)
{
App* app = Program::getApp();
FhgfsOpsErr closeMsgRes;
DynamicAttribs dynAttribs;
std::tie(closeMsgRes, dynAttribs) = close(ctx);
// if closeMsgRes == FhgfsOpsErr_COMMUNICATION, a GenericResponseMsg has been sent already
if (closeMsgRes != FhgfsOpsErr_COMMUNICATION)
ctx.sendResponse(
CloseChunkFileRespMsg(closeMsgRes, dynAttribs.filesize, dynAttribs.allocedBlocks,
dynAttribs.modificationTimeSecs, dynAttribs.lastAccessTimeSecs,
dynAttribs.storageVersion) );
// update op counters
app->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(), StorageOpCounter_CLOSELOCAL,
getMsgHeaderUserID() );
return true;
}
std::pair<FhgfsOpsErr, CloseChunkFileMsgEx::DynamicAttribs> CloseChunkFileMsgEx::close(
ResponseContext& ctx)
{
const char* logContext = "CloseChunkFileMsg incoming";
App* app = Program::getApp();
Config* config = app->getConfig();
SessionStore* sessions = app->getSessions();
uint16_t targetID;
FhgfsOpsErr closeMsgRes = FhgfsOpsErr_SUCCESS; // the result that will be sent to requestor
DynamicAttribs dynAttribs = {0, 0, 0, 0, 0};
std::string fileHandleID(getFileHandleID() );
bool isMirrorSession = isMsgHeaderFeatureFlagSet(CLOSECHUNKFILEMSG_FLAG_BUDDYMIRROR);
SessionLocalFileStore* sessionLocalFiles;
// select the right targetID
targetID = getTargetID();
if(isMsgHeaderFeatureFlagSet(CLOSECHUNKFILEMSG_FLAG_BUDDYMIRROR) )
{ // given targetID refers to a buddy mirror group
MirrorBuddyGroupMapper* mirrorBuddies = app->getMirrorBuddyGroupMapper();
targetID = isMsgHeaderFeatureFlagSet(CLOSECHUNKFILEMSG_FLAG_BUDDYMIRROR_SECOND) ?
mirrorBuddies->getSecondaryTargetID(targetID) :
mirrorBuddies->getPrimaryTargetID(targetID);
if(unlikely(!targetID) )
{ // unknown target
LogContext(logContext).logErr("Invalid mirror buddy group ID: " +
StringTk::uintToStr(getTargetID() ) );
return {FhgfsOpsErr_UNKNOWNTARGET, {}};
}
}
// forward to secondary (if appropriate)
closeMsgRes = forwardToSecondary(ctx);
if (unlikely(closeMsgRes != FhgfsOpsErr_SUCCESS))
return {closeMsgRes, dynAttribs};
auto session = sessions->referenceOrAddSession(getSessionID());
sessionLocalFiles = session->getLocalFiles();
auto fsState = sessionLocalFiles->removeSession(fileHandleID, targetID, isMirrorSession);
// get current dynamic file attribs
if (fsState)
{ // file no longer in use => refresh filesize and close file fd
auto& fd = fsState->getFD();
/* get dynamic attribs, here before closing the file.
* Note: Depending on the underlying file system the returned st_blocks might be too large
* (pre-allocated blocks, which are only released on close() ). Advantage here is
* that we already have the file descriptor. */
if( (config->getTuneEarlyStat() ) &&
(!isMsgHeaderFeatureFlagSet(CLOSECHUNKFILEMSG_FLAG_NODYNAMICATTRIBS) ) )
getDynamicAttribsByFD(*fd, fileHandleID, targetID, dynAttribs);
// close fd
if (!fsState->close())
closeMsgRes = FhgfsOpsErr_INTERNAL;
// only get the attributes here, in order to make xfs to release pre-allocated blocks
if( (!config->getTuneEarlyStat() ) &&
(!isMsgHeaderFeatureFlagSet(CLOSECHUNKFILEMSG_FLAG_NODYNAMICATTRIBS) ) )
getDynamicAttribsByPath(fileHandleID, targetID, dynAttribs);
}
else
if(!isMsgHeaderFeatureFlagSet(CLOSECHUNKFILEMSG_FLAG_NODYNAMICATTRIBS) )
{ // file still in use by other threads => get dynamic attribs by path
bool getRes = getDynamicAttribsByPath(fileHandleID, targetID, dynAttribs);
if (getRes)
{
// LogContext(logContext).log(Log_DEBUG, "Chunk file virtually closed. "
// "HandleID: " + fileHandleID);
}
}
// note: "file not exists" is not an error. we just have nothing to do in that case.
return {closeMsgRes, dynAttribs};
}
/**
* If this is a buddy mirror msg and we are the primary, forward this msg to secondary.
*
* @return _COMMUNICATION if forwarding to buddy failed and buddy is not marked offline (in which
* case *outChunkLocked==false is guaranteed).
* @throw SocketException if sending of GenericResponseMsg fails.
*/
FhgfsOpsErr CloseChunkFileMsgEx::forwardToSecondary(ResponseContext& ctx)
{
const char* logContext = "CloseChunkFileMsg incoming (forward to secondary)";
App* app = Program::getApp();
if(!isMsgHeaderFeatureFlagSet(CLOSECHUNKFILEMSG_FLAG_BUDDYMIRROR) ||
isMsgHeaderFeatureFlagSet(CLOSECHUNKFILEMSG_FLAG_BUDDYMIRROR_SECOND) )
return FhgfsOpsErr_SUCCESS; // nothing to do
// instead of creating a new msg object, we just re-use "this" with "buddymirror second" flag
addMsgHeaderFeatureFlag(CLOSECHUNKFILEMSG_FLAG_BUDDYMIRROR_SECOND);
RequestResponseArgs rrArgs(NULL, this, NETMSGTYPE_CloseChunkFileResp);
RequestResponseTarget rrTarget(getTargetID(), app->getTargetMapper(), app->getStorageNodes(),
app->getTargetStateStore(), app->getMirrorBuddyGroupMapper(), true);
FhgfsOpsErr commRes = MessagingTk::requestResponseTarget(&rrTarget, &rrArgs);
// remove the flag that we just added for secondary
unsetMsgHeaderFeatureFlag(CLOSECHUNKFILEMSG_FLAG_BUDDYMIRROR_SECOND);
if(unlikely(
(commRes == FhgfsOpsErr_COMMUNICATION) &&
(rrTarget.outTargetReachabilityState == TargetReachabilityState_OFFLINE) ) )
{
LOG_DEBUG(logContext, Log_DEBUG, std::string("Secondary is offline and will need resync. ") +
"mirror buddy group ID: " + StringTk::uintToStr(getTargetID() ) );;
return FhgfsOpsErr_SUCCESS; // go ahead with local msg processing
}
if(unlikely(commRes != FhgfsOpsErr_SUCCESS) )
{
LogContext(logContext).log(Log_DEBUG, "Forwarding failed. "
"mirror buddy group ID: " + StringTk::uintToStr(getTargetID() ) + "; "
"error: " + boost::lexical_cast<std::string>(commRes));
std::string genericRespStr = "Communication with secondary failed. "
"mirror buddy group ID: " + StringTk::uintToStr(getTargetID() );
ctx.sendResponse(
GenericResponseMsg(GenericRespMsgCode_INDIRECTCOMMERR, std::move(genericRespStr)));
return FhgfsOpsErr_COMMUNICATION;
}
CloseChunkFileRespMsg* respMsg = (CloseChunkFileRespMsg*)rrArgs.outRespMsg.get();
FhgfsOpsErr secondaryRes = respMsg->getResult();
if(unlikely(secondaryRes != FhgfsOpsErr_SUCCESS) )
{
LogContext(logContext).log(Log_NOTICE, std::string("Secondary reported error: ") +
boost::lexical_cast<std::string>(secondaryRes) + "; "
"mirror buddy group ID: " + StringTk::uintToStr(getTargetID() ) );
return secondaryRes;
}
return FhgfsOpsErr_SUCCESS;
}
bool CloseChunkFileMsgEx::getDynamicAttribsByFD(const int fd, std::string fileHandleID,
uint16_t targetID, DynamicAttribs& outDynAttribs)
{
SyncedStoragePaths* syncedPaths = Program::getApp()->getSyncedStoragePaths();
std::string fileID(SessionTk::fileIDFromHandleID(fileHandleID) );
uint64_t storageVersion = syncedPaths->lockPath(fileID, targetID); // LOCK
// note: this is locked because we need to get the filesize together with the storageVersion
bool getDynAttribsRes = StorageTkEx::getDynamicFileAttribs(fd, &outDynAttribs.filesize,
&outDynAttribs.allocedBlocks, &outDynAttribs.modificationTimeSecs,
&outDynAttribs.lastAccessTimeSecs);
if(getDynAttribsRes)
outDynAttribs.storageVersion = storageVersion;
syncedPaths->unlockPath(fileID, targetID); // UNLOCK
return getDynAttribsRes;
}
bool CloseChunkFileMsgEx::getDynamicAttribsByPath(std::string fileHandleID, uint16_t targetID,
DynamicAttribs& outDynAttribs)
{
const char* logContext = "CloseChunkFileMsg (attribs by path)";
App* app = Program::getApp();
SyncedStoragePaths* syncedPaths = app->getSyncedStoragePaths();
auto* const target = app->getStorageTargets()->getTarget(targetID);
if (!target)
{ // unknown targetID
LogContext(logContext).logErr("Unknown targetID: " + StringTk::uintToStr(targetID) );
return false;
}
const int targetFD = isMsgHeaderFeatureFlagSet(CLOSECHUNKFILEMSG_FLAG_BUDDYMIRROR)
? *target->getMirrorFD()
: *target->getChunkFD();
std::string fileID = SessionTk::fileIDFromHandleID(fileHandleID);
std::string pathStr = StorageTk::getFileChunkPath(getPathInfo(), fileID);
uint64_t storageVersion = syncedPaths->lockPath(fileID, targetID); // L O C K path
// note: this is locked because we need to get the filesize together with the storageVersion
bool getDynAttribsRes = StorageTkEx::getDynamicFileAttribs(targetFD, pathStr.c_str(),
&outDynAttribs.filesize, &outDynAttribs.allocedBlocks, &outDynAttribs.modificationTimeSecs,
&outDynAttribs.lastAccessTimeSecs);
if(getDynAttribsRes)
outDynAttribs.storageVersion = storageVersion;
syncedPaths->unlockPath(fileID, targetID); // U N L O C K path
return getDynAttribsRes;
}