Browse Source

HPCC-14528 Refactor DFS update during publish/add-pkgmap/copy-query

Clean up DFS meta data update code used when publishing queries,
copying queries, and deploying packagemaps.

Make the options for what DFS information gets updated more flexible,
so that when locking issues are encountered it's not an all or nothing
update scenario.

Add more checking to only update when file information has really changed.

Add more logging so that when there are locking issues we can see
what types of updating was being done.

Signed-off-by: Anthony Fishbeck <anthony.fishbeck@lexisnexis.com>
Anthony Fishbeck 9 years ago
parent
commit
43e4b401d8

+ 13 - 13
common/workunit/referencedfilelist.cpp

@@ -167,8 +167,8 @@ public:
             ep.set(NULL);
         return ep;
     }
-    virtual void cloneInfo(IDFUhelper *helper, IUserDescriptor *user, const char *dstCluster, const char *srcCluster, bool overwrite, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder);
-    void cloneSuperInfo(ReferencedFileList *list, IUserDescriptor *user, INode *remote, bool overwrite);
+    virtual void cloneInfo(unsigned updateFlags, IDFUhelper *helper, IUserDescriptor *user, const char *dstCluster, const char *srcCluster, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder);
+    void cloneSuperInfo(unsigned updateFlags, ReferencedFileList *list, IUserDescriptor *user, INode *remote);
     virtual const char *queryPackageId() const {return pkgid.get();}
     virtual __int64 getFileSize()
     {
@@ -229,12 +229,12 @@ public:
     void addFilesFromPackage(IPropertyTree &package, const char *_daliip, const char *srcCluster, const char *_remotePrefix);
 
     virtual IReferencedFileIterator *getFiles();
-    virtual void cloneFileInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder);
+    virtual void cloneFileInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder);
 
     virtual void cloneRelationships();
-    virtual void cloneAllInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
+    virtual void cloneAllInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
     {
-        cloneFileInfo(helper, overwrite, cloneSuperInfo, cloneForeign, redundancy, channelsPerNode, replicateOffset, defReplicateFolder);
+        cloneFileInfo(updateFlags, helper, cloneSuperInfo, cloneForeign, redundancy, channelsPerNode, replicateOffset, defReplicateFolder);
         cloneRelationships();
     }
     virtual void resolveFiles(const char *process, const char *remoteIP, const char *_remotePrefix, const char *srcCluster, bool checkLocalFirst, bool addSubFiles, bool trackSubFiles, bool resolveForeign=false);
@@ -415,7 +415,7 @@ void ReferencedFile::resolve(const char *dstCluster, const char *srcCluster, IUs
         resolveLocal(dstCluster, srcCluster, user, subfiles);
 }
 
-void ReferencedFile::cloneInfo(IDFUhelper *helper, IUserDescriptor *user, const char *dstCluster, const char *srcCluster, bool overwrite, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
+void ReferencedFile::cloneInfo(unsigned updateFlags, IDFUhelper *helper, IUserDescriptor *user, const char *dstCluster, const char *srcCluster, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
 {
     if ((flags & RefFileCloned) || (flags & RefFileSuper) || (flags & RefFileInPackage))
         return;
@@ -432,7 +432,7 @@ void ReferencedFile::cloneInfo(IDFUhelper *helper, IUserDescriptor *user, const
         if (filePrefix.length())
             srcLFN.append(filePrefix.str()).append("::");
         srcLFN.append(logicalName.str());
-        helper->cloneRoxieSubFile(srcLFN, srcCluster, logicalName, dstCluster, filePrefix, redundancy, channelsPerNode, replicateOffset, defReplicateFolder, user, daliip, overwrite);
+        helper->cloneRoxieSubFile(srcLFN, srcCluster, logicalName, dstCluster, filePrefix, redundancy, channelsPerNode, replicateOffset, defReplicateFolder, user, daliip, updateFlags);
         flags |= RefFileCloned;
     }
     catch (IException *e)
@@ -448,7 +448,7 @@ void ReferencedFile::cloneInfo(IDFUhelper *helper, IUserDescriptor *user, const
     }
 }
 
-void ReferencedFile::cloneSuperInfo(ReferencedFileList *list, IUserDescriptor *user, INode *remote, bool overwrite)
+void ReferencedFile::cloneSuperInfo(unsigned updateFlags, ReferencedFileList *list, IUserDescriptor *user, INode *remote)
 {
     if ((flags & RefFileCloned) || (flags & RefFileInPackage) || !(flags & RefFileSuper) || !(flags & RefFileRemote))
         return;
@@ -463,7 +463,7 @@ void ReferencedFile::cloneSuperInfo(ReferencedFileList *list, IUserDescriptor *u
         Owned<IDistributedFile> df = dir.lookup(logicalName.str(), user);
         if(df)
         {
-            if (!overwrite)
+            if (!(updateFlags & DALI_UPDATEF_SUPERFILES))
                 return;
             df->detach();
             df.clear();
@@ -480,7 +480,7 @@ void ReferencedFile::cloneSuperInfo(ReferencedFileList *list, IUserDescriptor *u
                 //ensure superfile in superfile is cloned, before add
                 ReferencedFile *subref = list->map.getValue(name);
                 if (subref)
-                    subref->cloneSuperInfo(list, user, remote, overwrite);
+                    subref->cloneSuperInfo(updateFlags, list, user, remote);
             }
             if (name && *name)
                 superfile->addSubFile(name, false, NULL, false);
@@ -713,14 +713,14 @@ void ReferencedFileList::resolveFiles(const char *_process, const char *remoteIP
         resolveSubFiles(subfiles, checkLocalFirst, trackSubFiles, resolveForeign);
 }
 
-void ReferencedFileList::cloneFileInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
+void ReferencedFileList::cloneFileInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defReplicateFolder)
 {
     ReferencedFileIterator files(this);
     ForEach(files)
-        files.queryObject().cloneInfo(helper, user, process, srcCluster, overwrite, cloneForeign, redundancy, channelsPerNode, replicateOffset, defReplicateFolder);
+        files.queryObject().cloneInfo(updateFlags, helper, user, process, srcCluster, cloneForeign, redundancy, channelsPerNode, replicateOffset, defReplicateFolder);
     if (cloneSuperInfo)
         ForEach(files)
-            files.queryObject().cloneSuperInfo(this, user, remote, overwrite);
+            files.queryObject().cloneSuperInfo(updateFlags, this, user, remote);
 }
 
 void ReferencedFileList::cloneRelationships()

+ 2 - 2
common/workunit/referencedfilelist.hpp

@@ -61,8 +61,8 @@ interface IReferencedFileList : extends IInterface
 
     virtual IReferencedFileIterator *getFiles()=0;
     virtual void resolveFiles(const char *process, const char *remoteIP, const char * remotePrefix, const char *srcCluster, bool checkLocalFirst, bool addSubFiles, bool trackSubFiles, bool resolveForeign=false)=0;
-    virtual void cloneAllInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defRepFolder)=0;
-    virtual void cloneFileInfo(IDFUhelper *helper, bool overwrite, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defRepFolder)=0;
+    virtual void cloneAllInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defRepFolder)=0;
+    virtual void cloneFileInfo(unsigned updateFlags, IDFUhelper *helper, bool cloneSuperInfo, bool cloneForeign, unsigned redundancy, unsigned channelsPerNode, int replicateOffset, const char *defRepFolder)=0;
     virtual void cloneRelationships()=0;
 };
 

+ 199 - 20
dali/dfu/dfuutil.cpp

@@ -139,13 +139,13 @@ public:
     StringAttr cluster2;
     Owned<IGroup> grp2;
     ClusterPartDiskMapSpec spec2;
-    bool overwrite;
+    unsigned overwriteFlags;
     IDistributedFileDirectory *fdir;
     bool repeattlk;
     bool copyphysical;
     unsigned level;
 
-
+    CFileCloner() : overwriteFlags(0) {}
 
     void physicalCopyFile(IFileDescriptor *srcdesc,IFileDescriptor *dstdesc)
     {
@@ -405,21 +405,22 @@ public:
             throw afor2.exc.getClear();
     }
 
-    void updateCloneFrom(IFileDescriptor *srcfdesc, IFileDescriptor *dstfdesc, INode *srcdali, const char *srcCluster)
+    void updateCloneFrom(const char *lfn, IPropertyTree &attrs, IFileDescriptor *srcfdesc, INode *srcdali, const char *srcCluster)
     {
+        DBGLOG("updateCloneFrom %s", lfn);
         if (!srcdali || srcdali->endpoint().isNull())
-            dstfdesc->queryProperties().setProp("@cloneFromPeerCluster", srcCluster);
+            attrs.setProp("@cloneFromPeerCluster", srcCluster);
         else
         {
             StringBuffer s;
-            dstfdesc->queryProperties().setProp("@cloneFrom", srcdali->endpoint().getUrlStr(s).str());
-            dstfdesc->queryProperties().setProp("@cloneFromDir", srcfdesc->queryDefaultDir());
+            attrs.setProp("@cloneFrom", srcdali->endpoint().getUrlStr(s).str());
+            attrs.setProp("@cloneFromDir", srcfdesc->queryDefaultDir());
             if (srcCluster && *srcCluster) //where to copy from has been explicity set to a remote location, don't copy from local sources
-                dstfdesc->queryProperties().setProp("@cloneFromPeerCluster", "-");
+                attrs.setProp("@cloneFromPeerCluster", "-");
             if (prefix.length())
-                dstfdesc->queryProperties().setProp("@cloneFromPrefix", prefix.get());
+                attrs.setProp("@cloneFromPrefix", prefix.get());
 
-            while(dstfdesc->queryProperties().removeProp("cloneFromGroup"));
+            while(attrs.removeProp("cloneFromGroup"));
 
             unsigned numClusters = srcfdesc->numClusters();
             for (unsigned clusterNum = 0; clusterNum < numClusters; clusterNum++)
@@ -432,13 +433,25 @@ public:
                 groupInfo->setProp("@groupName", sourceGroup);
                 ClusterPartDiskMapSpec &spec = srcfdesc->queryPartDiskMapping(clusterNum);
                 spec.toProp(groupInfo);
-                dstfdesc->queryProperties().addPropTree("cloneFromGroup", groupInfo.getClear());
+                attrs.addPropTree("cloneFromGroup", groupInfo.getClear());
             }
         }
     }
