浏览代码

Merge branch 'candidate-6.4.2'

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 7 年之前
父节点
当前提交
dcb0d7d6f2

+ 1 - 0
cmake_modules/commonSetup.cmake

@@ -108,6 +108,7 @@ IF ("${COMMONSETUP_DONE}" STREQUAL "")
   else()
       option(USE_TBB "Enable Threading Building Block support" ON)
       option(USE_TBBMALLOC "Enable Threading Building Block scalable allocator proxy support" ON)
+      option(USE_TBBMALLOC_ROXIE "Enable Threading Building Block scalable allocator proxy support in Roxie" OFF)
   endif()
   option(LOGGING_SERVICE "Configure use of logging service" ON)
 

+ 1 - 1
esp/platform/espbinding.hpp

@@ -106,7 +106,7 @@ public:
     virtual bool isValidServiceName(IEspContext & context, const char * name){return false;}
     virtual bool qualifyServiceName(IEspContext & context, const char * servname, const char * methname, StringBuffer & servQName, StringBuffer * methQName){return false;}
     virtual IRpcRequestBinding *createReqBinding(IEspContext &context, IHttpMessage *request, const char *service, const char *method){return NULL;}
-
+    virtual bool isDynamicBinding() const { return false; }
 };
 
 #endif

+ 2 - 0
esp/platform/espprotocol.cpp

@@ -494,6 +494,8 @@ void CEspBinding::getNavigationData(IEspContext &context, IPropertyTree & data)
         if (params.length())
             params.setCharAt(0,'&'); //the entire params string will follow the initial param: "?form"
 
+        folder->addPropBool("@isDynamicBinding", isDynamicBinding());
+
         MethodInfoArray methods;
         wsdl->getQualifiedNames(context, methods);
         ForEachItemIn(idx, methods)

+ 1 - 0
esp/services/esdl_svc_engine/esdl_binding.hpp

@@ -377,6 +377,7 @@ public:
 
     bool usesESDLDefinition(const char * name, int version);
     bool usesESDLDefinition(const char * id);
+    virtual bool isDynamicBinding() const override { return true; }
     virtual unsigned getCacheMethodCount(){return 0;}
 
 private:

+ 19 - 2
esp/xslt/nav.xsl

@@ -112,7 +112,8 @@
                  </xsl:if>      
             </td>
         </tr>
-        <xsl:if test="*">
+        <xsl:choose>
+        <xsl:when test="*">
             <tr>
                 <td>
                     <table border="0" cellspacing="0" cellpadding="0">
@@ -130,7 +131,23 @@
                     </table>
                 </td>
             </tr>
-        </xsl:if>
+        </xsl:when>
+        <xsl:when test="@isDynamicBinding=1">
+            <tr>
+                <td>
+                    <table border="0" cellspacing="0" cellpadding="0">
+                        <xsl:attribute name="id"><xsl:value-of select="@name"/></xsl:attribute>
+                        <tr>
+                        <td colspan="2">
+                            <xsl:text disable-output-escaping="yes">&amp;nbsp;&amp;nbsp;</xsl:text>
+                            <font size="-2" color="gray">Unbound DESDL Service</font>
+                        </td>
+                        </tr>
+                    </table>
+                </td>
+            </tr>
+        </xsl:when>
+        </xsl:choose>
     </xsl:template>
         
     <xsl:template match="Link">

+ 61 - 55
plugins/couchbase/README.md

@@ -1,105 +1,111 @@
 #ECL Embedded Couchbase plugin
 
