-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor XREF to use Sasha plane file storage instead of Dali IPT #153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
bfb8933
46c6991
e8678f8
0b600da
c7c4bbb
ab4d5fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,8 @@ | |
| #include "jstring.hpp" | ||
| #include "jptree.hpp" | ||
| #include "jmisc.hpp" | ||
| #include "jfile.hpp" | ||
| #include "jutil.hpp" | ||
|
|
||
| #include "mpcomm.hpp" | ||
| #include "platform.h" | ||
|
|
@@ -120,12 +122,81 @@ CXRefNode::CXRefNode(IPropertyTree* pTreeRoot) | |
| m_XRefTree.set(pTreeRoot); | ||
| rootDir.set(m_XRefTree->queryProp("@rootdir")); | ||
| pTreeRoot->getProp("@name",m_origName); | ||
| //load up our tree with the data.....if there is data | ||
| MemoryBuffer buff; | ||
| pTreeRoot->getPropBin("data",buff); | ||
| if (buff.length()) | ||
|
|
||
| // Check if path metadata is available (new Sasha plane storage) | ||
| const char *xrefPath = pTreeRoot->queryProp("@xrefPath"); | ||
| if (xrefPath && *xrefPath) | ||
| { | ||
| m_dataStr.append(buff.length(),buff.toByteArray()); | ||
| // New path-based storage - load branches from files | ||
| try | ||
| { | ||
| StringBuffer basePath(xrefPath); | ||
| // Handle file:// URLs | ||
| if (hasPrefix(basePath, "file://", false)) | ||
| { | ||
| // Extract path from file://hostname/path format | ||
| const char *pathStart = basePath.str() + 7; // Skip "file://" | ||
| // Find the next slash which marks the start of the actual path | ||
| const char *pathSep = strchr(pathStart, '/'); | ||
| if (pathSep) | ||
| { | ||
| basePath.clear().append(pathSep); | ||
| } | ||
| } | ||
|
|
||
| // Load each branch from its file | ||
| const char *branchNames[] = {"Orphans", "Lost", "Found", "Directories", "Messages", nullptr}; | ||
| for (int i = 0; branchNames[i] != nullptr; i++) | ||
| { | ||
| StringBuffer filepath(basePath); | ||
| addPathSepChar(filepath).append(branchNames[i]).append(".xml"); | ||
|
|
||
| Owned<IFile> file = createIFile(filepath.str()); | ||
| if (file->exists()) | ||
| { | ||
| Owned<IFileIO> fileIO = file->open(IFOread); | ||
| if (fileIO) | ||
| { | ||
| offset_t fileSize = file->size(); | ||
| if (fileSize > 0 && fileSize < 0x10000000) // Sanity check: < 256MB | ||
| { | ||
| StringBuffer xmlContent; | ||
| xmlContent.ensureCapacity((size32_t)fileSize); | ||
| size32_t bytesRead = fileIO->read(0, (size32_t)fileSize, (void*)xmlContent.reserve((size32_t)fileSize)); | ||
| if (bytesRead > 0) | ||
| { | ||
| Owned<IPropertyTree> branch = createPTreeFromXMLString(xmlContent.str()); | ||
| if (branch) | ||
| { | ||
| m_XRefTree->addPropTree(branchNames[i], branch.getClear()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| DBGLOG("XRefNode: Loaded XREF data from path: %s", basePath.str()); | ||
| } | ||
| catch (IException *e) | ||
| { | ||
| StringBuffer errMsg; | ||
| OWARNLOG("XRefNode: Failed to load from path '%s': %s, falling back to 'data' attribute", | ||
| xrefPath, e->errorMessage(errMsg).str()); | ||
| e->Release(); | ||
| // Fall through to load from data attribute | ||
| } | ||
| } | ||
|
|
||
| // Fall back to loading from "data" attribute if path not available or failed | ||
| if (!m_XRefTree->hasProp("Orphans") && !m_XRefTree->hasProp("Lost") && | ||
|
||
| !m_XRefTree->hasProp("Found") && !m_XRefTree->hasProp("Directories")) | ||
| { | ||
| MemoryBuffer buff; | ||
| pTreeRoot->getPropBin("data",buff); | ||
| if (buff.length()) | ||
| { | ||
| m_dataStr.append(buff.length(),buff.toByteArray()); | ||
| } | ||
| } | ||
| //lets check to ensure we have the correct children inplace(Orphan,lost,found) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,10 @@ | |
| #include "jmisc.hpp" | ||
| #include "jregexp.hpp" | ||
| #include "jset.hpp" | ||
| #include "jfile.hpp" | ||
| #include "jplane.hpp" | ||
| #include "jutil.hpp" | ||
| #include "jsocket.hpp" | ||
|
|
||
| #include <memory> | ||
| #include <unordered_map> | ||
|
|
@@ -1001,6 +1005,40 @@ class CNewXRefManagerBase | |
| heartbeatTimer.updatePeriod(); | ||
| } | ||
|
|
||
| bool saveBranchToSashaPlane(const char *sashaDir, const char *name, IPropertyTree *branch) | ||
| { | ||
| if (!branch) | ||
| return true; | ||
| try | ||
| { | ||
| branch->setProp("Cluster",clustname); | ||
| StringBuffer filepath(sashaDir); | ||
| addPathSepChar(filepath).append(name).append(".xml"); | ||
|
|
||
| StringBuffer datastr; | ||
| toXML(branch,datastr); | ||
|
|
||
| Owned<IFile> file = createIFile(filepath.str()); | ||
| Owned<IFileIO> fileIO = file->open(IFOcreate); | ||
| if (!fileIO) | ||
| { | ||
| OERRLOG(LOGPFX "Failed to create file: %s", filepath.str()); | ||
| return false; | ||
| } | ||
| fileIO->write(0, datastr.length(), datastr.str()); | ||
| fileIO->close(); | ||
| PROGLOG(LOGPFX "Saved branch %s to %s", name, filepath.str()); | ||
| return true; | ||
| } | ||
| catch (IException *e) | ||
| { | ||
| StringBuffer errMsg; | ||
| EXCLOG(e, LOGPFX "Error saving branch to Sasha plane"); | ||
| e->Release(); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| void addBranch(IPropertyTree *root,const char *name,IPropertyTree *branch) | ||
| { | ||
| if (!branch) | ||
|
|
@@ -1086,11 +1124,119 @@ class CNewXRefManagerBase | |
| else if (fnum) | ||
| ss.appendf(" [%d files]",fnum); | ||
| croot->setProp("@status",ss.str()); | ||
| addBranch(croot,"Orphans",orphansbranch); | ||
| addBranch(croot,"Lost",lostbranch); | ||
| addBranch(croot,"Found",foundbranch); | ||
| addBranch(croot,"Directories",dirbranch); | ||
| addErrorsWarnings(croot); | ||
|
|
||
| // Try to use Sasha plane for storage | ||
| bool useSashaPlane = false; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot - make this a configurable via an expert option, default to true if isContainerized().
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in c7c4bbb - Made configurable via @useSashaPlane expert option, defaults to isContainerized(). |
||
| StringBuffer sashaDir; | ||
| try | ||
| { | ||
| if (isContainerized()) | ||
| { | ||
| StringBuffer planeName; | ||
| // Look for sasha plane | ||
| if (getDefaultPlane(planeName, nullptr, "sasha")) | ||
|
||
| { | ||
| Owned<const IPropertyTree> sashaPlane = getStoragePlaneConfig(planeName, false); | ||
| if (sashaPlane) | ||
| { | ||
| StringBuffer prefix; | ||
| if (sashaPlane->getProp("@prefix", prefix)) | ||
|
||
| { | ||
| // Create directory structure: <prefix>/xref/<cluster>/<datestamp>/ | ||
| sashaDir.append(prefix); | ||
| addPathSepChar(sashaDir).append("xref"); | ||
| addPathSepChar(sashaDir).append(clustname); | ||
|
|
||
| // Create datestamp directory | ||
| StringBuffer datestamp; | ||
| dt.getDateString(datestamp, false); // YYYY-MM-DD format | ||
| addPathSepChar(sashaDir).append(datestamp); | ||
|
|
||
| // Create the directory | ||
| Owned<IFile> dir = createIFile(sashaDir.str()); | ||
|
||
| if (dir->createDirectory()) | ||
| { | ||
| useSashaPlane = true; | ||
| PROGLOG(LOGPFX "Using Sasha plane storage at: %s", sashaDir.str()); | ||
| } | ||
| else | ||
| { | ||
| OWARNLOG(LOGPFX "Failed to create Sasha directory: %s, falling back to Dali storage", sashaDir.str()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| catch (IException *e) | ||
| { | ||
| StringBuffer errMsg; | ||
| OWARNLOG(LOGPFX "Exception getting Sasha plane: %s, falling back to Dali storage", e->errorMessage(errMsg).str()); | ||
| e->Release(); | ||
| useSashaPlane = false; | ||
| } | ||
|
|
||
| if (useSashaPlane) | ||
| { | ||
| // Save branches to Sasha plane files | ||
| bool saveSuccess = true; | ||
| saveSuccess = saveBranchToSashaPlane(sashaDir.str(), "Orphans", orphansbranch) && saveSuccess; | ||
| saveSuccess = saveBranchToSashaPlane(sashaDir.str(), "Lost", lostbranch) && saveSuccess; | ||
| saveSuccess = saveBranchToSashaPlane(sashaDir.str(), "Found", foundbranch) && saveSuccess; | ||
| saveSuccess = saveBranchToSashaPlane(sashaDir.str(), "Directories", dirbranch) && saveSuccess; | ||
|
||
|
|
||
| // Save Messages | ||
| Owned<IPropertyTree> message = createPTree("Messages"); | ||
| ForEachItemIn(i1,errors) { | ||
| cMessage &item = errors.item(i1); | ||
| IPropertyTree *t = message->addPropTree("Error",createPTree("Error")); | ||
| t->addProp("File",item.lname.get()); | ||
| t->addProp("Text",item.msg.get()); | ||
| } | ||
| ForEachItemIn(i2,warnings) { | ||
| cMessage &item = warnings.item(i2); | ||
| IPropertyTree *t = message->addPropTree("Warning",createPTree("Warning")); | ||
| t->addProp("File",item.lname.get()); | ||
| t->addProp("Text",item.msg.get()); | ||
| } | ||
| saveSuccess = saveBranchToSashaPlane(sashaDir.str(), "Messages", message) && saveSuccess; | ||
|
|
||
| if (saveSuccess) | ||
|
||
| { | ||
| // Store path reference in Dali instead of full data | ||
| // Convert to hostname-based URL if not a local path | ||
| StringBuffer pathUrl; | ||
| if (!isAbsolutePath(sashaDir.str())) | ||
|
||
| { | ||
| // If not absolute, make it a URL with hostname | ||
| StringBuffer hostname; | ||
| queryHostIP().getHostText(hostname); | ||
| pathUrl.append("file://").append(hostname).append(sashaDir); | ||
| } | ||
| else | ||
| { | ||
| pathUrl.append(sashaDir); | ||
| } | ||
| croot->setProp("@xrefPath", pathUrl.str()); | ||
| PROGLOG(LOGPFX "Saved XREF data to Sasha plane with path reference: %s", pathUrl.str()); | ||
| } | ||
| else | ||
| { | ||
| OWARNLOG(LOGPFX "Failed to save some branches to Sasha plane, falling back to Dali storage"); | ||
| useSashaPlane = false; | ||
| } | ||
| } | ||
|
|
||
| if (!useSashaPlane) | ||
|
||
| { | ||
| // Fall back to traditional Dali storage | ||
| addBranch(croot,"Orphans",orphansbranch); | ||
| addBranch(croot,"Lost",lostbranch); | ||
| addBranch(croot,"Found",foundbranch); | ||
| addBranch(croot,"Directories",dirbranch); | ||
| addErrorsWarnings(croot); | ||
| } | ||
|
|
||
| if (abort) | ||
| return; | ||
| logconn.clear(); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot - this is not necessary, remove this block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in c7c4bbb - Removed file:// URL handling block entirely.