+    void updateCloneFrom(IDistributedFile *dfile, IFileDescriptor *srcfdesc, INode *srcdali, const char *srcCluster)
+    {
+        DistributedFilePropertyLock lock(dfile);
+        IPropertyTree &attrs = lock.queryAttributes();
+        updateCloneFrom(dfile->queryLogicalName(), attrs, srcfdesc, srcdali, srcCluster);
+    }
+    void updateCloneFrom(const char *lfn, IFileDescriptor *dstfdesc, IFileDescriptor *srcfdesc, INode *srcdali, const char *srcCluster)
+    {
+        updateCloneFrom(lfn, dstfdesc->queryProperties(), srcfdesc, srcdali, srcCluster);
+    }
 
     void cloneSubFile(IPropertyTree *ftree,const char *destfilename, INode *srcdali, const char *srcCluster)   // name already has prefix added
     {
+        DBGLOG("cloneSubFile %s", destfilename);
+
         Owned<IFileDescriptor> srcfdesc = deserializeFileDescriptorTree(ftree, NULL, 0);
         const char * kind = srcfdesc->queryProperties().queryProp("@kind");
         bool iskey = kind&&(strcmp(kind,"key")==0);
@@ -481,16 +494,39 @@ public:
         }
 
         if (copyphysical) {
+            DBGLOG("copyphysical dst=%s", destfilename);
             physicalCopyFile(srcfdesc,dstfdesc);
             physicalReplicateFile(dstfdesc,destfilename);
         }
 
-        updateCloneFrom(srcfdesc, dstfdesc, srcdali, srcCluster);
+        updateCloneFrom(destfilename, dstfdesc, srcfdesc, srcdali, srcCluster);
 
         Owned<IDistributedFile> dstfile = fdir->createNew(dstfdesc);
         dstfile->attach(destfilename,userdesc);
     }
 
+    void addCluster(IPropertyTree *ftree,const char *destfilename)
+    {
+        CDfsLogicalFileName dstlfn;
+        if (!dstlfn.setValidate(destfilename,true))
+            throw MakeStringException(-1,"Logical name %s invalid",destfilename);
+        Owned<IDistributedFile> dfile = fdir->lookup(dstlfn,userdesc,true);
+        if (dfile) {
+            ClusterPartDiskMapSpec spec = spec1;
+            const char * kind = ftree->queryProp("Attr/@kind");
+            bool iskey = kind&&(strcmp(kind,"key")==0);
+            if (iskey&&repeattlk)
+                spec.setRepeatedCopies(CPDMSRP_lastRepeated,false);
+            dfile->addCluster(cluster1,spec);
+            if (iskey&&!cluster2.isEmpty())
+                dfile->addCluster(cluster2,spec2);
+            if (copyphysical) {
+                Owned<IFileDescriptor> fdesc=dfile->getFileDescriptor();
+                physicalReplicateFile(fdesc,destfilename);
+            }
+        }
+    }
+
     void extendSubFile(IPropertyTree *ftree,const char *destfilename)
     {
         CDfsLogicalFileName dstlfn;
@@ -532,7 +568,7 @@ public:
             foreignuserdesc.set(_foreignuserdesc);
         else if (_userdesc)
             foreignuserdesc.set(_userdesc);
-        overwrite = _overwrite;
+        overwriteFlags = _overwrite ? DALI_UPDATEF_REPLACE_FILE : 0;
         copyphysical = _copyphysical;
         nameprefix.set(_nameprefix);
         repeattlk = _repeattlk;
@@ -576,7 +612,7 @@ public:
                 spec2.setDefaultBaseDir(defdir2.str());
         }
     }
