Jelajahi Sumber

Merge branch 'candidate-5.4.0'

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 10 tahun lalu
induk
melakukan
ccb4b4bb31

+ 1 - 1
cmake_modules/dependencies/lenny.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.34.1, libicu38, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, sudo, openssh-client, openssh-server, expect, libarchive, rsync, libapr1, libaprutil1, zip")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.34.1, libicu38, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, sudo, openssh-client, openssh-server, expect, libarchive, rsync, libapr1, libaprutil1, zip, python")

+ 1 - 1
cmake_modules/dependencies/lucid.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.40.0, libicu42, libxslt1.1, libxml2, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1, rsync, zip")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.40.0, libicu42, libxslt1.1, libxml2, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1, rsync, zip, python")

+ 1 - 1
cmake_modules/dependencies/natty.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.42.0, libicu44, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1, rsync, zip")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.42.0, libicu44, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1, rsync, zip, python")

+ 1 - 1
cmake_modules/dependencies/oneiric.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.46.1, libicu44, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1, rsync, libapr1, libaprutil1, zip")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.46.1, libicu44, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1, rsync, libapr1, libaprutil1, zip, python")

+ 1 - 1
cmake_modules/dependencies/precise.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.46.1, libicu48, libxslt1.1, libxml2, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive12, rsync, libapr1, libaprutil1, zip")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.46.1, libicu48, libxslt1.1, libxml2, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive12, rsync, libapr1, libaprutil1, zip, python")

+ 1 - 1
cmake_modules/dependencies/quantal.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.49.0, libicu48, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive12, rsync, libapr1, libaprutil1, zip")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.49.0, libicu48, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive12, rsync, libapr1, libaprutil1, zip, python")

+ 1 - 1
cmake_modules/dependencies/saucy.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.53.0, libicu48, libxalan-c111, libxerces-c3.1, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive13, rsync, libapr1, libaprutil1, zip")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.53.0, libicu48, libxalan-c111, libxerces-c3.1, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive13, rsync, libapr1, libaprutil1, zip, python")

+ 1 - 1
cmake_modules/dependencies/squeeze.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.42.0, libicu44, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1, rsync, libapr1, libaprutil1, zip")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.42.0, libicu44, libxalan110, libxerces-c28, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive1, rsync, libapr1, libaprutil1, zip, python")

+ 1 - 1
cmake_modules/dependencies/trusty.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.54.0, libicu52, libxslt1.1, libxml2, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive13, rsync, libapr1, libaprutil1, zip")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.54.0, libicu52, libxslt1.1, libxml2, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive13, rsync, libapr1, libaprutil1, zip, python")

+ 1 - 1
cmake_modules/dependencies/utopic.cmake

@@ -1 +1 @@
-set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.55.0, libicu52, libxslt1.1, libxml2, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive13, rsync, libapr1, libaprutil1")
+set ( CPACK_DEBIAN_PACKAGE_DEPENDS "libboost-regex1.55.0, libicu52, libxslt1.1, libxml2, binutils, libldap-2.4-2, openssl, zlib1g, g++, openssh-client, openssh-server, expect, libarchive13, rsync, libapr1, libaprutil1, python")

+ 14 - 14
docs/HPCCSystemAdmin/HPCCSystemAdministratorsGuide.xml

@@ -520,6 +520,12 @@
       <para>Running multiple HPCC Roxie nodes on a single physical server is
       not recommended, except in the cases of virtualization or
       containers.</para>
+
+      <para>Configure your system to balance the size of your Thor and Roxie
+      clusters. The number of Roxie nodes should never exceed the number of
+      Thor nodes. In addition, the number of Thor nodes should be evenly
+      divisible by the number of Roxie nodes. This ensures an efficient
+      distribution of file parts from Thor to Roxie. </para>
     </sect1>
 
     <sect1>
@@ -1208,7 +1214,7 @@ lock=/var/lock/HPCCSystems</programlisting>
       invalidate those files and make the physical files inaccessible.</para>
 
       <para>There are a couple of scenarios where you would want to redefine
-      your Thor cluster. </para>
+      your Thor cluster.</para>
 
       <sect2 id="Replace_Faulty_Node" role="nobrk">
         <title>Replacing faulty node(s)</title>
@@ -1228,7 +1234,7 @@ lock=/var/lock/HPCCSystems</programlisting>
       </sect2>
 
       <sect2 id="SysAdmin_Resizing_The_Cluster" role="nobrk">
-        <title>Resizing the cluster </title>
+        <title>Resizing the cluster</title>
 
         <para>If you are adding or removing Thor cluster nodes but
         <emphasis>all previous nodes remain part of the environment and
@@ -1378,7 +1384,7 @@ lock=/var/lock/HPCCSystems</programlisting>
     </sect1>
 
     <sect1 id="BP_High_Availability">