-This is the ECL plugin to access Couchbase](http://www.couchbase.com/), an
+This is the ECL plugin to access [Couchbase](http://www.couchbase.com/), an
 open-source, distributed multi-model NoSQL document-oriented database software
 package that is optimized for interactive applications. Freehand N1QL queries
 can be embedded within your ECL query.
 
-Client access is based on libcouchbase (c interface provided by couchbase install) and
-libcouchbase-cxx (from https://github.com/couchbaselabs/libcouchbase-cxx)
+Client access is based on libcouchbase and libcouchbase-cxx.
 
 ##Installation and Dependencies
 
-[libcouchbase] is installed via standard package managers
-
-
-(https://github.com/couchbaselabs/libcouchbase-cxx) is included as a git
-submodule in HPCC-Platform.  It will be built and integrated automatically when
-you build the HPCC-Platform project with the couchbase plugin flag turned on.
-
-The recommended method for obtaining libcouchbase is via
-On Ubuntu: sudo apt-get install libcouchbase-dev libcouchbase2-bin build-essential
+Both [libcouchbase](https://github.com/couchbase/libcouchbase) and
+[libcouchbase-cxx](https://github.com/couchbaselabs/libcouchbase-cxx) are
+included as git submodules within HPCC-Platform. They will be built and
+integrated automatically when you build the HPCC-Platform project with the
+couchbase plugin flag turned on.
 
 ##Plugin Configuration
- 
+
 The ECL Embedded couchbase plugin uses sensible default configuration values but these can
 be modified via configuration parameters.
- 
+
 The accepted configuration parameters are as follows:
+
                 Name                  Default Value
                 server                "localhost"
                 port                  8091
-                user                  ""
                 password              ""
                 bucket                "default"
                 useSSL                false
- 
+                max_connections       0
                 detailed_errcodes
                 operation_timeout
                 config_total_timeout
                 http_poolsize
- 
-Configuration parameters are declared as part of the ENDEMBED couchbase funtion definition.
+
+The `max_connections` parameter governs the maximum number of separate, active
+connections for the exact combination of server, port, bucket parameters. A
+value of zero indicates that there is no set maximum. This parameter may need
+to be set if very large ECL workloads (such as handling many concurrent Roxie
+requests) overwhelm the Couchbase server.
+
+See the libcouchbase client settings documentation for appropriate values for
+`detailed_errcodes`, `operation_timeout`, `config_total_timeout`, and `http_poolsize`.
+
+Configuration parameters are declared as part of the ENDEMBED couchbase function definition.
 For example:
-     boolean  eventstatus(REAL m) := EMBED(couchbase : server(10.0.0.1), port(8091),  bucket('mybucket'), user('myname'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
+
+     BOOLEAN  eventstatus(REAL m) := EMBED(couchbase : server(10.0.0.1), port(8091),  bucket('mybucket'), detailed_errcodes(1), operation_timeout(5.5), config_total_timeout(15))
          ...
      ENDEMBED;
 
 ##N1QL Query Examples
+
                 //Creates a primary index on 'mybucket' bucket
                 createprimeindex() := EMBED(couchbase : server(server), port(port), bucket('mybucket'))
-                                create primary index on iot;
+                    CREATE PRIMARY INDEX ON iot;
                 ENDEMBED;
- 
+
                 //Simple select returning scalar string
-                string scalarsimpleselect() := EMBED(couchbase : server(server), port(port), bucket('mybucket'))
-                                SELECT timestamp from mybucket where timestamp = '2016-06-06 11:36:05.657314-04:00' limit 1;
+                STRING scalarsimpleselect() := EMBED(couchbase : server(server), port(port), bucket('mybucket'))
+                    SELECT timestamp from mybucket where timestamp = '2016-06-06 11:36:05.657314-04:00' LIMIT 1;
                 ENDEMBED;
- 
+
                 //Simple parameterized select returning scalar numeric
-                unsigned parameterizedselectbool(boolean m) := EMBED(couchbase : server(server), port(port),  bucket('mybucket'))
-                                SELECT count(sequence) from mybucket where isStaleData = $m; // boolean value 'm' passed in
+                UNSIGNED parameterizedselectbool(BOOLEAN m) := EMBED(couchbase : server(server), port(port),  bucket('mybucket'))
+                    SELECT COUNT(sequence) FROM mybucket WHERE isStaleData = $m; // boolean value 'm' passed in
                 ENDEMBED;
- 
+
                 flatrec := RECORD
-        string latitude,
-                                real4 longitude,
-                                boolean isStaleData,
-                                INTEGER sequence;
+                    STRING latitude,
+                    REAL4 longitude,
+                    BOOLEAN isStaleData,
+                    INTEGER sequence;
                 END;
-               
+
                 // simple select returning flat record(s)
-                dataset(flatrec) flatdatasetpreparedselect(REAL m) := EMBED(couchbase : server(server), port(port),  bucket('mybucket'))
-                                SELECT contextualData.gps.latitude,contextualData.gps.longitude, isStaleData, sequence from mybucket where contextualData.gps.latitude = $m;
+                DATASET(flatrec) flatdatasetpreparedselect(REAL m) := EMBED(couchbase : server(server), port(port),  bucket('mybucket'))
+                    SELECT contextualData.gps.latitude,contextualData.gps.longitude, isStaleData, sequence FROM mybucket WHERE contextualData.gps.latitude = $m;
                 ENDEMBED;
- 
+
                 XYRecord := RECORD
                     STRING x,
                     STRING y;
                 END;
- 
+
                 //Function accepts row type
-                boolean selectrow(row(XYRecord) values) := EMBED(couchbase : server(server), port(port),  bucket('mybucket'))
-                                SELECT eventStatus from mybucket where accelx = $x and accely = $y;
+                BOOLEAN selectrow(row(XYRecord) values) := EMBED(couchbase : server(server), port(port),  bucket('mybucket'))
+                    SELECT eventStatus FORM mybucket WHERE accelx = $x AND accely = $y;
                 ENDEMBED;
- 
+
                 FullRec := RECORD
-                                real4 accelx;
-                                real4 accely;
-                                real4 accelz;
-                                string4 eventId;
-                                boolean eventStatus;
-                                string guid;
-                                boolean isStaleData;
-                                integer sequence;
-                                integer sourceoffset;
-                                unsigned sourcepartition;
-                                string sourcetopic;
-                                string timestamp;
-                                dataset(locationDatarec) locationData;
+                    REAL4 accelx;
+                    REAL4 accely;
+                    REAL4 accelz;
+                    STRING4 eventId;
+                    BOOLEAN eventStatus;
+                    STRING guid;
+                    BOOLEAN isStaleData;
+                    INTEGER sequence;
+                    INTEGER sourceoffset;
+                    UNSIGNED sourcepartition;
+                    STRING sourcetopic;
+                    STRING timestamp;
+                    DATASET(locationDatarec) locationData;
                 END;
- 
+
                 //Full select
-                dataset(FullRec) fullselect() := EMBED(couchbase : server(server), port(port),  bucket('mybucket'))
-                                SELECT mybucket.* from mybucket;
+                DATASET(FullRec) fullselect() := EMBED(couchbase : server(server), port(port),  bucket('mybucket'))
+                    SELECT mybucket.* FROM mybucket;
                 ENDEMBED;

+ 371 - 7
plugins/couchbase/couchbaseembed.cpp

@@ -26,6 +26,10 @@
 #include "eclrtl.hpp"
 #include "eclrtl_imp.hpp"
 
+#include <map>
+#include <mutex>
+#include <thread>
+
 static const char *g_moduleName = "couchbase";
 static const char *g_moduleDescription = "Couchbase Embed Helper";
 static const char *g_version = "Couchbase Embed Helper 1.0.0";
@@ -45,7 +49,7 @@ extern "C" COUCHBASEEMBED_PLUGIN_API bool getECLPluginDefinition(ECLPluginDefini
     pb->magicVersion = PLUGIN_VERSION;
     pb->version = g_version;
     pb->moduleName = g_moduleName;
-    pb->ECL = NULL;
+    pb->ECL = nullptr;
     pb->flags = PLUGIN_IMPLICIT_MODULE;
     pb->description = g_moduleDescription;
     return true;
@@ -53,6 +57,9 @@ extern "C" COUCHBASEEMBED_PLUGIN_API bool getECLPluginDefinition(ECLPluginDefini
 
 namespace couchbaseembed
 {
+    const time_t OBJECT_EXPIRE_TIMEOUT_SECONDS = 60 * 2; // Two minutes
+    static std::once_flag connectionCacheInitFlag;
+
     //--------------------------------------------------------------------------
     // Plugin Classes
     //--------------------------------------------------------------------------
@@ -97,7 +104,7 @@ namespace couchbaseembed
 
     const void * CouchbaseRowStream::nextRow()
     {
-        const void * result = NULL;
+        const void * result = nullptr;
         if (m_shouldRead && m_currentRow < m_Rows.length())
         {
             auto json = m_Rows.item(m_currentRow++);
@@ -360,6 +367,324 @@ namespace couchbaseembed
        return thisParam++;
     }
 
+    static class ConnectionCacheObj
+    {
+        private:
+
+            typedef std::vector<CouchbaseConnection*> ConnectionList;
+            typedef std::map<hash64_t, ConnectionList> ObjMap;
+
+        public:
+
+            ConnectionCacheObj(int _traceLevel)
+                :   traceLevel(_traceLevel)
+            {
+
+            }
+
+            ~ConnectionCacheObj()
+            {
+                deleteAll();
+            }
+
+            void deleteAll()
+            {
+                CriticalBlock block(cacheLock);
+
+                // Delete all idle connection objects
+                for (ObjMap::iterator keyIter = idleConnections.begin(); keyIter != idleConnections.end(); keyIter++)
+                {
+                    for (ConnectionList::iterator connectionIter = keyIter->second.begin(); connectionIter != keyIter->second.end(); connectionIter++)
+                    {
+                        if (*connectionIter)
+                        {
+                            delete(*connectionIter);
+                        }
+                    }
+                }
+
+                idleConnections.clear();
+
+                // Delete all active connection objects
+                for (ObjMap::iterator keyIter = activeConnections.begin(); keyIter != activeConnections.end(); keyIter++)
+                {
+                    for (ConnectionList::iterator connectionIter = keyIter->second.begin(); connectionIter != keyIter->second.end(); connectionIter++)
+                    {
+                        if (*connectionIter)
+                        {
+                            delete(*connectionIter);
+                        }
+                    }
+                }
+
+                activeConnections.clear();
+            }
+
+            void releaseActive(CouchbaseConnection* connectionPtr)
+            {
+                CriticalBlock block(cacheLock);
+
+                // Find given connection in our active list and move it to our
+                // idle list
+                for (ObjMap::iterator keyIter = activeConnections.begin(); keyIter != activeConnections.end(); keyIter++)
+                {
+                    for (ConnectionList::iterator connectionIter = keyIter->second.begin(); connectionIter != keyIter->second.end(); connectionIter++)
+                    {
+                        if (*connectionIter == connectionPtr)
+                        {
+                            connectionPtr->updateTimeTouched();
+                            keyIter->second.erase(connectionIter);
+                            idleConnections[keyIter->first].push_back(connectionPtr);
+
+                            if (traceLevel > 4)
+                            {
+                                DBGLOG("Couchbase: Released connection object %p", connectionPtr);
+                            }
+
+                            return;
+                        }
+                    }
+                }
+            }
+
+            void expire()
+            {
+                if (!idleConnections.empty())
+                {
+                    CriticalBlock block(cacheLock);
+
+                    time_t oldestAllowedTime = time(NULL) - OBJECT_EXPIRE_TIMEOUT_SECONDS;
+                    __int32 expireCount = 0;
+
+                    for (ObjMap::iterator keyIter = idleConnections.begin(); keyIter != idleConnections.end(); keyIter++)
+                    {
+                        ConnectionList::iterator connectionIter = keyIter->second.begin();
+
+                        while (connectionIter != keyIter->second.end())
+                        {
+                            if (*connectionIter)
+                            {
+                                if ((*connectionIter)->getTimeTouched() < oldestAllowedTime)
+                                {
+                                    delete(*connectionIter);
+                                    connectionIter =  keyIter->second.erase(connectionIter);
+                                    ++expireCount;
+                                }
+                                else
+                                {
+                                    ++connectionIter;
+                                }
+                            }
+                            else
+                            {
+                                connectionIter =  keyIter->second.erase(connectionIter);
+                            }
+                        }
+                    }
+
+                    if (traceLevel > 4 && expireCount > 0)
+                    {
+                        DBGLOG("Couchbase: Expired %d cached connection%s", expireCount, (expireCount == 1 ? "" : "s"));
+                    }
+                }
+            }
+
+            CouchbaseConnection* getConnection(bool useSSL, const char * host, unsigned port, const char * bucketname, const char * password, const char * connOptions, unsigned int maxConnections)
+            {
+                CouchbaseConnection* connectionObjPtr = nullptr;
+                StringBuffer connectionString;
+
+                CouchbaseConnection::makeConnectionString(useSSL, host, port, bucketname, connOptions, connectionString);
+
+                // Use a hash of the connection string as the key to finding
+                // any idle connection objects
+                hash64_t key = rtlHash64VStr(connectionString.str(), 0);
+
+                while (true)
+                {
+                    {
+                        CriticalBlock block(cacheLock);
+                        ConnectionList& idleConnectionList = idleConnections[key];
+
+                        if (!idleConnectionList.empty())
+                        {
+                            // We have at least one idle connection; use that
+                            connectionObjPtr = idleConnectionList.back();
+                            idleConnectionList.pop_back();
+
+                            connectionObjPtr->updateTimeTouched();
+
+                            // Push the connection object onto our active list
+                            activeConnections[key].push_back(connectionObjPtr);
+
+                            if (traceLevel > 4)
+                            {
+                                DBGLOG("Couchbase: Using cached connection object %p: %s", connectionObjPtr, connectionString.str());
+                            }
+
+                            break;
+                        }
+                        else if (maxConnections == 0 || activeConnections[key].size() < maxConnections)
+                        {
+                            // No idle connections but we don't have to wait for
+                            // one; exit the loop and create a new connection
+                            break;
+                        }
+                    }
+
+                    // We can't exit the loop and allow a new connection to
+                    // be created because there are too many active
+                    // connections already; wait for a short while
+                    // and try again
+                    std::this_thread::sleep_for(std::chrono::microseconds(10));
+                }
+
+                if (!connectionObjPtr)
+                {
+                    // An idle connection for that particular combination of
+                    // options does not exist so we need to create one;
+                    // use a small loop to retry connections if necessary
+                    unsigned int connectAttempt = 0;
+                    unsigned int MAX_ATTEMPTS = 10;
+                    useconds_t SLEEP_TIME = 100 + (rand() % 200);
+
+                    while (true)
+                    {
+                        connectionObjPtr = new CouchbaseConnection(connectionString, password);
+                        connectionObjPtr->connect();
+
+                        if (connectionObjPtr->getConnectionStatus().success())
+                        {
+                            {
+                                // Push new connection object onto our active list
+                                CriticalBlock block(cacheLock);
+
+                                connectionObjPtr->updateTimeTouched();
+                                ConnectionList& activeConnectionList = activeConnections[key];
+                                activeConnectionList.push_back(connectionObjPtr);
+                            }
+
+                            if (traceLevel > 4)
+                            {
+                                DBGLOG("Couchbase: Created and cached new connection object %p: %s", connectionObjPtr, connectionString.str());
+                            }
+
+                            break;
+                        }
+                        else if (connectionObjPtr->getConnectionStatus().isTemporary())
+                        {
+                            ++connectAttempt;
+                            if (connectAttempt < MAX_ATTEMPTS)
+                            {
+                                // According to libcouchbase-cxx, we need
+                                // to destroy the connection object if
+                                // there has been a failure of any kind
+                                delete(connectionObjPtr);
+                                connectionObjPtr = nullptr;
+                                std::this_thread::sleep_for(std::chrono::microseconds(SLEEP_TIME));
+                            }
+                            else
+                            {
+                                // Capture the final failure reason and
+                                // destroy the connection object before
+                                // throwing an error
+                                std::string     reason = connectionObjPtr->getConnectionStatus().description();
+
+                                delete(connectionObjPtr);
+                                connectionObjPtr = nullptr;
+
+                                failx("Failed to connect to couchbase instance: %s Reason: '%s'", connectionString.str(), reason.c_str());
+                            }
+                        }
+                        else
+                        {
+                            // Capture the final failure reason and
+                            // destroy the connection object before
+                            // throwing an error
+                            std::string     reason = connectionObjPtr->getConnectionStatus().description();
+
+                            delete(connectionObjPtr);
+                            connectionObjPtr = nullptr;
+
+                            failx("Failed to connect to couchbase instance: %s Reason: '%s'", connectionString.str(), reason.c_str());
+                        }
+                    }
+                }
+
+                if (!connectionObjPtr)
+                {
+                    failx("Couchbase: Unable to create connection: %s", connectionString.str());
+                }
+
+                return connectionObjPtr;
+            }
+
+        private:
+
+            ObjMap          idleConnections;    //!< std::map of created CouchbaseConnection object pointers
+            ObjMap          activeConnections;  //!< std::map of created CouchbaseConnection object pointers
+            CriticalSection cacheLock;          //!< Mutex guarding modifications to connection pools
+            int             traceLevel;         //!< The current logging level
+    } *connectionCache;
+
+    static class ConnectionCacheExpirerObj : public Thread
+    {
+        public:
+
+            ConnectionCacheExpirerObj()
+                :   Thread("Couchbase::ConnectionCacheExpirer"),
+                    shouldRun(false)
+            {
+
+            }
+
+            virtual void start()
+            {
+                if (!isAlive())
+                {
+                    shouldRun = true;
+                    Thread::start();
+                }
+            }
+
+            virtual void stop()
+            {
+                if (isAlive())
+                {
+                    shouldRun = false;
+                    join();
+                }
+            }
+
+            virtual int run()
+            {
+                // Periodically delete connections that have been idle too long
+                while (shouldRun)
+                {
+                    if (connectionCache)
+                    {
+                        connectionCache->expire();
+                    }
+
+                    std::this_thread::sleep_for(std::chrono::microseconds(1000));
+                }
+
+                return 0;
+            }
+
+        private:
+
+            std::atomic_bool    shouldRun;      //!< If true, we should execute our thread's main event loop
+    } *connectionCacheExpirer;
+
+    static void setupConnectionCache(int traceLevel)
+    {
+        couchbaseembed::connectionCache = new couchbaseembed::ConnectionCacheObj(traceLevel);
+
+        couchbaseembed::connectionCacheExpirer = new couchbaseembed::ConnectionCacheExpirerObj;
+        couchbaseembed::connectionCacheExpirer->start();
+    }
+
     CouchbaseEmbedFunctionContext::CouchbaseEmbedFunctionContext(const IContextLogger &_logctx, const char *options, unsigned _flags)
     : logctx(_logctx), m_NextRow(), m_nextParam(0), m_numParams(0), m_scriptFlags(_flags)
     {
@@ -373,6 +698,7 @@ namespace couchbaseembed
         unsigned port = 8091;
         bool useSSL = false;
         StringBuffer connectionOptions;
+        unsigned int maxConnections = 0;
 
         StringArray inputOptions;
         inputOptions.appendList(options, ",");
@@ -389,28 +715,31 @@ namespace couchbaseembed
                 else if (stricmp(optName, "port")==0)
                     port = atoi(val);
                 else if (stricmp(optName, "user")==0)
-                    user = val;
+                    user = val;     // This is not used but retained for backwards-compatibility
                 else if (stricmp(optName, "password")==0)
                     password = val;
                 else if (stricmp(optName, "bucket")==0)
                     bucketname = val;
                 else if (stricmp(optName, "useSSL")==0)
                     useSSL = clipStrToBool(val);
+                else if (stricmp(optName, "max_connections")==0)
+                    maxConnections = atoi(val);
 
                 //Connection String options
                 else if (stricmp(optName,   "detailed_errcodes")==0
                         || stricmp(optName, "operation_timeout")==0
                         || stricmp(optName, "config_total_timeout")==0
-                        || stricmp(optName, "http_poolsize")==0
-                        || stricmp(optName, "detailed_errcodes")==0)
+                        || stricmp(optName, "http_poolsize")==0)
                     connectionOptions.appendf("%s%s=%s", connectionOptions.length() == 0 ? "?" : "&", optName.str(), val);
                 else
                     failx("Unknown option %s", optName.str());
             }
         }
 
-        m_oCBConnection.setown(new CouchbaseConnection(useSSL, server, port, bucketname, user, password, connectionOptions.str()));
-        m_oCBConnection->connect();
+        std::call_once(connectionCacheInitFlag, setupConnectionCache, logctx.queryTraceLevel());
+
+        // Get a cached idle connection or create a new one
+        m_oCBConnection = connectionCache->getConnection(useSSL, server, port, bucketname, password, connectionOptions.str(), maxConnections);
     }
 
     CouchbaseEmbedFunctionContext::~CouchbaseEmbedFunctionContext()
@@ -426,6 +755,14 @@ namespace couchbaseembed
             delete m_pQuery;
             m_pQuery = nullptr;
         }
+
+        if (m_oCBConnection)
+        {
+            // When the context is deleted we should return any connection
+            // object back to idle status
+            connectionCache->releaseActive(m_oCBConnection);
+            m_oCBConnection = nullptr;
+        }
     }
 
     IPropertyTree * CouchbaseEmbedFunctionContext::nextResultRowTree()
@@ -1195,3 +1532,30 @@ namespace couchbaseembed
         return true; // TO-DO
     }
 } // namespace
+
+MODULE_INIT(INIT_PRIORITY_STANDARD)
+{
+    couchbaseembed::connectionCache = nullptr;
+    couchbaseembed::connectionCacheExpirer = nullptr;
+
+    return true;
+}
+
+MODULE_EXIT()
+{
+    // Delete the background thread expiring items from the CouchbaseConnection
+    // cache before deleting the connection cache
+    if (couchbaseembed::connectionCacheExpirer)
+    {
+        couchbaseembed::connectionCacheExpirer->stop();
+        delete(couchbaseembed::connectionCacheExpirer);
+        couchbaseembed::connectionCacheExpirer = nullptr;
+    }
+
+     if (couchbaseembed::connectionCache)
+    {
+        couchbaseembed::connectionCache->deleteAll();
+        delete(couchbaseembed::connectionCache);
+        couchbaseembed::connectionCache = nullptr;
+    }
+}

+ 21 - 9
plugins/couchbase/couchbaseembed.hpp

@@ -42,6 +42,7 @@
 #include "rtlds_imp.hpp"
 #include "rtlfield.hpp"
 #include "roxiemem.hpp"
+#include <time.h>
 
 #include <vector>
 
@@ -185,10 +186,17 @@ namespace couchbaseembed
     class CouchbaseConnection : public CInterface
     {
     public:
-        inline CouchbaseConnection(bool useSSL, const char * host, unsigned port, const char * bucketname, const char * user, const char * password, const char * connOptions)
+        CouchbaseConnection(bool useSSL, const char * host, unsigned port, const char * bucketname, const char * password, const char * connOptions)
         {
-            m_connectionString.setf("couchbase%s://%s:%d/%s%s", useSSL ? "s" : "", host, port, bucketname, connOptions);
-            m_pCouchbaseClient = new Couchbase::Client(m_connectionString.str(), password);
+            StringBuffer connectionString;
+            
+            makeConnectionString(useSSL, host, port, bucketname, connOptions, connectionString);
+            m_pCouchbaseClient = new Couchbase::Client(connectionString.str(), password);
+        }
+
+        CouchbaseConnection(const StringBuffer& connectionString, const char * password)
+        {
+            m_pCouchbaseClient = new Couchbase::Client(connectionString.str(), password);
         }
 
         virtual ~CouchbaseConnection()
@@ -200,21 +208,25 @@ namespace couchbaseembed
             }
         }
 
-        inline void connect()
+        static void makeConnectionString(bool useSSL, const char * host, unsigned port, const char * bucketname, const char * connOptions, StringBuffer& out)
         {
-            m_connectionStatus = m_pCouchbaseClient->connect();
-            if (!m_connectionStatus.success())
-                failx("Failed to connect to couchbase instance: %s Reason: %s", m_connectionString.str(), m_connectionStatus.description());
+            out.setf("couchbase%s://%s:%d/%s%s", useSSL ? "s" : "", host, port, bucketname, connOptions);
         }
 
         Couchbase::Query * query(Couchbase::QueryCommand * qcommand);
 
+        inline void connect() { m_connectionStatus = m_pCouchbaseClient->connect(); }
+        inline const Couchbase::Status& getConnectionStatus() const { return m_connectionStatus; }
+        inline time_t getTimeTouched() const { return timeLastUsed; }
+        inline void updateTimeTouched() {timeLastUsed = time(NULL); }
+
     private:
-        StringBuffer m_connectionString;
         Couchbase::Client * m_pCouchbaseClient;
         Couchbase::Status  m_connectionStatus;
 
         CouchbaseConnection(const CouchbaseConnection &);
+
+        time_t timeLastUsed;
     };
 
     enum PathNodeType {CPNTScalar, CPNTDataset, CPNTSet};
@@ -445,7 +457,7 @@ namespace couchbaseembed
            unsigned checkNextParam(const char *name);
 
            const IContextLogger &logctx;
-           Owned<CouchbaseConnection>    m_oCBConnection;
+           CouchbaseConnection         * m_oCBConnection;
            Couchbase::Query            * m_pQuery;
            Couchbase::QueryCommand     * m_pQcmd;
            Owned<IPropertyTreeIterator>  m_resultrow;

+ 1 - 1
roxie/ccd/CMakeLists.txt

@@ -123,7 +123,7 @@ IF (USE_OPENSSL)
     )
 ENDIF()
 
-IF (USE_TBBMALLOC)
+IF (USE_TBBMALLOC AND USE_TBBMALLOC_ROXIE)
    add_dependencies ( ccd tbb )
    target_link_libraries ( ccd libtbbmalloc_proxy libtbbmalloc)
 ENDIF()