beegfs/meta/source/session/SessionFileStore.cpp
2025-08-10 01:34:16 +02:00

370 lines
11 KiB
C++

#include <program/Program.h>
#include "SessionFileStore.h"
#include <mutex>
/**
* @param session belongs to the store after calling this method - so do not free it and don't
* use it any more afterwards (re-get it from this store if you need it)
* @return assigned sessionID
*/
unsigned SessionFileStore::addSession(SessionFile* session)
{
const std::lock_guard<Mutex> lock(mutex);
unsigned sessionID = generateNewSessionID();
session->setSessionID(sessionID);
sessions.insert(SessionFileMapVal(sessionID, new SessionFileReferencer(session) ) );
return sessionID;
}
/**
* @param session belongs to the store after calling this method - so do not free it and don't
* use it any more afterwards (re-get it from this store if you need it)
* @param sessionFileID use this ID to store session file
* @return true if successfully added, false otherwise (most likely ID conflict)
*/
bool SessionFileStore::addSession(SessionFile* session, unsigned sessionFileID)
{
const std::lock_guard<Mutex> lock(mutex);
session->setSessionID(sessionFileID);
std::pair<SessionFileMapIter, bool> insertRes =
sessions.insert(SessionFileMapVal(sessionFileID, new SessionFileReferencer(session) ) );
return insertRes.second;
}
/**
* Insert a session with a pre-defined sessionID and reference it. Will fail if given ID existed
* already.
*
* Note: This is a special method intended for recovery purposes only!
* Note: remember to call releaseSession() if the new session was actually inserted.
*
* @param session belongs to the store if it was inserted - so do not free it and don't
* use it any more afterwards (reference it if you still need it)
* @return NULL if sessionID existed already (and new session not inserted or touched in any way),
* pointer to referenced session file otherwise.
*/
SessionFile* SessionFileStore::addAndReferenceRecoverySession(SessionFile* session)
{
const std::lock_guard<Mutex> lock(mutex);
SessionFile* retVal = NULL;
unsigned sessionID = session->getSessionID();
SessionFileMapIter iter = sessions.find(sessionID);
if(iter == sessions.end() )
{ // session with this ID didn't exist (this is the normal case) => insert and reference it
/* note: we do the sessions.find() first to avoid allocating SessionRefer if it's not
necessary */
SessionFileReferencer* sessionFileRefer = new SessionFileReferencer(session);
sessions.insert(SessionFileMapVal(sessionID, sessionFileRefer) );
retVal = sessionFileRefer->reference();
}
return retVal;
}
/**
* Note: remember to call releaseSession()
*
* @return NULL if no such session exists
*/
SessionFile* SessionFileStore::referenceSession(unsigned sessionID)
{
const std::lock_guard<Mutex> lock(mutex);
SessionFileMapIter iter = sessions.find(sessionID);
if(iter == sessions.end() )
{ // not found
return NULL;
}
else
{
SessionFileReferencer* sessionRefer = iter->second;
return sessionRefer->reference();
}
}
void SessionFileStore::releaseSession(SessionFile* session, EntryInfo* entryInfo)
{
unsigned sessionID = session->getSessionID();
bool asyncCleanup = false;
unsigned asyncCleanupAccessFlags = 0; // only for asyncCleanup
MetaFileHandle asyncCleanupFile; // only for asyncCleanup
{
const std::lock_guard<Mutex> lock(mutex);
SessionFileMapIter iter = sessions.find(sessionID);
if(iter != sessions.end() )
{ // session exists => decrease refCount
SessionFileReferencer* sessionRefer = iter->second;
SessionFile* sessionNonRef = sessionRefer->getReferencedObject();
if(unlikely(sessionNonRef->getUseAsyncCleanup() ) )
{ // marked for async cleanup => check whether we're dropping the last reference
if(sessionRefer->getRefCount() == 1)
{ // we're dropping the last reference => save async cleanup data and trigger cleanup
asyncCleanup = true;
asyncCleanupFile = sessionNonRef->releaseInode();
asyncCleanupAccessFlags = sessionNonRef->getAccessFlags();
sessionRefer->release();
sessions.erase(iter);
delete(sessionRefer);
}
}
else
{ // the normal case: just release this reference
sessionRefer->release();
}
}
}
if(unlikely(asyncCleanup) )
performAsyncCleanup(entryInfo, std::move(asyncCleanupFile), asyncCleanupAccessFlags);
}
/**
* @return false if session could not be removed, because it was still in use; it will be marked
* for async cleanup when the last reference is dropped
*/
bool SessionFileStore::removeSession(unsigned sessionID)
{
bool delErr = true;
const std::lock_guard<Mutex> lock(mutex);
SessionFileMapIter iter = sessions.find(sessionID);
if(iter != sessions.end() )
{
SessionFileReferencer* sessionRefer = iter->second;
if(sessionRefer->getRefCount() )
{ // session still in use => mark for async cleanup (on last reference drop)
SessionFile* fileNonRef = sessionRefer->getReferencedObject();
fileNonRef->setUseAsyncCleanup();
delErr = true;
}
else
{ // no references => delete
sessions.erase(iter);
delete(sessionRefer);
delErr = false;
}
}
return !delErr;
}
/**
* @return might be NULL if the session is in use
*/
SessionFile* SessionFileStore::removeAndGetSession(unsigned sessionID)
{
// note: this method is currently unused
SessionFile* session = NULL;
const std::lock_guard<Mutex> lock(mutex);
SessionFileMapIter iter = sessions.find(sessionID);
if(iter != sessions.end() )
{
SessionFileReferencer* sessionRefer = iter->second;
if(!sessionRefer->getRefCount() )
{ // no references => allow deletion
sessions.erase(iter);
sessionRefer->setOwnReferencedObject(false);
session = sessionRefer->getReferencedObject();
delete(sessionRefer);
}
}
return session;
}
/**
* Removes all sessions and additionally adds those that had a reference count to the StringList.
*
* @outRemovedSessions caller is responsible for clean up of contained objects
*/
void SessionFileStore::removeAllSessions(SessionFileList* outRemovedSessions,
UIntList* outReferencedSessions)
{
const std::lock_guard<Mutex> lock(mutex);
for(SessionFileMapIter iter = sessions.begin(); iter != sessions.end(); iter++)
{
SessionFileReferencer* sessionRefer = iter->second;
SessionFile* session = sessionRefer->getReferencedObject();
outRemovedSessions->push_back(session);
if(unlikely(sessionRefer->getRefCount() ) )
outReferencedSessions->push_back(iter->first);
sessionRefer->setOwnReferencedObject(false);
delete(sessionRefer);
}
sessions.clear();
}
/*
* Intended to be used for cleanup if deserialization failed, no locking is used
*/
void SessionFileStore::deleteAllSessions()
{
for(SessionFileMapIter iter = sessions.begin(); iter != sessions.end(); iter++)
{
SessionFileReferencer* sessionRefer = iter->second;
delete(sessionRefer);
}
sessions.clear();
}
size_t SessionFileStore::getSize()
{
const std::lock_guard<Mutex> lock(mutex);
return sessions.size();
}
unsigned SessionFileStore::generateNewSessionID()
{
SessionFileMapIter iter;
// note: we assume here that there always is at least one free sessionID.
do
{
// generate new ID
lastSessionID++;
// check whether this ID is being used already
iter = sessions.find(lastSessionID);
} while(iter != sessions.end() );
// we found an available sessionID => return it
return lastSessionID;
}
/**
* Performs local cleanup tasks for file sessions, which are marked for async cleanup.
*/
void SessionFileStore::performAsyncCleanup(EntryInfo* entryInfo,
MetaFileHandle inode, unsigned accessFlags)
{
const char* logContext = __func__;
App* app = Program::getApp();
MetaStore* metaStore = app->getMetaStore();
unsigned numHardlinks; // ignored here
unsigned numInodeRefs; // ignored here
bool lastWriterClosed; // ignored here
LOG_DEBUG(logContext, Log_NOTICE, "Performing async cleanup of file session");
IGNORE_UNUSED_VARIABLE(logContext);
metaStore->closeFile(entryInfo, std::move(inode), accessFlags, &numHardlinks, &numInodeRefs,
lastWriterClosed);
/* note: we ignore closing storage server files here (e.g. because we don't have the sessionID
and fileHandleID at hand) and unlinking of disposable files (disposal can still be triggered
by fhgfs_online_cfg).
this is something that we should change in the future (but maybe rather indirectly by syncing
open files between clients and servers at regular intervals). */
}
SessionFileMap* SessionFileStore::getSessionMap()
{
return &this->sessions;
}
/* Merges the SessionFiles of the given SessionFileStore into this SessionFileStore.
* Only not existing SessionFiles will be added to the existing SessionFileStore
*
* @param sessionFileStore the sessionFileStore which will be merged with this sessionFileStore
*/
void SessionFileStore::mergeSessionFiles(SessionFileStore* sessionFileStore)
{
Logger* log = Logger::getLogger();
SessionFileMapIter sessionIter = sessionFileStore->getSessionMap()->begin();
while(sessionIter != sessionFileStore->getSessionMap()->end() )
{
bool sessionFound = false;
SessionFileMapIter destSessionIter = this->sessions.find(sessionIter->first);
if (destSessionIter != this->sessions.end())
{
sessionFound = true;
log->log(Log_WARNING, "SessionFileStore merge", "found SessionFile with same "
"ID: " + StringTk::uintToStr(sessionIter->first) +
" , merge not possible, may be a bug?");
}
if (!sessionFound)
{
bool success = this->sessions.insert(SessionFileMapVal(sessionIter->first,
sessionIter->second)).second;
if (!success)
{
log->log(Log_WARNING, "SessionFileStore merge", "could not merge: " +
StringTk::uintToStr(sessionIter->first) );
delete(sessionIter->second);
}
}
else
{
delete(sessionIter->second);
}
sessionIter++;
}
}
bool SessionFileStore::operator==(const SessionFileStore& other) const
{
struct ops {
static bool cmp(const SessionFileMapVal& lhs, const SessionFileMapVal& rhs)
{
return lhs.first == rhs.first
&& *lhs.second->getReferencedObject() == *rhs.second->getReferencedObject();
}
};
return lastSessionID == other.lastSessionID
&& sessions.size() == other.sessions.size()
&& std::equal(
sessions.begin(), sessions.end(),
other.sessions.begin(),
ops::cmp);
}