-      <title>High Availability and Disaster Recovery</title>
+      <title>High Availability</title>
 
       <para>If you require high availability for your HPCC system, there are
       some additional considerations that you should be aware of. This is not
@@ -1570,21 +1576,15 @@ lock=/var/lock/HPCCSystems</programlisting>
     <sect1>
       <title>Additional Resources</title>
 
-      <para>Additional help for Learning ECL is also available. There are
-      online courses.</para>
+      <para>Additional help with HPCC and Learning ECL is also available.
+      There are online courses available. Go to :</para>
 
       <para><ulink
-      url="https://learn.lexisnexis.com/lexisnexis/resources/courses">https://learn.lexisnexis.com/lexisnexis/resources/courses
+      url="https://learn.lexisnexis.com/hpcc">https://learn.lexisnexis.com/hpcc
       </ulink></para>
 
-      <para>There are training videos online.</para>
-
-      <para><ulink
-      url="https://learn.lexisnexis.com/lexisnexis/resources/courses/HPCC/Summit2014/NewECLWatch50Features/NewECLWatch50Features.html">Legacy
-      ECL Watch and New 5.0 ECL Watch</ulink></para>
-
-      <para>A quick summary of the differences in the interface, goes into
-      particular detail. Helpful for learning how to deploy Roxies.</para>
+      <para>You may need to register for the site. There are several training
+      videos and other very helpful information.</para>
     </sect1>
   </chapter>
 </book>

+ 3 - 2
docs/HPCCSystemAdmin/SA-Mods/SysAdminConfigMod.xml

@@ -2,7 +2,6 @@
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
 "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
 <book xml:base="../">
-  
   <chapter id="Usin-HPCC-ConfigMgr">
     <title>Using Configuration Manager</title>
 
@@ -551,7 +550,9 @@ sudo -u hpcc cp /etc/HPCCSystems/source/NewEnvironment.xml /etc/HPCCSystems/envi
         a site with large volumes of data. A good policy is to set the Thor
         size to 4 times the source data on your HPCC. Typically, Roxie would
         be about ¼ the size of Thor. This is because the data is compressed
-        and the system does not hold any transient data in Roxie.</para>
+        and the system does not hold any transient data in Roxie. Remember
+        that you do not want the number of Roxie nodes to exceed the number of
+        Thor nodes. </para>
 
         <sect3>
           <title>High Data Thor sizing considerations</title>

+ 43 - 18
roxie/ccd/ccdqueue.cpp

@@ -237,7 +237,7 @@ public:
         data->packetlength = lengthRemaining;
         const byte *finger = (const byte *) (data + 1);
         lengthRemaining -= sizeof(RoxiePacketHeader);
-        if (data->activityId == ROXIE_FILECALLBACK || data->activityId == ROXIE_DEBUGCALLBACK)
+        if (data->activityId == ROXIE_FILECALLBACK || data->activityId == ROXIE_DEBUGCALLBACK || data->retries == QUERY_ABORTED)
         {
             continuationData = NULL;
             continuationLength = 0;
@@ -2214,7 +2214,7 @@ public:
 
 interface ILocalMessageCollator : extends IMessageCollator
 {
-    virtual void enqueueMessage(void *data, unsigned datalen, void *meta, unsigned metalen, void *header, unsigned headerlen) = 0;
+    virtual void enqueueMessage(bool outOfBand, void *data, unsigned datalen, void *meta, unsigned metalen, void *header, unsigned headerlen) = 0;
 };
 
 interface ILocalReceiveManager : extends IReceiveManager
@@ -2223,16 +2223,18 @@ interface ILocalReceiveManager : extends IReceiveManager
 };
 
 