-
+    inline bool checkOverwrite(unsigned flag) { return (overwriteFlags & flag)!=0; }
     void cloneSuperFile(const char *filename, CDfsLogicalFileName &dlfn)
     {
         level++;
@@ -633,7 +669,7 @@ public:
         // first see if target exists (and remove if does and overwrite specified)
         Owned<IDistributedFile> dfile = fdir->lookup(dlfn,userdesc,true);
         if (dfile) {
-            if (!overwrite)
+            if (!checkOverwrite(DALI_UPDATEF_REPLACE_FILE))
                 throw MakeStringException(-1,"Destination file %s already exists",dlfn.get());
             dfile->detach();
             dfile.clear();
@@ -709,7 +745,7 @@ public:
         // first see if target exists (and remove if does and overwrite specified)
         Owned<IDistributedFile> dfile = fdir->lookup(dlfn,userdesc,true);
         if (dfile) {
-            if (!overwrite)
+            if (!checkOverwrite(DALI_UPDATEF_REPLACE_FILE))
                 throw MakeStringException(-1,"Destination file %s already exists",dlfn.get());
 
             IPropertyTree &attloc = dfile->queryAttributes();
@@ -719,7 +755,7 @@ public:
             {
                 Owned<IFileDescriptor> dstfdesc=dfile->getFileDescriptor();
                 Owned<IFileDescriptor> srcfdesc = deserializeFileDescriptorTree(ftree, NULL, 0);
-                updateCloneFrom(srcfdesc, dstfdesc, srcdali, srcCluster);
+                updateCloneFrom(filename, dstfdesc, srcfdesc, srcdali, srcCluster);
                 return;
             }
 
@@ -730,6 +766,148 @@ public:
         level--;
     }
 
+    inline const char *getDaliEndPointStr(INode *daliNode, StringBuffer &s)
+    {
+        if (!daliNode)
+            return "(local)";
+        return daliNode->endpoint().getUrlStr(s).str();
+    }
+    inline bool checkHasCluster(IDistributedFile *dfile)
+    {
+        StringArray clusterNames;
+        dfile->getClusterNames(clusterNames);
+        return clusterNames.find(cluster1)!=NotFound;
+    }
+    bool checkIsKey(IPropertyTree *ftree)
+    {
+        const char * kind = ftree->queryProp("Attr/@kind");
+        return kind && streq(kind, "key");
+    }
+    void addCluster(IDistributedFile *dfile, IPropertyTree *srcFtree)
+    {
+        if (dfile)
+        {
+            ClusterPartDiskMapSpec spec = spec1;
+            if (checkIsKey(srcFtree) && repeattlk)
+                spec.setRepeatedCopies(CPDMSRP_lastRepeated, false);
+            DBGLOG("addCluster %s to file %s", cluster1.str(), dfile->queryLogicalName());
+            dfile->addCluster(cluster1, spec);
+        }
+    }
+    bool checkFileChanged(IDistributedFile *dfile, IPropertyTree *srcftree, IPropertyTree *srcAttrs)
+    {
+        IPropertyTree &dstAttrs = dfile->queryAttributes();
+        return (dfile->numParts() != (unsigned) srcftree->getPropInt("@numparts") ||
+                srcAttrs->getPropInt("@eclCRC") != dstAttrs.getPropInt("@eclCRC") ||
+                srcAttrs->getPropInt("@totalCRC") != dstAttrs.getPropInt64("@totalCRC"));
+    }
+    inline bool checkValueChanged(const char *s1, const char *s2)
+    {
+        if (s1 && s2)
+            return !streq(s1, s2);
+        return ((s1 && *s1) || (s2 && *s2));
+    }
+    bool checkCloneFromChanged(IDistributedFile *dfile, IFileDescriptor *srcfdesc, INode *srcdali, const char *srcCluster)
+    {
+        if (!srcdali || srcdali->endpoint().isNull())
+        {
+            return checkValueChanged(dfile->queryAttributes().queryProp("@cloneFromPeerCluster"), srcCluster);
+        }
+        else
+        {
+            StringBuffer s;
+            if (checkValueChanged(dfile->queryAttributes().queryProp("@cloneFrom"), srcdali->endpoint().getUrlStr(s).str()))
+                return true;
+            if (checkValueChanged(dfile->queryAttributes().queryProp("@cloneFromDir"), srcfdesc->queryDefaultDir()))
+                return true;
+            if (checkValueChanged(dfile->queryAttributes().queryProp("@cloneFromPeerCluster"), (srcCluster && *srcCluster) ? "-" : NULL))
+                return true;
+            if (checkValueChanged(dfile->queryAttributes().queryProp("@cloneFromPrefix"), prefix.get()))
+                return true;
+
+            unsigned groupCount = dfile->queryAttributes().getCount("cloneFromGroup");
+            if (srcCluster && *srcCluster && groupCount!=1)
+                return true;
+
+            unsigned numSrcClusters = srcfdesc->numClusters();
+            if (numSrcClusters != groupCount)
+                return true;
+            StringArray clusterNames;
+            dfile->getClusterNames(clusterNames);
+            for (unsigned clusterNum = 0; clusterNum < numSrcClusters; clusterNum++)
+            {
+                StringBuffer sourceGroup;
+                srcfdesc->getClusterGroupName(clusterNum, sourceGroup, NULL);
+                if (!clusterNames.contains(sourceGroup.str()))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    void cloneRoxieFile(const char *filename, const char *destfilename)
+    {
+        Linked<INode> srcdali = foreigndalinode;
+        CDfsLogicalFileName srcLFN;
+        srcLFN.set(filename);
+        if (srcLFN.isForeign())
+        {
+            SocketEndpoint ep;
+            srcLFN.getEp(ep);
+            srcLFN.clearForeign();
+            srcdali.setown(createINode(ep));
+        }
+        StringBuffer s;
+        Owned<IPropertyTree> ftree = fdir->getFileTree(srcLFN.get(), foreignuserdesc, srcdali, FOREIGN_DALI_TIMEOUT, false);
+        if (!ftree.get())
+            throw MakeStringException(-1,"Source file %s could not be found in Dali %s",srcLFN.get(), getDaliEndPointStr(srcdali, s));
+        IPropertyTree *attsrc = ftree->queryPropTree("Attr");
+        if (!attsrc)
+            throw MakeStringException(-1,"Attributes for source file %s could not be found in Dali %s",srcLFN.get(), getDaliEndPointStr(srcdali, s));
+
+        CDfsLogicalFileName dlfn;
+        dlfn.set(destfilename);
+        if (!streq(ftree->queryName(),queryDfsXmlBranchName(DXB_File)))
+            throw MakeStringException(-1,"Source file %s in Dali %s is not a simple file",filename, getDaliEndPointStr(srcdali, s));
+
+        if (!srcdali.get()||queryCoven().inCoven(srcdali))
+        {
+            // if dali is local and filenames same
+            if (streq(srcLFN.get(), dlfn.get()))
+            {
+                extendSubFile(ftree,dlfn.get());
+                return;
+            }
+        }
+
+        //see if target already exists
+        Owned<IDistributedFile> dfile = fdir->lookup(dlfn, userdesc, true);
+        if (dfile)
+        {
+            if (!checkOverwrite(DALI_UPDATEF_SUBFILE_MASK))
+                throw MakeStringException(-1, "Destination file %s already exists", dlfn.get());
+
+            if (checkOverwrite(DALI_UPDATEF_REPLACE_FILE) && checkFileChanged(dfile, ftree, attsrc)) //complete overwrite
+            {
+                DBGLOG("replacing file %s", destfilename);
+                dfile->detach();
+                dfile.clear();
+            }
+            else
+            {
+                if (checkOverwrite(DALI_UPDATEF_APPEND_CLUSTER) && !checkHasCluster(dfile))
+                    addCluster(dfile, ftree);
+                if (checkOverwrite(DALI_UPDATEF_CLONE_FROM))
+                {
+                    Owned<IFileDescriptor> srcfdesc = deserializeFileDescriptorTree(ftree, NULL, 0);
+                    if (checkCloneFromChanged(dfile, srcfdesc, srcdali, srcCluster))
+                        updateCloneFrom(dfile, srcfdesc, srcdali, srcCluster);
+                }
+                return;
+            }
+        }
+        cloneSubFile(ftree,dlfn.get(),srcdali, srcCluster);
+    }
 
 };
 
@@ -1030,18 +1208,19 @@ public:
                          const char *defReplicateFolder,
                          IUserDescriptor *userdesc,                // user desc for local dali
                          const char *foreigndali,                  // can be omitted if srcname foreign or local
-                         bool overwrite                            // overwrite destination if exists
+                         unsigned overwriteFlags                            // overwrite destination if exists
                          )
     {
-        DBGLOG("cloneRoxieSubFile src=%s@%s, dst=%s@%s, prefix=%s, ow=%d, docopy=false", srcLFN, srcCluster, dstLFN, dstCluster, prefix, overwrite);
+        DBGLOG("cloneRoxieSubFile src=%s@%s, dst=%s@%s, prefix=%s, ow=%d, docopy=false", srcLFN, srcCluster, dstLFN, dstCluster, prefix, overwriteFlags);
         CFileCloner cloner;
-        cloner.init(dstCluster, DFUcpdm_c_replicated_by_d, true, NULL, userdesc, foreigndali, NULL, NULL, overwrite, false);
+        cloner.init(dstCluster, DFUcpdm_c_replicated_by_d, true, NULL, userdesc, foreigndali, NULL, NULL, false, false);
+        cloner.overwriteFlags = overwriteFlags;
         cloner.spec1.setRoxie(redundancy, channelsPerNode, replicateOffset);
         if (defReplicateFolder)
             cloner.spec1.setDefaultReplicateDir(defReplicateFolder);
         cloner.srcCluster.set(srcCluster);
         cloner.prefix.set(prefix);
-        cloner.cloneFile(srcLFN, dstLFN);
+        cloner.cloneRoxieFile(srcLFN, dstLFN);
     }
 
 

+ 9 - 1
dali/dfu/dfuutil.hpp

@@ -29,6 +29,14 @@ interface IDfuFileCopier: extends IInterface
     virtual bool wait()=0; // waits for all outstanding copies to complete
 };
 
+#define DALI_UPDATEF_REPLACE_FILE   0x0001
+#define DALI_UPDATEF_CLONE_FROM     0x0002
+#define DALI_UPDATEF_APPEND_CLUSTER 0x0004
+#define DALI_UPDATEF_SUPERFILES     0x0008
+#define DALI_UPDATEF_PACKAGEMAP     0x0100
+
+#define DALI_UPDATEF_SUBFILE_MASK (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_APPEND_CLUSTER)
+#define DALI_UPDATEF_MASK (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_APPEND_CLUSTER | DALI_UPDATEF_SUPERFILES | DALI_UPDATEF_PACKAGEMAP)
 
 interface IDFUhelper: extends IInterface
 {
@@ -82,7 +90,7 @@ interface IDFUhelper: extends IInterface
                          const char *defReplicateFolder,
                          IUserDescriptor *userdesc,                // user desc for local dali
                          const char *foreigndali,                  // can be omitted if srcname foreign or local
-                         bool overwrite                            // overwrite destination if exists
+                         unsigned overwriteFlags                   // overwrite destination options
                          ) = 0;
 
     virtual void cloneFileRelationships(

+ 32 - 11
ecl/ecl-package/ecl-package.cpp

@@ -481,7 +481,8 @@ private:
 class EclCmdPackageAdd : public EclCmdCommon
 {
 public:
-    EclCmdPackageAdd() : optActivate(false), optOverWrite(false), optGlobalScope(false), optAllowForeign(false), optPreloadAll(false)
+    EclCmdPackageAdd() : optActivate(false), optOverWrite(false), optGlobalScope(false), optAllowForeign(false), optPreloadAll(false),
+        optUpdateSuperfiles(false), optUpdateCloneFrom(false), optDontAppendCluster(false), optReplacePackagemap(false)
     {
     }
     virtual bool parseCommandLineOptions(ArgvIterator &iter)
@@ -515,6 +516,14 @@ public:
                 continue;
             if (iter.matchFlag(optOverWrite, ECLOPT_OVERWRITE)||iter.matchFlag(optOverWrite, ECLOPT_OVERWRITE_S))
                 continue;
+            if (iter.matchFlag(optReplacePackagemap, ECLOPT_REPLACE))
+                continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
             if (iter.matchFlag(optGlobalScope, ECLOPT_GLOBAL_SCOPE))
                 continue;
             if (iter.matchFlag(optAllowForeign, ECLOPT_ALLOW_FOREIGN))
@@ -577,6 +586,10 @@ public:
         request->setSourceProcess(optSourceProcess);
         request->setAllowForeignFiles(optAllowForeign);
         request->setPreloadAllPackages(optPreloadAll);
+        request->setReplacePackageMap(optReplacePackagemap);
+        request->setUpdateSuperFiles(optUpdateSuperfiles);
+        request->setUpdateCloneFrom(optUpdateCloneFrom);
+        request->setAppendCluster(!optDontAppendCluster);
 
         Owned<IClientAddPackageResponse> resp = packageProcessClient->AddPackage(request);
         if (resp->getExceptions().ordinality())
@@ -601,17 +614,21 @@ public:
                     "The 'add' command will add the package map information to dali \n"
                     "\n"
                     "ecl packagemap add [options] <target> <filename>\n"
+                    "   <target>                 Name of target to use when adding package map information\n"
+                    "   <filename>               Name of file containing package map information\n"
                     " Options:\n"
-                    "   -O, --overwrite             Overwrite existing information\n"
-                    "   -A, --activate              Activate the package information\n"
-                    "   --daliip=<ip>               IP of the remote dali to use for logical file lookups\n"
-                    "   --pmid                      Identifier of package map - defaults to filename if not specified\n"
-                    "   --global-scope              The specified packagemap can be shared across multiple targets\n"
-                    "   --source-process            Process cluster to copy files from\n"
-                    "   --allow-foreign             Do not fail if foreign files are used in packagemap\n"
-                    "   --preload-all               Set preload files option for all packages\n"
-                    "   <target>                    Name of target to use when adding package map information\n"
-                    "   <filename>                  Name of file containing package map information\n",
+                    "   -O, --overwrite          Replace existing packagemap and file information (dangerous)\n"
+                    "   -A, --activate           Activate the package information\n"
+                    "   --daliip=<ip>            IP of the remote dali to use for logical file lookups\n"
+                    "   --pmid                   Identifier of package map - defaults to filename if not specified\n"
+                    "   --global-scope           The specified packagemap can be shared across multiple targets\n"
+                    "   --source-process         Process cluster to copy files from\n"
+                    "   --allow-foreign          Do not fail if foreign files are used in packagemap\n"
+                    "   --preload-all            Set preload files option for all packages\n"
+                    "   --replace                Replace existing packagmap"
+                    "   --update-super-files     Update local DFS super-files if remote DALI has changed\n"
+                    "   --update-clone-from      Update local clone from location if remote DALI has changed\n"
+                    "   --dont-append-cluster    Only use to avoid locking issues due to adding cluster to file\n",
                     stdout);
 
         EclCmdCommon::usage();
@@ -626,6 +643,10 @@ private:
     StringAttr optSourceProcess;
     bool optActivate;
     bool optOverWrite;
+    bool optReplacePackagemap;
+    bool optUpdateSuperfiles;
+    bool optUpdateCloneFrom;
+    bool optDontAppendCluster; //Undesirable but here temporarily because DALI may have locking issues
     bool optGlobalScope;
     bool optAllowForeign;
     bool optPreloadAll;

+ 4 - 0
ecl/eclcmd/eclcmd_common.hpp

@@ -61,6 +61,7 @@ typedef IEclCommand *(*EclCommandFactory)(const char *cmdname);
 
 #define ECLOPT_NORELOAD "--no-reload"
 #define ECLOPT_OVERWRITE "--overwrite"
+#define ECLOPT_REPLACE "--replace"
 #define ECLOPT_OVERWRITE_S "-O"
 #define ECLOPT_OVERWRITE_INI "overwriteDefault"
 #define ECLOPT_OVERWRITE_ENV NULL
@@ -95,6 +96,9 @@ typedef IEclCommand *(*EclCommandFactory)(const char *cmdname);
 #define ECLOPT_CHECK_ALL_NODES "--check-all-nodes"
 #define ECLOPT_CHECK_ALL_NODES_INI "checkAllNodes"
 #define ECLOPT_CHECK_ALL_NODES_ENV "CHECK_ALL_NODES"
+#define ECLOPT_UPDATE_SUPER_FILES "--update-super-files"
+#define ECLOPT_UPDATE_CLONE_FROM "--update-clone-from"
+#define ECLOPT_DONT_APPEND_CLUSTER "--dont-append-cluster"
 
 #define ECLOPT_MAIN "--main"
 #define ECLOPT_MAIN_S "-main"  //eclcc compatible format

+ 18 - 2
ecl/eclcmd/eclcmd_core.cpp

@@ -280,7 +280,8 @@ class EclCmdPublish : public EclCmdWithEclTarget
 {
 public:
     EclCmdPublish() : optNoActivate(false), optSuspendPrevious(false), optDeletePrevious(false),
-        activateSet(false), optNoReload(false), optDontCopyFiles(false), optMsToWait(10000), optAllowForeign(false), optUpdateDfs(false)
+        activateSet(false), optNoReload(false), optDontCopyFiles(false), optMsToWait(10000), optAllowForeign(false), optUpdateDfs(false),
+        optUpdateSuperfiles(false), optUpdateCloneFrom(false), optDontAppendCluster(false)
     {
         optObj.accept = eclObjWuid | eclObjArchive | eclObjSharedObject;
         optTimeLimit = (unsigned) -1;
@@ -337,6 +338,12 @@ public:
                 continue;
             if (iter.matchFlag(optUpdateDfs, ECLOPT_UPDATE_DFS))
                 continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
             if (EclCmdWithEclTarget::matchCommandLineOption(iter, true)!=EclCmdOptionMatch)
                 return false;
         }
@@ -413,6 +420,9 @@ public:
         req->setDontCopyFiles(optDontCopyFiles);
         req->setAllowForeignFiles(optAllowForeign);
         req->setUpdateDfs(optUpdateDfs);
+        req->setUpdateSuperFiles(optUpdateSuperfiles);
+        req->setUpdateCloneFrom(optUpdateCloneFrom);
+        req->setAppendCluster(!optDontAppendCluster);
 
         if (optTimeLimit != (unsigned) -1)
             req->setTimeLimit(optTimeLimit);
@@ -470,7 +480,10 @@ public:
             "   --no-files             Do not copy DFS file information for referenced files\n"
             "   --allow-foreign        Do not fail if foreign files are used in query (roxie)\n"
             "   --daliip=<IP>          The IP of the DALI to be used to locate remote files\n"
-            "   --update-dfs           Update local DFS info if remote DALI has changed\n"
+            "   -O, --overwrite        Completely replace existing DFS file information (dangerous)\n"
+            "   --update-super-files   Update local DFS super-files if remote DALI has changed\n"
+            "   --update-clone-from    Update local clone from location if remote DALI has changed\n"
+            "   --dont-append-cluster  Only use to avoid locking issues due to adding cluster to file\n"
             "   --source-process       Process cluster to copy files from\n"
             "   --timeLimit=<ms>       Value to set for query timeLimit configuration\n"
             "   --warnTimeLimit=<ms>   Value to set for query warnTimeLimit configuration\n"
@@ -501,6 +514,9 @@ private:
     bool optDeletePrevious;
     bool optAllowForeign;
     bool optUpdateDfs;
+    bool optUpdateSuperfiles;
+    bool optUpdateCloneFrom;
+    bool optDontAppendCluster; //Undesirable but here temporarily because DALI may have locking issues
 };
 
 class EclCmdRun : public EclCmdWithEclTarget

+ 42 - 4
ecl/eclcmd/queries/ecl-queries.cpp

@@ -406,7 +406,8 @@ private:
 class EclCmdQueriesCopy : public EclCmdCommon
 {
 public:
-    EclCmdQueriesCopy() : optActivate(false), optNoReload(false), optMsToWait(10000), optDontCopyFiles(false), optOverwrite(false), optAllowForeign(false)
+    EclCmdQueriesCopy() : optActivate(false), optNoReload(false), optMsToWait(10000), optDontCopyFiles(false), optOverwrite(false), optAllowForeign(false),
+        optUpdateSuperfiles(false), optUpdateCloneFrom(false), optDontAppendCluster(false)
     {
         optTimeLimit = (unsigned) -1;
         optWarnTimeLimit = (unsigned) -1;
@@ -462,6 +463,18 @@ public:
                 continue;
             if (iter.matchFlag(optOverwrite, ECLOPT_OVERWRITE)||iter.matchFlag(optOverwrite, ECLOPT_OVERWRITE_S))
                 continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
             if (iter.matchOption(optName, ECLOPT_NAME)||iter.matchOption(optName, ECLOPT_NAME_S))
                 continue;
             if (EclCmdCommon::matchCommandLineOption(iter, true)!=EclCmdOptionMatch)
@@ -503,6 +516,9 @@ public:
         req->setSourceProcess(optSourceProcess);
         req->setActivate(optActivate);
         req->setOverwrite(optOverwrite);
+        req->setUpdateSuperFiles(optUpdateSuperfiles);
+        req->setUpdateCloneFrom(optUpdateCloneFrom);
+        req->setAppendCluster(!optDontAppendCluster);
         req->setDontCopyFiles(optDontCopyFiles);
         req->setWait(optMsToWait);
         req->setNoReload(optNoReload);
@@ -554,7 +570,10 @@ public:
             "   --source-process       Process cluster to copy files from\n"
             "   -A, --activate         Activate the new query\n"
             "   --no-reload            Do not request a reload of the (roxie) cluster\n"
-            "   -O, --overwrite        Overwrite existing files\n"
+            "   -O, --overwrite        Completely replace existing DFS file information (dangerous)\n"
+            "   --update-super-files   Update local DFS super-files if remote DALI has changed\n"
+            "   --update-clone-from    Update local clone from location if remote DALI has changed\n"
+            "   --dont-append-cluster  Only use to avoid locking issues due to adding cluster to file\n"
             "   --allow-foreign        Do not fail if foreign files are used in query (roxie)\n"
             "   --wait=<ms>            Max time to wait in milliseconds\n"
             "   --timeLimit=<sec>      Value to set for query timeLimit configuration\n"
@@ -585,6 +604,9 @@ private:
     bool optActivate;
     bool optNoReload;
     bool optOverwrite;
+    bool optUpdateSuperfiles;
+    bool optUpdateCloneFrom;
+    bool optDontAppendCluster; //Undesirable but here temporarily because DALI may have locking issues
     bool optDontCopyFiles;
     bool optAllowForeign;
 };
@@ -592,7 +614,8 @@ private:
 class EclCmdQueriesCopyQueryset : public EclCmdCommon
 {
 public:
-    EclCmdQueriesCopyQueryset() : optCloneActiveState(false), optAllQueries(false), optDontCopyFiles(false), optOverwrite(false), optAllowForeign(false)
+    EclCmdQueriesCopyQueryset() : optCloneActiveState(false), optAllQueries(false), optDontCopyFiles(false), optOverwrite(false), optAllowForeign(false),
+        optUpdateSuperfiles(false), optUpdateCloneFrom(false), optDontAppendCluster(false)
     {
     }
     virtual bool parseCommandLineOptions(ArgvIterator &iter)
@@ -630,6 +653,12 @@ public:
                 continue;
             if (iter.matchFlag(optOverwrite, ECLOPT_OVERWRITE)||iter.matchFlag(optOverwrite, ECLOPT_OVERWRITE_S))
                 continue;
+            if (iter.matchFlag(optUpdateSuperfiles, ECLOPT_UPDATE_SUPER_FILES))
+                continue;
+            if (iter.matchFlag(optUpdateCloneFrom, ECLOPT_UPDATE_CLONE_FROM))
+                continue;
+            if (iter.matchFlag(optDontAppendCluster, ECLOPT_DONT_APPEND_CLUSTER))
+                continue;
             if (EclCmdCommon::matchCommandLineOption(iter, true)!=EclCmdOptionMatch)
                 return false;
         }
@@ -658,6 +687,9 @@ public:
         req->setSourceProcess(optSourceProcess);
         req->setCloneActiveState(optCloneActiveState);
         req->setOverwriteDfs(optOverwrite);
+        req->setUpdateSuperFiles(optUpdateSuperfiles);
+        req->setUpdateCloneFrom(optUpdateCloneFrom);
+        req->setAppendCluster(!optDontAppendCluster);
         req->setCopyFiles(!optDontCopyFiles);
         req->setAllowForeignFiles(optAllowForeign);
 
@@ -708,7 +740,10 @@ public:
             "   --daliip=<ip>          Remote Dali DFS to use for copying file information\n"
             "   --source-process       Process cluster to copy files from\n"
             "   --clone-active-state   Make copied queries active if active on source\n"
-            "   -O, --overwrite        Overwrite existing DFS file information\n"
+            "   -O, --overwrite        Completely replace existing DFS file information (dangerous)\n"
+            "   --update-super-files   Update local DFS super-files if remote DALI has changed\n"
+            "   --update-clone-from    Update local clone from location if remote DALI has changed\n"
+            "   --dont-append-cluster  Only use to avoid locking issues due to adding cluster to file\n"
             "   --allow-foreign        Do not fail if foreign files are used in query (roxie)\n"
             " Common Options:\n",
             stdout);
@@ -721,6 +756,9 @@ private:
     StringAttr optSourceProcess;
     bool optCloneActiveState;
     bool optOverwrite;
+    bool optUpdateSuperfiles;
+    bool optUpdateCloneFrom;
+    bool optDontAppendCluster; //Undesirable but here temporarily because DALI may have locking issues
     bool optDontCopyFiles;
     bool optAllowForeign;
     bool optAllQueries;

+ 5 - 1
esp/scm/ws_packageprocess.ecm

@@ -29,7 +29,7 @@ ESPrequest AddPackageRequest
 {
     string Info;
     boolean Activate;
-    boolean OverWrite;
+    boolean OverWrite; //deprecated, use flags below
     string Target;
     string PackageMap;
     string Process;
@@ -38,6 +38,10 @@ ESPrequest AddPackageRequest
     string SourceProcess;
     bool AllowForeignFiles(true);
     [min_ver("1.02")] bool PreloadAllPackages(false);
+    bool ReplacePackageMap(false);
+    bool UpdateSuperFiles(false); //usually wouldn't be needed, packagemap referencing superfiles?
+    bool UpdateCloneFrom(false); //explicitly wan't to change where roxie will grab from
+    bool AppendCluster(true); //file exists on other local cluster, add new one, make optional in case of locking issues, but should be made to work
 };
 
 

+ 9 - 0
esp/scm/ws_workunits.ecm

@@ -1213,6 +1213,9 @@ ESPrequest [nil_remove] WUPublishWorkunitRequest
     string SourceProcess;
     bool AllowForeignFiles(false);
     bool UpdateDfs(false);
+    bool UpdateSuperFiles(false); //update content of superfiles if changed
+    bool UpdateCloneFrom(false); //explicity wan't to change where roxie will grab from
+    bool AppendCluster(true); //file exists on other local cluster, add new one, make optional in case of locking issues, but should be made to work
 };
 
 ESPresponse [exceptions_inline] WUPublishWorkunitResponse
@@ -1577,6 +1580,9 @@ ESPrequest [nil_remove] WUQuerySetCopyQueryRequest
     string SourceProcess;
     string DestName;
     bool AllowForeignFiles(true);
+    bool UpdateSuperFiles(false); //usually wouldn't be needed, packagemap referencing superfiles?
+    bool UpdateCloneFrom(false); //explicity wan't to change where roxie will grab from
+    bool AppendCluster(true); //file exists on other local cluster, add new one, make optional in case of locking issues, but should be made to work
 };
 
 ESPresponse [exceptions_inline] WUQuerySetCopyQueryResponse
@@ -1596,6 +1602,9 @@ ESPrequest [nil_remove] WUCopyQuerySetRequest
     bool CopyFiles(true);
     bool OverwriteDfs(false);
     string SourceProcess;
+    bool UpdateSuperFiles(false); //usually wouldn't be needed, packagemap referencing superfiles?
+    bool UpdateCloneFrom(false); //explicity wan't to change where roxie will grab from
+    bool AppendCluster(true); //file exists on other local cluster, add new one, make optional in case of locking issues, but should be made to work
 };
 
 ESPresponse [exceptions_inline] WUCopyQuerySetResponse