+
 class LocalMessagePacker : public CDummyMessagePacker
 {
     MemoryBuffer meta;
     MemoryBuffer header;
     Linked<ILocalReceiveManager> rm;
     ruid_t id;
+    bool outOfBand;
 
 public:
     IMPLEMENT_IINTERFACE;
-    LocalMessagePacker(RoxiePacketHeader &_header, ILocalReceiveManager *_rm) : rm(_rm)
+    LocalMessagePacker(RoxiePacketHeader &_header, bool _outOfBand, ILocalReceiveManager *_rm) : rm(_rm), outOfBand(_outOfBand)
     {
         id = _header.uid;
         header.append(sizeof(RoxiePacketHeader), &_header);
@@ -2382,10 +2384,13 @@ public:
         sem.interrupt(E);
     }
 
-    virtual void enqueueMessage(void *data, unsigned datalen, void *meta, unsigned metalen, void *header, unsigned headerlen)
+    virtual void enqueueMessage(bool outOfBand, void *data, unsigned datalen, void *meta, unsigned metalen, void *header, unsigned headerlen)
     {
         CriticalBlock c(crit);
-        pending.enqueue(new CLocalMessageResult(data, datalen, meta, metalen, header, headerlen));
+        if (outOfBand)
+            pending.enqueueHead(new CLocalMessageResult(data, datalen, meta, metalen, header, headerlen));
+        else
+            pending.enqueue(new CLocalMessageResult(data, datalen, meta, metalen, header, headerlen));
         sem.signal();
         totalBytesReceived += datalen + metalen + headerlen;
     }
@@ -2399,7 +2404,6 @@ public:
 class RoxieLocalReceiveManager : public CInterface, implements ILocalReceiveManager
 {
     MapXToMyClass<ruid_t, ruid_t, ILocalMessageCollator> collators;
-    Owned<IRowManager> rowManager;
     CriticalSection crit;
     Owned<StringContextLogger> logctx;
     Linked<IMessageCollator> defaultCollator;
@@ -2412,10 +2416,8 @@ public:
 
     virtual IMessageCollator *createMessageCollator(IRowManager *manager, ruid_t ruid)
     {
+        IMessageCollator *collator = new CLocalMessageCollator(manager, ruid);
         CriticalBlock b(crit);
-        if (!rowManager)
-            rowManager.setown(roxiemem::createRowManager(0, NULL, *logctx, NULL, false)); // MORE - should not really use default limits
-        IMessageCollator *collator = new CLocalMessageCollator(rowManager, ruid); // MORE - is this right - why two rowManagers and why pass this one (not the other) ?
         collators.setValue(ruid, collator);
         return collator;
     }
@@ -2455,7 +2457,7 @@ void LocalMessagePacker::flush(bool last_message)
             unsigned datalen = data.length();
             unsigned metalen = meta.length();
             unsigned headerlen = header.length();
-            collator->enqueueMessage(data.detach(), datalen, meta.detach(), metalen, header.detach(), headerlen);
+            collator->enqueueMessage(outOfBand, data.detach(), datalen, meta.detach(), metalen, header.detach(), headerlen);
         }
         // otherwise Roxie server is no longer interested and we can simply discard
     }
@@ -2483,7 +2485,6 @@ CLocalMessageCollator::~CLocalMessageCollator()
 
 class RoxieLocalQueueManager : public RoxieReceiverBase
 {
-    Linked<ISendManager> sendManager;
     Linked<RoxieLocalReceiveManager> receiveManager;
 
 public:
@@ -2520,6 +2521,7 @@ public:
                 RoxiePacketHeader newHeader(header, ROXIE_ALIVE);
                 Owned<IMessagePacker> output = createOutputStream(newHeader, true, logctx);
                 output->flush(true);
+                return; // No point sending the retry in localSlave mode
             }
             RoxieQueue *targetQueue;
 #ifdef ROXIE_SLA_LOGIC
@@ -2555,19 +2557,39 @@ public:
 
     virtual void sendAbort(RoxiePacketHeader &header, const IRoxieContextLogger &logctx)
     {
-        // MORE - should really have some code here? - no one to alert about the abort
-        //UNIMPLEMENTED;
+        MTIME_SECTION(queryActiveTimer(), "RoxieLocalQueueManager::sendAbort");
+        RoxiePacketHeader abortHeader(header, header.activityId & ROXIE_PRIORITY_MASK);
+        abortHeader.retries = QUERY_ABORTED;
+        if (logctx.queryTraceLevel() > 8)
+        {
+            StringBuffer s; logctx.CTXLOG("Sending ABORT packet %s", abortHeader.toString(s).str());
+        }
+        MemoryBuffer data;
+        data.append(sizeof(abortHeader), &abortHeader);
+        Owned<IRoxieQueryPacket> packet = createRoxiePacket(data);
+        sendPacket(packet, logctx);
+        atomic_inc(&abortsSent);
     }
 
     virtual void sendAbortCallback(const RoxiePacketHeader &header, const char *lfn, const IRoxieContextLogger &logctx)
     {
-        // MORE - should really have some code here
-        //UNIMPLEMENTED;
+        MTIME_SECTION(queryActiveTimer(), "RoxieLocalQueueManager::sendAbortCallback");
+        RoxiePacketHeader abortHeader(header, ROXIE_FILECALLBACK);
+        abortHeader.retries = QUERY_ABORTED;
+        MemoryBuffer data;
+        data.append(sizeof(abortHeader), &abortHeader).append(lfn);
+        if (logctx.queryTraceLevel() > 5)
+        {
+            StringBuffer s; logctx.CTXLOG("Sending ABORT FILECALLBACK packet %s for file %s", abortHeader.toString(s).str(), lfn);
+        }
+        Owned<IRoxieQueryPacket> packet = createRoxiePacket(data);
+        sendPacket(packet, logctx);
+        atomic_inc(&abortsSent);
     }
 
     virtual IMessagePacker *createOutputStream(RoxiePacketHeader &header, bool outOfBand, const IRoxieContextLogger &logctx)
     {
-        return new LocalMessagePacker(header, receiveManager);
+        return new LocalMessagePacker(header, outOfBand, receiveManager);
     }
 
     virtual IReceiveManager *queryReceiveManager()