+ 24 - 11
esp/services/ws_packageprocess/ws_packageprocessService.cpp

@@ -124,7 +124,7 @@ bool isFileKnownOnCluster(const char *logicalname, const char *target, IUserDesc
     return isFileKnownOnCluster(logicalname, clusterInfo, userdesc);
 }
 
-void cloneFileInfoToDali(StringArray &notFound, IPropertyTree *packageMap, const char *lookupDaliIp, IConstWUClusterInfo *dstInfo, const char *srcCluster, const char *remotePrefix, bool overWrite, IUserDescriptor* userdesc, bool allowForeignFiles)
+void cloneFileInfoToDali(unsigned updateFlags, StringArray &notFound, IPropertyTree *packageMap, const char *lookupDaliIp, IConstWUClusterInfo *dstInfo, const char *srcCluster, const char *remotePrefix, IUserDescriptor* userdesc, bool allowForeignFiles)
 {
     StringBuffer user;
     StringBuffer password;
@@ -139,13 +139,13 @@ void cloneFileInfoToDali(StringArray &notFound, IPropertyTree *packageMap, const
     wufiles->addFilesFromPackageMap(packageMap);
     SCMStringBuffer processName;
     dstInfo->getRoxieProcess(processName);
-    wufiles->resolveFiles(processName.str(), lookupDaliIp, remotePrefix, srcCluster, !overWrite, false, false);
+    wufiles->resolveFiles(processName.str(), lookupDaliIp, remotePrefix, srcCluster, !(updateFlags & (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM)), false, false);
 
     StringBuffer defReplicateFolder;
     getConfigurationDirectory(NULL, "data2", "roxie", processName.str(), defReplicateFolder);
 
     Owned<IDFUhelper> helper = createIDFUhelper();
-    wufiles->cloneAllInfo(helper, overWrite, true, false, dstInfo->getRoxieRedundancy(), dstInfo->getChannelsPerNode(), dstInfo->getRoxieReplicateOffset(), defReplicateFolder);
+    wufiles->cloneAllInfo(updateFlags, helper, true, false, dstInfo->getRoxieRedundancy(), dstInfo->getChannelsPerNode(), dstInfo->getRoxieReplicateOffset(), defReplicateFolder);
 
     Owned<IReferencedFileIterator> iter = wufiles->getFiles();
     ForEach(*iter)
@@ -156,13 +156,13 @@ void cloneFileInfoToDali(StringArray &notFound, IPropertyTree *packageMap, const
     }
 }
 
-void cloneFileInfoToDali(StringArray &notFound, IPropertyTree *packageMap, const char *lookupDaliIp, const char *dstCluster, const char *srcCluster, const char *prefix, bool overWrite, IUserDescriptor* userdesc, bool allowForeignFiles)
+void cloneFileInfoToDali(unsigned updateFlags, StringArray &notFound, IPropertyTree *packageMap, const char *lookupDaliIp, const char *dstCluster, const char *srcCluster, const char *prefix, IUserDescriptor* userdesc, bool allowForeignFiles)
 {
     Owned<IConstWUClusterInfo> clusterInfo = getTargetClusterInfo(dstCluster);
     if (!clusterInfo)
         throw MakeStringException(PKG_TARGET_NOT_DEFINED, "Could not find information about target cluster %s ", dstCluster);
 
-    cloneFileInfoToDali(notFound, packageMap, lookupDaliIp, clusterInfo, srcCluster, prefix, overWrite, userdesc, allowForeignFiles);
+    cloneFileInfoToDali(updateFlags, notFound, packageMap, lookupDaliIp, clusterInfo, srcCluster, prefix, userdesc, allowForeignFiles);
 }
 
 void makePackageActive(IPropertyTree *pkgSet, IPropertyTree *psEntryNew, const char *target, bool activate)
@@ -184,7 +184,7 @@ void makePackageActive(IPropertyTree *pkgSet, IPropertyTree *psEntryNew, const c
 
 //////////////////////////////////////////////////////////
 
-void addPackageMapInfo(const char *xml, StringArray &filesNotFound, const char *process, const char *target, const char *pmid, const char *packageSetName, const char *lookupDaliIp, const char *srcCluster, const char *prefix, bool activate, bool overWrite, IUserDescriptor* userdesc, bool allowForeignFiles, bool preloadAll)
+void addPackageMapInfo(unsigned updateFlags, const char *xml, StringArray &filesNotFound, const char *process, const char *target, const char *pmid, const char *packageSetName, const char *lookupDaliIp, const char *srcCluster, const char *prefix, bool activate, IUserDescriptor* userdesc, bool allowForeignFiles, bool preloadAll)
 {
     if (!xml || !*xml)
         throw MakeStringExceptionDirect(PKG_INFO_NOT_DEFINED, "PackageMap info not provided");
@@ -246,10 +246,10 @@ void addPackageMapInfo(const char *xml, StringArray &filesNotFound, const char *
     Owned<IPropertyTree> pkgSet = getPkgSetRegistry(process, false);
     IPropertyTree *psEntry = pkgSet->queryPropTree(xpath);
 
-    if (!overWrite && (psEntry || pmExisting))
-        throw MakeStringException(PKG_NAME_EXISTS, "Package name %s already exists, either delete it or specify overwrite", pmid);
+    if (!(updateFlags & DALI_UPDATEF_PACKAGEMAP) && (psEntry || pmExisting))
+        throw MakeStringException(PKG_NAME_EXISTS, "Package name %s already exists, either delete it or specify replace packagemap", pmid);
 
-    cloneFileInfoToDali(filesNotFound, pmTree, lookupDaliIp, clusterInfo, srcCluster, prefix, overWrite, userdesc, allowForeignFiles);
+    cloneFileInfoToDali(updateFlags, filesNotFound, pmTree, lookupDaliIp, clusterInfo, srcCluster, prefix, userdesc, allowForeignFiles);
 
     if (pmExisting)
         packageMaps->removeTree(pmExisting);
@@ -538,13 +538,26 @@ bool CWsPackageProcessEx::onAddPackage(IEspContext &context, IEspAddPackageReque
     if (name.isEmpty())
         throw MakeStringExceptionDirect(PKG_MISSING_PARAM, "PackageMap name parameter required");
 
+    DBGLOG("%s adding packagemap %s to target %s", context.queryUserId(), name.str(), target.str());
+
     StringBuffer pmid;
     if (!req.getGlobalScope())
         pmid.append(target).append("::");
     pmid.append(name.get());
 
     bool activate = req.getActivate();
-    bool overWrite = req.getOverWrite();
+    unsigned updateFlags = 0;
+    if (req.getOverWrite())
+        updateFlags |= (DALI_UPDATEF_PACKAGEMAP | DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_SUPERFILES);
+    if (req.getReplacePackageMap())
+        updateFlags |= DALI_UPDATEF_PACKAGEMAP;
+    if (req.getUpdateCloneFrom())
+        updateFlags |= DALI_UPDATEF_CLONE_FROM;
+    if (req.getUpdateSuperFiles())
+        updateFlags |= DALI_UPDATEF_SUPERFILES;
+    if (req.getAppendCluster())
+        updateFlags |= DALI_UPDATEF_APPEND_CLUSTER;
+
     StringAttr processName(req.getProcess());
 
     Owned<IUserDescriptor> userdesc;
@@ -565,7 +578,7 @@ bool CWsPackageProcessEx::onAddPackage(IEspContext &context, IEspAddPackageReque
     buildPkgSetId(pkgSetId, processName.get());
 
     StringArray filesNotFound;
-    addPackageMapInfo(req.getInfo(), filesNotFound, processName, target, pmid, pkgSetId, daliip, srcCluster, prefix, activate, overWrite, userdesc, req.getAllowForeignFiles(), req.getPreloadAllPackages());
+    addPackageMapInfo(updateFlags, req.getInfo(), filesNotFound, processName, target, pmid, pkgSetId, daliip, srcCluster, prefix, activate, userdesc, req.getAllowForeignFiles(), req.getPreloadAllPackages());
     resp.setFilesNotFound(filesNotFound);
 
     StringBuffer msg;

+ 48 - 12
esp/services/ws_workunits/ws_workunitsQuerySets.cpp

@@ -671,7 +671,7 @@ static inline void updateQueryPriority(IPropertyTree *queryTree, const char *val
     }
 }
 
-void copyQueryFilesToCluster(IEspContext &context, IConstWorkUnit *cw, const char *remoteIP, const char *remotePrefix, const char *target, const char *srcCluster, const char *queryname, bool overwrite, bool allowForeignFiles)
+void copyQueryFilesToCluster(unsigned updateFlags, IEspContext &context, IConstWorkUnit *cw, const char *remoteIP, const char *remotePrefix, const char *target, const char *srcCluster, const char *queryname, bool allowForeignFiles)
 {
     if (!target || !*target)
         return;
@@ -689,11 +689,11 @@ void copyQueryFilesToCluster(IEspContext &context, IConstWorkUnit *cw, const cha
         if (queryname && *queryname)
             queryname = queryid.append(queryname).append(".0").str(); //prepublish dummy version number to support fuzzy match like queries="myquery.*" in package
         wufiles->addFilesFromQuery(cw, (ps) ? ps->queryActiveMap(target) : NULL, queryname);
-        wufiles->resolveFiles(process.str(), remoteIP, remotePrefix, srcCluster, !overwrite, true, false, true);
+        wufiles->resolveFiles(process.str(), remoteIP, remotePrefix, srcCluster, !(updateFlags & (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_SUPERFILES)), true, false, true);
         StringBuffer defReplicateFolder;
         getConfigurationDirectory(NULL, "data2", "roxie", process.str(), defReplicateFolder);
         Owned<IDFUhelper> helper = createIDFUhelper();
-        wufiles->cloneAllInfo(helper, overwrite, true, true, clusterInfo->getRoxieRedundancy(), clusterInfo->getChannelsPerNode(), clusterInfo->getRoxieReplicateOffset(), defReplicateFolder);
+        wufiles->cloneAllInfo(updateFlags, helper, true, true, clusterInfo->getRoxieRedundancy(), clusterInfo->getChannelsPerNode(), clusterInfo->getRoxieReplicateOffset(), defReplicateFolder);
     }
 }
 
@@ -766,6 +766,9 @@ bool CWsWorkunitsEx::onWUPublishWorkunit(IEspContext &context, IEspWUPublishWork
         throw MakeStringException(ECLWATCH_MISSING_PARAMS, "Cluster name not defined for publishing workunit %s", wuid.str());
     if (!isValidCluster(target.str()))
         throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "Invalid cluster name: %s", target.str());
+
+    DBGLOG("%s publishing wuid %s to target %s as query %s", context.queryUserId(), wuid.str(), target.str(), queryName.str());
+
     StringBuffer daliIP;
     StringBuffer srcCluster;
     StringBuffer srcPrefix;
@@ -776,9 +779,18 @@ bool CWsWorkunitsEx::onWUPublishWorkunit(IEspContext &context, IEspWUPublishWork
         if (!isProcessCluster(daliIP, srcCluster))
             throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "Process cluster %s not found on %s DALI", srcCluster.str(), daliIP.length() ? daliIP.str() : "local");
     }
+    unsigned updateFlags = 0;
+    if (req.getUpdateDfs())
+        updateFlags |= (DALI_UPDATEF_SUPERFILES | DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM);
+    if (req.getUpdateCloneFrom())
+        updateFlags |= DALI_UPDATEF_CLONE_FROM;
+    if (req.getUpdateSuperFiles())
+        updateFlags |= DALI_UPDATEF_SUPERFILES;
+    if (req.getAppendCluster())
+        updateFlags |= DALI_UPDATEF_APPEND_CLUSTER;
 
     if (!req.getDontCopyFiles())
-        copyQueryFilesToCluster(context, cw, daliIP, srcPrefix, target.str(), srcCluster, queryName.str(), req.getUpdateDfs(), req.getAllowForeignFiles());
+        copyQueryFilesToCluster(updateFlags, context, cw, daliIP, srcPrefix, target.str(), srcCluster, queryName.str(), req.getAllowForeignFiles());
 
     WorkunitUpdate wu(&cw->lock());
     if (req.getUpdateWorkUnitName() && notEmpty(req.getJobName()))
@@ -2097,7 +2109,7 @@ class QueryCloner
 {
 public:
     QueryCloner(IEspContext *_context, const char *address, const char *source, const char *_target) :
-        context(_context), cloneFilesEnabled(false), target(_target), overwriteDfs(false), srcAddress(address)
+        context(_context), cloneFilesEnabled(false), target(_target), updateFlags(0), srcAddress(address)
     {
         if (srcAddress.length())
             srcQuerySet.setown(fetchRemoteQuerySetInfo(context, srcAddress, source));
@@ -2272,10 +2284,10 @@ public:
         else
             cloneAllLocal(cloneActiveState);
     }
-    void enableFileCloning(const char *dfsServer, const char *destProcess, const char *sourceProcess, bool _overwriteDfs, bool allowForeign)
+    void enableFileCloning(unsigned _updateFlags, const char *dfsServer, const char *destProcess, const char *sourceProcess, bool allowForeign)
     {
         cloneFilesEnabled = true;
-        overwriteDfs = _overwriteDfs;
+        updateFlags = _updateFlags;
         splitDerivedDfsLocation(dfsServer, srcCluster, dfsIP, srcPrefix, sourceProcess, sourceProcess, NULL, NULL);
         wufiles.setown(createReferencedFileList(context->queryUserId(), context->queryPassword(), allowForeign, false));
         Owned<IHpccPackageSet> ps = createPackageSet(destProcess);
@@ -2287,7 +2299,7 @@ public:
     {
         if (cloneFilesEnabled)
         {
-            wufiles->resolveFiles(process, dfsIP, srcPrefix, srcCluster, !overwriteDfs, true, false, true);
+            wufiles->resolveFiles(process, dfsIP, srcPrefix, srcCluster, !(updateFlags & (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM)), true, false, true);
             Owned<IDFUhelper> helper = createIDFUhelper();
             Owned <IConstWUClusterInfo> cl = getTargetClusterInfo(target);
             if (cl)
@@ -2295,7 +2307,7 @@ public:
                 SCMStringBuffer process;
                 StringBuffer defReplicateFolder;
                 getConfigurationDirectory(NULL, "data2", "roxie", cl->getRoxieProcess(process).str(), defReplicateFolder);
-                wufiles->cloneAllInfo(helper, overwriteDfs, true, true, cl->getRoxieRedundancy(), cl->getChannelsPerNode(), cl->getRoxieReplicateOffset(), defReplicateFolder);
+                wufiles->cloneAllInfo(updateFlags, helper, true, true, cl->getRoxieRedundancy(), cl->getChannelsPerNode(), cl->getRoxieReplicateOffset(), defReplicateFolder);
             }
         }
     }
@@ -2313,7 +2325,7 @@ private:
     StringAttr target;
     StringAttr process;
     bool cloneFilesEnabled;
-    bool overwriteDfs;
+    unsigned updateFlags;
 
 public:
     StringArray existingQueryIds;
@@ -2339,6 +2351,8 @@ bool CWsWorkunitsEx::onWUCopyQuerySet(IEspContext &context, IEspWUCopyQuerySetRe
     if (!isValidCluster(target))
         throw MakeStringException(ECLWATCH_INVALID_CLUSTER_NAME, "Invalid destination target name: %s", target);
 
+    DBGLOG("%s copying queryset %s from %s target %s", context.queryUserId(), target, srcAddress.str(), srcTarget.str());
+
     QueryCloner cloner(&context, srcAddress, srcTarget, target);
 
     SCMStringBuffer process;
@@ -2350,7 +2364,17 @@ bool CWsWorkunitsEx::onWUCopyQuerySet(IEspContext &context, IEspWUCopyQuerySetRe
             clusterInfo->getRoxieProcess(process);
             if (!process.length())
                 throw MakeStringException(ECLWATCH_INVALID_CLUSTER_INFO, "DFS process cluster not found for destination target %s", target);
-            cloner.enableFileCloning(req.getDfsServer(), process.str(), req.getSourceProcess(), req.getOverwriteDfs(), req.getAllowForeignFiles());
+            unsigned updateFlags = 0;
+            if (req.getOverwriteDfs())
+                updateFlags |= (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_SUPERFILES);
+            if (req.getUpdateCloneFrom())
+                updateFlags |= DALI_UPDATEF_CLONE_FROM;
+            if (req.getUpdateSuperFiles())
+                updateFlags |= DALI_UPDATEF_SUPERFILES;
+            if (req.getAppendCluster())
+                updateFlags |= DALI_UPDATEF_APPEND_CLUSTER;
+
+            cloner.enableFileCloning(updateFlags, req.getDfsServer(), process.str(), req.getSourceProcess(), req.getAllowForeignFiles());
         }
     }
 
@@ -2391,6 +2415,8 @@ bool CWsWorkunitsEx::onWUQuerysetCopyQuery(IEspContext &context, IEspWUQuerySetC
     Owned<IClientWUQuerySetDetailsResponse> sourceQueryInfoResp;
     IConstQuerySetQuery *srcInfo=NULL;
 
+    DBGLOG("%s copying query %s to target %s from %s target %s", context.queryUserId(), srcQuery.str(), target, srcAddress.str(), srcQuerySet.str());
+
     StringBuffer remoteIP;
     StringBuffer wuid;
     if (srcAddress.length())
@@ -2437,7 +2463,17 @@ bool CWsWorkunitsEx::onWUQuerysetCopyQuery(IEspContext &context, IEspWUQuerySetC
         StringBuffer srcCluster;
         StringBuffer srcPrefix;
         splitDerivedDfsLocation(req.getDaliServer(), srcCluster, daliIP, srcPrefix, req.getSourceProcess(), req.getSourceProcess(), remoteIP.str(), NULL);
-        copyQueryFilesToCluster(context, cw, daliIP.str(), srcPrefix, target, srcCluster, targetQueryName.get(), req.getOverwrite(), req.getAllowForeignFiles());
+        unsigned updateFlags = 0;
+        if (req.getOverwrite())
+            updateFlags |= (DALI_UPDATEF_REPLACE_FILE | DALI_UPDATEF_CLONE_FROM | DALI_UPDATEF_SUPERFILES);
+        if (req.getUpdateCloneFrom())
+            updateFlags |= DALI_UPDATEF_CLONE_FROM;
+        if (req.getUpdateSuperFiles())
+            updateFlags |= DALI_UPDATEF_SUPERFILES;
+        if (req.getAppendCluster())
+            updateFlags |= DALI_UPDATEF_APPEND_CLUSTER;
+
+        copyQueryFilesToCluster(updateFlags, context, cw, daliIP.str(), srcPrefix, target, srcCluster, targetQueryName.get(), req.getAllowForeignFiles());
     }
 
     WorkunitUpdate wu(&cw->lock());

+ 3 - 3
initfiles/etc/bash_completion/ecl

@@ -89,7 +89,7 @@ _ecl_opts_queries()
             echo "--help list copy config"
             ;;
         copy)
-            echo -n "--no-reload --allow-foreign --wait= --timeLimit= --warnTimeLimit= --memoryLimit= --priority= --daliip= "
+            echo -n "--no-reload --allow-foreign --wait= --timeLimit= --warnTimeLimit= --memoryLimit= --priority= --daliip= --update-super-files --update-clone-from --dont-append-cluster "
             _ecl_opts_common
             ;;
         config)
@@ -140,7 +140,7 @@ _ecl_opts_packagemap()
             then
                 _ecl_opts_file
             else
-                echo -n "-O --overwrite --allow-foreign "
+                echo -n "-O --overwrite --allow-foreign --update-super-files --update-clone-from --dont-append-cluster --replace "
                 _ecl_opts_common
             fi
             ;;
@@ -170,7 +170,7 @@ _ecl_opts_core_file()
                 _ecl_opts_common
                 ;;
             publish)
-                echo -n "-A --activate --no-reload --allow-foreign --timeLimit= --warnTimeLimit= --memoryLimit= --priority= --daliip= "
+                echo -n "-A --activate --no-reload --allow-foreign --timeLimit= --warnTimeLimit= --memoryLimit= --priority= --daliip= --update-super-files --update-clone-from --dont-append-cluster "
                 _ecl_opts_deploy
                 _ecl_opts_common
                 ;;