@@ -2577,8 +2599,8 @@ public:
 
     virtual bool replyPending(RoxiePacketHeader &header)
     {
-        // MORE - should really have some code here!
-        return false;
+        // MORE - should really have some code here! But returning true is a reasonable approximation.
+        return true;
     }
 
     virtual bool abortCompleted(RoxiePacketHeader &header)
@@ -2586,6 +2608,9 @@ public:
         // MORE - should really have some code here!
         return false;
     }
+
+
+
 };
 
 IRoxieOutputQueueManager *ROQ;

+ 5 - 5
roxie/ccd/ccdqueue.ipp

@@ -76,17 +76,17 @@ public:
         return false;
     }
 
+
     virtual void *getBuffer(unsigned len, bool variable)
     {
-        data.setLength(lastput);
         if (variable)
         {
-            char *ret = (char *) data.reserve(len + sizeof(RecordLengthType));
+            char *ret = (char *) data.ensureCapacity(len + sizeof(RecordLengthType));
             return ret + sizeof(RecordLengthType);
         }
         else
         {
-            return data.reserve(len);
+            return data.ensureCapacity(len);
         }
     }
 
@@ -98,11 +98,11 @@ public:
             *(RecordLengthType *) buf = len;
             len += sizeof(RecordLengthType);
         }
-        data.setLength(lastput + len);
+        data.setWritePos(lastput + len);
         lastput += len;
     }
 
-    virtual void flush(bool last_message) { data.setLength(lastput); }
+    virtual void flush(bool last_message) { }
     virtual void sendMetaInfo(const void *buf, unsigned len) { throwUnexpected(); }
     virtual unsigned size() const { return lastput; }
 };

+ 1 - 1
roxie/ccd/ccdserver.cpp

@@ -4272,7 +4272,7 @@ public:
             }
             else
             {
-                if (!anyActivity)
+                if (!anyActivity && !localSlave)
                 {
                     activity.queryLogCtx().CTXLOG("Input has stalled - retry required?");
                     retryPending();

+ 2 - 0
roxie/udplib/udplib.hpp

@@ -103,10 +103,12 @@ interface ISendManager : extends IInterface
     virtual bool dataQueued(ruid_t ruid, unsigned sequence, unsigned destNodeIndex) = 0;
     virtual bool abortData(ruid_t ruid, unsigned sequence, unsigned destNodeIndex) = 0;
     virtual bool allDone() = 0;
+    virtual void writeOwn(unsigned destNodeIndex, roxiemem::DataBuffer *buffer, unsigned len, unsigned queue) = 0;  // NOTE: takes ownership of the DataBuffer
 };
 
 extern UDPLIB_API IReceiveManager *createReceiveManager(int server_flow_port, int data_port, int client_flow_port, int sniffer_port, const IpAddress &sniffer_multicast_ip, int queue_size, unsigned maxSlotsPerSender, unsigned myNodeIndex);
 extern UDPLIB_API ISendManager *createSendManager(int server_flow_port, int data_port, int client_flow_port, int sniffer_port, const IpAddress &sniffer_multicast_ip, int queue_size_pr_server, int queues_pr_server, unsigned maxRetryData, TokenBucket *rateLimiter, unsigned myNodeIndex);
+extern UDPLIB_API IMessagePacker *createMessagePacker(ruid_t ruid, unsigned msgId, const void *messageHeader, unsigned headerSize, ISendManager &_parent, unsigned _destNode, unsigned _sourceNode, unsigned _msgSeq, unsigned _queue);
 
 extern UDPLIB_API const IpAddress &getNodeAddress(unsigned index);
 extern UDPLIB_API unsigned addRoxieNode(const char *ipString);

+ 213 - 209
roxie/udplib/udptrs.cpp

@@ -440,214 +440,6 @@ public:
 class CSendManager : public CInterface, implements ISendManager
 {
     friend class send_send_flow;
-    class CMessagePacker : public CInterface, implements IMessagePacker
-    {
-        CSendManager   &parent;
-        unsigned        destNodeIndex;
-        UdpPacketHeader package_header;
-        DataBuffer     *part_buffer;
-        unsigned data_buffer_size;
-        unsigned data_used;
-        void *mem_buffer;
-        unsigned mem_buffer_size;
-        unsigned totalSize;
-        bool            packed_request;
-        unsigned        requested_size;
-        MemoryBuffer    metaInfo;
-        bool            last_message_done;
-        int             queue_number;
-        
-    public:
-        IMPLEMENT_IINTERFACE;
-
-        CMessagePacker(ruid_t ruid, unsigned msgId, const void *messageHeader, unsigned headerSize, CSendManager &_parent, unsigned _destNode, unsigned _sourceNode, unsigned _msgSeq, unsigned _queue) 
-            : parent(_parent)
-        {
-            queue_number = _queue;
-            destNodeIndex = _destNode;
-            
-            package_header.length = 0;          // filled in with proper value later
-            package_header.metalength = 0;
-            package_header.ruid = ruid;
-            package_header.msgId = msgId;
-            package_header.pktSeq = 0;
-            package_header.nodeIndex = _sourceNode;
-            package_header.msgSeq = _msgSeq;
-            package_header.udpSequence = 0; // these are allocated when transmitted
-            
-            packed_request = false;
-            part_buffer = bufferManager->allocate();
-            data_buffer_size = DATA_PAYLOAD - sizeof(UdpPacketHeader);
-            assertex(data_buffer_size >= headerSize + sizeof(unsigned short));
-            *(unsigned short *) (&part_buffer->data[sizeof(UdpPacketHeader)]) = headerSize; 
-            memcpy(&part_buffer->data[sizeof(UdpPacketHeader)+sizeof(unsigned short)], messageHeader, headerSize);
-            data_used = headerSize + sizeof(unsigned short);
-            mem_buffer = 0;
-            mem_buffer_size = 0;
-            last_message_done = false;
-            totalSize = 0;
-
-            if (udpTraceLevel >= 40) 
-                DBGLOG("UdpSender: CMessagePacker::CMessagePacker - ruid=" RUIDF " id=0x%.8x mseq=%u node=%u queue=%d", ruid, msgId, _msgSeq, destNodeIndex, _queue);
-            
-        }
-
-        ~CMessagePacker() 
-        {
-            if (part_buffer)
-                part_buffer->Release();
-            if (mem_buffer) free (mem_buffer);
-            
-            if (udpTraceLevel >= 40) 
-            {
-                DBGLOG("UdpSender: CMessagePacker::~CMessagePacker - ruid=" RUIDF " id=0x%.8x mseq=%u pktSeq=%x node=%u",
-                    package_header.ruid, package_header.msgId, package_header.msgSeq, package_header.pktSeq, destNodeIndex);
-            }
-        }
-
-        virtual void *getBuffer(unsigned len, bool variable) 
-        {
-            if (variable)
-                len += sizeof(RecordLengthType);
-            if (DATA_PAYLOAD - sizeof(UdpPacketHeader) < len) 
-            {
-                // Won't fit in one, so allocate temp location
-                if (mem_buffer_size < len) 
-                {
-                    free(mem_buffer);
-                    mem_buffer = malloc(len);
-                    mem_buffer_size = len;
-                }
-                packed_request = false;
-                if (variable)
-                    return ((char *) mem_buffer) + sizeof(RecordLengthType);
-                else
-                    return mem_buffer;
-            }
-            
-            if (part_buffer && ((data_buffer_size - data_used) < len)) 
-                flush(false); // Note that we never span records that are small enough to fit - this can result in significant wastage if record just over DATA_PAYLOAD/2
-            
-            if (!part_buffer) 
-            {
-                part_buffer = bufferManager->allocate();
-                data_buffer_size = DATA_PAYLOAD - sizeof(UdpPacketHeader);
-            }
-            packed_request = true;
-            if (variable)
-                return &part_buffer->data[data_used + sizeof(UdpPacketHeader) + sizeof(RecordLengthType)];
-            else
-                return &part_buffer->data[data_used + sizeof(UdpPacketHeader)]; 
-        }
-        
-        virtual void putBuffer(const void *buf, unsigned len, bool variable) 
-        {
-            if (variable)
-            {
-                assertex(len < MAX_RECORD_LENGTH);
-                buf = ((char *) buf) - sizeof(RecordLengthType);
-                *(RecordLengthType *) buf = len;
-                len += sizeof(RecordLengthType);
-            }
-            totalSize += len;
-            if (packed_request) 
-            {
-                assert(len <= (data_buffer_size - data_used));
-                data_used += len;
-            }
-            else
-            {
-                while (len) 
-                {
-                    if (!part_buffer) 
-                    {
-                        part_buffer = bufferManager->allocate();
-                        data_buffer_size = DATA_PAYLOAD - sizeof(UdpPacketHeader);
-                        data_used = 0;
-                    }
-                    unsigned chunkLen = data_buffer_size - data_used;
-                    if (chunkLen > len) 
-                        chunkLen = len;
-                    memcpy(&part_buffer->data[sizeof(UdpPacketHeader)+data_used], buf, chunkLen);
-                    data_used += chunkLen;
-                    len -= chunkLen;
-                    buf = &(((char*)buf)[chunkLen]);
-                    if (len)
-                        flush(false);
-                }
-            }
-        }
-
-        virtual void sendMetaInfo(const void *buf, unsigned len) {
-            metaInfo.append(len, buf);
-        }
-
-        virtual void flush(bool last_msg = false) 
-        {
-            if (!last_message_done && last_msg) 
-            {
-                last_message_done = true;
-                if (!part_buffer)
-                    part_buffer = bufferManager->allocate();
-                const char *metaData = metaInfo.toByteArray();
-                unsigned metaLength = metaInfo.length();
-                unsigned maxMetaLength = DATA_PAYLOAD - (sizeof(UdpPacketHeader) + data_used);
-                while (metaLength > maxMetaLength)
-                {
-                    memcpy(&part_buffer->data[sizeof(UdpPacketHeader)+data_used], metaData, maxMetaLength);
-                    put_package(part_buffer, data_used, maxMetaLength);
-                    metaLength -= maxMetaLength;
-                    metaData += maxMetaLength;
-                    data_used = 0;
-                    maxMetaLength = DATA_PAYLOAD - sizeof(UdpPacketHeader);
-                    part_buffer = bufferManager->allocate();
-                }
-                memcpy(&part_buffer->data[sizeof(UdpPacketHeader)+data_used], metaData, metaLength);
-                package_header.pktSeq |= 0x80000000;
-                put_package(part_buffer, data_used, metaLength);
-            }
-            else if (part_buffer) 
-            {
-                // Just flush current - used when no room for current row
-                if (data_used) 
-                    put_package(part_buffer, data_used, 0); // buffer released in put_package
-                else 
-                    part_buffer->Release(); // If NO data in buffer, release buffer back to pool
-            }
-            part_buffer = 0;
-            data_buffer_size = 0;
-            data_used = 0;
-        }
-        
-        void put_package(DataBuffer *dataBuff, unsigned datalength, unsigned metalength)
-        {
-            package_header.length = datalength + metalength + sizeof(UdpPacketHeader);
-            package_header.metalength = metalength;
-            memcpy(dataBuff->data, &package_header, sizeof(package_header));
-            parent.writeOwn(destNodeIndex, dataBuff, package_header.length, queue_number);
-
-            if (udpTraceLevel >= 50) 
-            {
-                if (package_header.length==991)
-                    DBGLOG("NEarly");
-                DBGLOG("UdpSender: CMessagePacker::put_package Qed packet - ruid=" RUIDF " id=0x%.8X mseq=%u pkseq=0x%.8X len=%u node=%u queue=%d",
-                    package_header.ruid, package_header.msgId, package_header.msgSeq,
-                    package_header.pktSeq, package_header.length, destNodeIndex, queue_number);
-            }
-            package_header.pktSeq++;
-        }
-
-        virtual bool dataQueued()
-        {
-            return(parent.dataQueued(package_header.ruid, package_header.msgId, destNodeIndex));
-        }
-
-        virtual unsigned size() const
-        {
-            return totalSize;
-        }
-    };
-
     class StartedThread : public Thread
     {
     private:
@@ -1197,7 +989,7 @@ public:
     {
         if (destNodeIndex >= numNodes)
             throw MakeStringException(ROXIE_UDP_ERROR, "createMessagePacker: invalid destination node index %i", destNodeIndex);
-        return new CMessagePacker(ruid, sequence, messageHeader, headerSize, *this, destNodeIndex, myNodeIndex, getNextMessageSequence(), queue);
+        return ::createMessagePacker(ruid, sequence, messageHeader, headerSize, *this, destNodeIndex, myNodeIndex, getNextMessageSequence(), queue);
     }
 
     virtual bool dataQueued(ruid_t ruid, unsigned msgId, unsigned destIndex)
@@ -1233,4 +1025,216 @@ ISendManager *createSendManager(int server_flow_port, int data_port, int client_
     return new CSendManager(server_flow_port, data_port, client_flow_port, sniffer_port, sniffer_multicast_ip, queue_size_pr_server, queues_pr_server, maxRetryData, myNodeIndex, rateLimiter);
 }
 
+class CMessagePacker : public CInterface, implements IMessagePacker
+{
+    ISendManager   &parent;
+    unsigned        destNodeIndex;
+    UdpPacketHeader package_header;
+    DataBuffer     *part_buffer;
+    unsigned data_buffer_size;
+    unsigned data_used;
+    void *mem_buffer;
+    unsigned mem_buffer_size;
+    unsigned totalSize;
+    bool            packed_request;
+    unsigned        requested_size;
+    MemoryBuffer    metaInfo;
+    bool            last_message_done;
+    int             queue_number;
+
+public:
+    IMPLEMENT_IINTERFACE;
+
+    CMessagePacker(ruid_t ruid, unsigned msgId, const void *messageHeader, unsigned headerSize, ISendManager &_parent, unsigned _destNode, unsigned _sourceNode, unsigned _msgSeq, unsigned _queue)
+        : parent(_parent)
+    {
+        queue_number = _queue;
+        destNodeIndex = _destNode;
+
+        package_header.length = 0;          // filled in with proper value later
+        package_header.metalength = 0;
+        package_header.ruid = ruid;
+        package_header.msgId = msgId;
+        package_header.pktSeq = 0;
+        package_header.nodeIndex = _sourceNode;
+        package_header.msgSeq = _msgSeq;
+        package_header.udpSequence = 0; // these are allocated when transmitted
+
+        packed_request = false;
+        part_buffer = bufferManager->allocate();
+        data_buffer_size = DATA_PAYLOAD - sizeof(UdpPacketHeader);
+        assertex(data_buffer_size >= headerSize + sizeof(unsigned short));
+        *(unsigned short *) (&part_buffer->data[sizeof(UdpPacketHeader)]) = headerSize;
+        memcpy(&part_buffer->data[sizeof(UdpPacketHeader)+sizeof(unsigned short)], messageHeader, headerSize);
+        data_used = headerSize + sizeof(unsigned short);
+        mem_buffer = 0;
+        mem_buffer_size = 0;
+        last_message_done = false;
+        totalSize = 0;
+
+        if (udpTraceLevel >= 40)
+            DBGLOG("UdpSender: CMessagePacker::CMessagePacker - ruid=" RUIDF " id=0x%.8x mseq=%u node=%u queue=%d", ruid, msgId, _msgSeq, destNodeIndex, _queue);
+
+    }
+
+    ~CMessagePacker()
+    {
+        if (part_buffer)
+            part_buffer->Release();
+        if (mem_buffer) free (mem_buffer);
+
+        if (udpTraceLevel >= 40)
+        {
+            DBGLOG("UdpSender: CMessagePacker::~CMessagePacker - ruid=" RUIDF " id=0x%.8x mseq=%u pktSeq=%x node=%u",
+                package_header.ruid, package_header.msgId, package_header.msgSeq, package_header.pktSeq, destNodeIndex);
+        }
+    }
 
+    virtual void *getBuffer(unsigned len, bool variable)
+    {
+        if (variable)
+            len += sizeof(RecordLengthType);
+        if (DATA_PAYLOAD - sizeof(UdpPacketHeader) < len)
+        {
+            // Won't fit in one, so allocate temp location
+            if (mem_buffer_size < len)
+            {
+                free(mem_buffer);
+                mem_buffer = malloc(len);
+                mem_buffer_size = len;
+            }
+            packed_request = false;
+            if (variable)
+                return ((char *) mem_buffer) + sizeof(RecordLengthType);
+            else
+                return mem_buffer;
+        }
+
+        if (part_buffer && ((data_buffer_size - data_used) < len))
+            flush(false); // Note that we never span records that are small enough to fit - this can result in significant wastage if record just over DATA_PAYLOAD/2
+
+        if (!part_buffer)
+        {
+            part_buffer = bufferManager->allocate();
+            data_buffer_size = DATA_PAYLOAD - sizeof(UdpPacketHeader);
+        }
+        packed_request = true;
+        if (variable)
+            return &part_buffer->data[data_used + sizeof(UdpPacketHeader) + sizeof(RecordLengthType)];
+        else
+            return &part_buffer->data[data_used + sizeof(UdpPacketHeader)];
+    }
+
+    virtual void putBuffer(const void *buf, unsigned len, bool variable)
+    {
+        if (variable)
+        {
+            assertex(len < MAX_RECORD_LENGTH);
+            buf = ((char *) buf) - sizeof(RecordLengthType);
+            *(RecordLengthType *) buf = len;
+            len += sizeof(RecordLengthType);
+        }
+        totalSize += len;
+        if (packed_request)
+        {
+            assert(len <= (data_buffer_size - data_used));
+            data_used += len;
+        }
+        else
+        {
+            while (len)
+            {
+                if (!part_buffer)
+                {
+                    part_buffer = bufferManager->allocate();
+                    data_buffer_size = DATA_PAYLOAD - sizeof(UdpPacketHeader);
+                    data_used = 0;
+                }
+                unsigned chunkLen = data_buffer_size - data_used;
+                if (chunkLen > len)
+                    chunkLen = len;
+                memcpy(&part_buffer->data[sizeof(UdpPacketHeader)+data_used], buf, chunkLen);
+                data_used += chunkLen;
+                len -= chunkLen;
+                buf = &(((char*)buf)[chunkLen]);
+                if (len)
+                    flush(false);
+            }
+        }
+    }
+
+    virtual void sendMetaInfo(const void *buf, unsigned len) {
+        metaInfo.append(len, buf);
+    }
+
+    virtual void flush(bool last_msg = false)
+    {
+        if (!last_message_done && last_msg)
+        {
+            last_message_done = true;
+            if (!part_buffer)
+                part_buffer = bufferManager->allocate();
+            const char *metaData = metaInfo.toByteArray();
+            unsigned metaLength = metaInfo.length();
+            unsigned maxMetaLength = DATA_PAYLOAD - (sizeof(UdpPacketHeader) + data_used);
+            while (metaLength > maxMetaLength)
+            {
+                memcpy(&part_buffer->data[sizeof(UdpPacketHeader)+data_used], metaData, maxMetaLength);
+                put_package(part_buffer, data_used, maxMetaLength);
+                metaLength -= maxMetaLength;
+                metaData += maxMetaLength;
+                data_used = 0;
+                maxMetaLength = DATA_PAYLOAD - sizeof(UdpPacketHeader);
+                part_buffer = bufferManager->allocate();
+            }
+            memcpy(&part_buffer->data[sizeof(UdpPacketHeader)+data_used], metaData, metaLength);
+            package_header.pktSeq |= 0x80000000;
+            put_package(part_buffer, data_used, metaLength);
+        }
+        else if (part_buffer)
+        {
+            // Just flush current - used when no room for current row
+            if (data_used)
+                put_package(part_buffer, data_used, 0); // buffer released in put_package
+            else
+                part_buffer->Release(); // If NO data in buffer, release buffer back to pool
+        }
+        part_buffer = 0;
+        data_buffer_size = 0;
+        data_used = 0;
+    }
+
+    void put_package(DataBuffer *dataBuff, unsigned datalength, unsigned metalength)
+    {
+        package_header.length = datalength + metalength + sizeof(UdpPacketHeader);
+        package_header.metalength = metalength;
+        memcpy(dataBuff->data, &package_header, sizeof(package_header));
+        parent.writeOwn(destNodeIndex, dataBuff, package_header.length, queue_number);
+
+        if (udpTraceLevel >= 50)
+        {
+            if (package_header.length==991)
+                DBGLOG("NEarly");
+            DBGLOG("UdpSender: CMessagePacker::put_package Qed packet - ruid=" RUIDF " id=0x%.8X mseq=%u pkseq=0x%.8X len=%u node=%u queue=%d",
+                package_header.ruid, package_header.msgId, package_header.msgSeq,
+                package_header.pktSeq, package_header.length, destNodeIndex, queue_number);
+        }
+        package_header.pktSeq++;
+    }
+
+    virtual bool dataQueued()
+    {
+        return(parent.dataQueued(package_header.ruid, package_header.msgId, destNodeIndex));
+    }
+
+    virtual unsigned size() const
+    {
+        return totalSize;
+    }
+};
+
+
+IMessagePacker *createMessagePacker(ruid_t ruid, unsigned msgId, const void *messageHeader, unsigned headerSize, ISendManager &_parent, unsigned _destNode, unsigned _sourceNode, unsigned _msgSeq, unsigned _queue)
+{
+    return new CMessagePacker(ruid, msgId, messageHeader, headerSize, _parent, _destNode, _sourceNode, _msgSeq, _queue);
+}

+ 2 - 1
system/jlib/jbuff.cpp

@@ -287,10 +287,11 @@ void *MemoryBuffer::insertDirect(unsigned offset, size32_t insertLen)
 }
 
 
-void MemoryBuffer::ensureCapacity(unsigned max)
+void * MemoryBuffer::ensureCapacity(unsigned max)
 {
     if (maxLen - curLen < max)
         _realloc(curLen + max);
+    return buffer + curLen;
 }
 
 void MemoryBuffer::kill()

+ 1 - 1
system/jlib/jbuff.hpp

@@ -197,7 +197,7 @@ public:
     void            swapWith(MemoryBuffer & other);
 
     inline size32_t capacity() { return (maxLen - curLen); }
-    void            ensureCapacity(unsigned max);
+    void *          ensureCapacity (unsigned max);
     inline size32_t length() const { return curLen; }
     inline size32_t remaining() const { return curLen - readPos; }
 

+ 4 - 0
tools/esdlcomp/esdlgram.y

@@ -995,6 +995,10 @@ Param
      CurParam->flags |= PF_IN;
  }
  |
+ {
+   //There was no Param, make sure CurParam is nulled out
+   CurParam = NULL;
+ }
  ;
 
 TypeModifiers