Pārlūkot izejas kodu

HPCC-12516 Add generalized Esp caching mechanism

Signed-off-by: wangkx <kevin.wang@lexisnexis.com>
wangkx 8 gadi atpakaļ
vecāks
revīzija
b79dddebca

+ 2 - 1
cmake_modules/FindLIBMEMCACHED.cmake

@@ -108,7 +108,8 @@ else()
 endif()
 
 include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(libmemcached DEFAULT_MSG
+find_package_handle_standard_args(
+    LIBMEMCACHED DEFAULT_MSG
     LIBMEMCACHEDCORE_LIBRARY
     LIBMEMCACHEDUTIL_LIBRARY
     LIBMEMCACHED_INCLUDE_DIR

+ 5 - 0
cmake_modules/commonSetup.cmake

@@ -169,6 +169,11 @@ IF ("${COMMONSETUP_DONE}" STREQUAL "")
     #"cmake -DEXAMPLEPLUGIN=ON <path-to/HPCC-Platform/>" will configure the plugin makefiles to be built with "make".
 
   set(LIBMEMCACHED_MINVERSION "1.0.10")
+  find_package(LIBMEMCACHED ${LIBMEMCACHED_MINVERSION})
+  if(LIBMEMCACHED_FOUND)
+      add_definitions(-DUSE_LIBMEMCACHED)
+      include_directories(${LIBMEMCACHED_INCLUDE_DIR})
+  endif()
 
   if (SIGN_MODULES)
       message(STATUS "GPG signing check")

+ 0 - 6
esp/bindings/SOAP/soaplib/CMakeLists.txt

@@ -24,12 +24,6 @@
 
 project(soaplib)
 
-find_package(LIBMEMCACHED ${LIBMEMCACHED_MINVERSION})
-if(LIBMEMCACHED_FOUND)
-    add_definitions(-DUSE_LIBMEMCACHED)
-    include_directories(${LIBMEMCACHED_INCLUDE_DIR})
-endif()
-
 set(SRCS
     ../../../platform/espcontext.cpp
     ../../../platform/espprotocol.cpp

+ 115 - 1
esp/bindings/http/platform/httpbinding.cpp

@@ -260,6 +260,17 @@ EspHttpBinding::EspHttpBinding(IPropertyTree* tree, const char *bindname, const
     }
     if(m_challenge_realm.length() == 0)
         m_challenge_realm.append("ESP");
+
+#ifdef USE_LIBMEMCACHED
+    if(proc_cfg)
+    {
+        const char* initString = proc_cfg->queryProp("@memCachedOptionString");
+        if (!isEmptyString(initString))
+            memCachedInitString.set(initString);
+        else
+            memCachedInitString.set("--SERVER=127.0.0.1");//using local memcached server
+    }
+#endif
 }
 
 StringBuffer &EspHttpBinding::generateNamespace(IEspContext &context, CHttpRequest* request, const char *serv, const char *method, StringBuffer &ns)
@@ -507,9 +518,107 @@ bool EspHttpBinding::basicAuth(IEspContext* ctx)
     ctx->addTraceSummaryTimeStamp(LogMin, "basicAuth");
     return authorized;
 }
-    
+
+#ifdef USE_LIBMEMCACHED
+void EspHttpBinding::ensureMemCachedClient()
+{
+    if ((memCachedMethods == 0) || memCachedClient)
+        return;
+
+    memCachedClient.setown(new ESPMemCached());
+    if (!memCachedClient->init(memCachedInitString.get()))
+        memCachedClient.clear();
+}
+
+int EspHttpBinding::queryMemCacheSeconds(const char *method)
+{
+    StringBuffer key(method);
+    int* value = memCachedSecondsMap.getValue(key.toUpperCase().str());
+    if (!value)
+        return -1;
+    return unsigned (*value);
+}
+
+bool EspHttpBinding::queryMemCacheGlobal(const char *method)
+{
+    StringBuffer key(method);
+    bool* cacheGlobal = memCachedGlobalMap.getValue(key.toUpperCase().str());
+    return cacheGlobal && *cacheGlobal;
+}
+
+const char* EspHttpBinding::createMemCachedID(CHttpRequest* request, StringBuffer& memCachedID)
+{
+    memCachedID.clear();
+
+    const char* method = request->queryServiceMethod();
+    int cacheSeconds = queryMemCacheSeconds(method);
+    if (cacheSeconds < 0) //no memcache required for this method
+        return memCachedID.str();
+
+    if(request->isSoapMessage())
+        memCachedID.append(request->createUniqueRequestHash(queryMemCacheGlobal(method), "SOAP"));
+    else if(request->isFormSubmission())
+        memCachedID.append(request->createUniqueRequestHash(queryMemCacheGlobal(method), "FORM"));
+    else
+        memCachedID.append(request->createUniqueRequestHash(queryMemCacheGlobal(method), ""));
+    return memCachedID.str();
+}
+
+bool EspHttpBinding::sendFromMemCached(CHttpRequest* request, CHttpResponse* response, const char* memCachedID)
+{
+    StringBuffer content, contentType, contentTypeCachedID;
+    contentTypeCachedID.set(memCachedID).append("t");
+
+    {
+        CriticalBlock block(memCachedCrit);
+
+        if (memCachedClient->exists("ESPResponse", memCachedID))
+            memCachedClient->get("ESPResponse", memCachedID, content);
+        if (memCachedClient->exists("ESPResponse", contentTypeCachedID.str()))
+            memCachedClient->get("ESPResponse", contentTypeCachedID.str(), contentType);
+    }
+    if (content.isEmpty() || contentType.isEmpty())
+        return false;
+
+    ESPLOG(LogMax, "Sending MemCached for %s.", request->queryServiceMethod());
+    response->setContentType(contentType.str());
+    response->setContent(content.str());
+    response->send();
+    return true;
+}
+
+void EspHttpBinding::addToMemCached(CHttpRequest* request, CHttpResponse* response, const char* memCachedID)
+{
+    const char* method = request->queryServiceMethod();
+    int cacheSeconds = queryMemCacheSeconds(method);
+    if (cacheSeconds < 0) //no memcache required for this method
+        return;
+
+    StringBuffer content, contentType, contentTypeID;
+    contentTypeID.set(memCachedID).append("t");
+    response->getContent(content);
+    response->getContentType(contentType);
+    {
+        CriticalBlock block(memCachedCrit);
+        memCachedClient->set("ESPResponse", memCachedID, content.str(), (unsigned) cacheSeconds);
+        memCachedClient->set("ESPResponse", contentTypeID.str(), contentType.str(), (unsigned) cacheSeconds);
+    }
+    ESPLOG(LogMax, "AddTo MemCached for %s.", method);
+}
+#endif
+
 void EspHttpBinding::handleHttpPost(CHttpRequest *request, CHttpResponse *response)
 {
+#ifdef USE_LIBMEMCACHED
+    ensureMemCachedClient();
+
+    StringBuffer memCachedID;
+    if (memCachedClient)
+        createMemCachedID(request, memCachedID);
+    if (!memCachedID.isEmpty() && sendFromMemCached(request, response, memCachedID.str()))
+        return;
+#endif
+
     if(request->isSoapMessage()) 
     {
         request->queryParameters()->setProp("__wsdl_address", m_wsdlAddress.str());
@@ -519,6 +628,11 @@ void EspHttpBinding::handleHttpPost(CHttpRequest *request, CHttpResponse *respon
         onPostForm(request, response);
     else
         onPost(request, response);
+
+#ifdef USE_LIBMEMCACHED
+    if (!memCachedID.isEmpty())
+        addToMemCached(request, response, memCachedID.str());
+#endif
 }
 
 int EspHttpBinding::onGet(CHttpRequest* request, CHttpResponse* response)

+ 29 - 0
esp/bindings/http/platform/httpbinding.hpp

@@ -141,8 +141,24 @@ private:
 
     StringAttrMapping desc_map;
     StringAttrMapping help_map;
+#ifdef USE_LIBMEMCACHED
+    Owned<ESPMemCached> memCachedClient;
+    CriticalSection memCachedCrit;
+    StringAttr memCachedInitString;
+    unsigned memCachedMethods = 0;
+    MapStringTo<int> memCachedSecondsMap;
+    MapStringTo<bool> memCachedGlobalMap;
+#endif
 
     void getXMLMessageTag(IEspContext& ctx, bool isRequest, const char *method, StringBuffer& tag);
+#ifdef USE_LIBMEMCACHED
+    void ensureMemCachedClient();
+    int queryMemCacheSeconds(const char *method);
+    bool queryMemCacheGlobal(const char *method);
+    const char* createMemCachedID(CHttpRequest* request, StringBuffer& memCachedID);
+    void addToMemCached(CHttpRequest* request, CHttpResponse* response, const char* memCachedID);
+    bool sendFromMemCached(CHttpRequest* request, CHttpResponse* response, const char* memCachedID);
+#endif
 protected:
     MethodInfoArray m_methods;
     bool                    m_includeSoapTest;
@@ -196,6 +212,19 @@ public:
         StringBuffer key(method);
         help_map.setValue(key.toUpperCase().str(), help);
     }
+#ifdef USE_LIBMEMCACHED
+    void addMemCachedSeconds(const char *method, int cacheSeconds)
+    {
+        StringBuffer key(method);
+        memCachedSecondsMap.setValue(key.toUpperCase().str(), cacheSeconds);
+        memCachedMethods++;
+    }
+    void addMemCachedGlobal(const char *method, bool cacheGlobal)
+    {
+        StringBuffer key(method);
+        memCachedGlobalMap.setValue(key.toUpperCase().str(), cacheGlobal);
+    }
+#endif
 
     int onGetConfig(IEspContext &context, CHttpRequest* request, CHttpResponse* response);
 

+ 2 - 2
esp/bindings/http/platform/httpservice.cpp

@@ -453,12 +453,12 @@ int CEspHttpServer::processRequest()
             {
                 if(stricmp(method.str(), POST_METHOD)==0)
                     thebinding->handleHttpPost(m_request.get(), m_response.get());
-                else if(!stricmp(method.str(), GET_METHOD)) 
+                else if(!stricmp(method.str(), GET_METHOD))
                 {
                     if (stype==sub_serv_index_redirect)
                     {
                         StringBuffer url;
-                        if (isSubService) 
+                        if (isSubService)
                         {
                             StringBuffer qSvcName;
                             thebinding->qualifySubServiceName(*ctx,serviceName,NULL, qSvcName, NULL);

+ 19 - 1
esp/bindings/http/platform/httptransport.cpp

@@ -456,6 +456,9 @@ void CHttpMessage::addParameter(const char* paramname, const char *value)
 
     m_queryparams->setProp(paramname, value);
     m_paramCount++;
+#ifdef USE_LIBMEMCACHED
+    allParameterString.append("&").append(paramname).append("=").append(value);
+#endif
 }
 
 StringBuffer& CHttpMessage::getParameter(const char* paramname, StringBuffer& paramval)
@@ -1564,7 +1567,22 @@ StringBuffer& CHttpRequest::getPeer(StringBuffer& Peer)
     }
     return Peer;
 }
-
+#ifdef USE_LIBMEMCACHED
+unsigned CHttpRequest::createUniqueRequestHash(bool cacheGlobal, const char* msgType)
+{
+    StringBuffer idStr;
+    if (!cacheGlobal)
+    {
+        const char* userID = m_context->queryUserId();
+        if (!isEmptyString(userID))
+            idStr.append(userID).append("_");
+    }
+    if (!isEmptyString(msgType))
+        idStr.append(msgType).append("_");
+    idStr.appendf("%s_%s_%s", m_espServiceName.get(), m_espMethodName.get(), allParameterString.str());
+    return hashc((unsigned char *)idStr.str(), idStr.length(), 0);
+}
+#endif
 void CHttpRequest::getBasicAuthorization(StringBuffer& userid, StringBuffer& password,StringBuffer& realm)
 {
     StringBuffer authheader;

+ 225 - 1
esp/bindings/http/platform/httptransport.ipp

@@ -37,7 +37,6 @@
 
 #include "xslprocessor.hpp"
 
-
 #define POST_METHOD "POST"
 #define GET_METHOD "GET"
 #define HEAD_METHOD "HEAD"
@@ -77,6 +76,9 @@ protected:
     Owned<IProperties> m_queryparams;
     MapStrToBuf  m_attachments;
     StringArray  m_headers;
+#ifdef USE_LIBMEMCACHED
+    StringBuffer allParameterString;
+#endif
 
     Owned<IEspContext> m_context;
     IArrayOf<CEspCookie> m_cookies;
@@ -336,6 +338,9 @@ public:
     virtual int receive(IMultiException *me);
 
     void updateContext();
+#ifdef USE_LIBMEMCACHED
+    unsigned createUniqueRequestHash(bool cacheGlobal, const char* msgType);
+#endif
 
     virtual void setMaxRequestEntityLength(int len) {m_MaxRequestEntityLength = len;}
     virtual int getMaxRequestEntityLength() { return m_MaxRequestEntityLength; }
@@ -416,4 +421,223 @@ inline bool skipXslt(IEspContext &context)
     return (context.getResponseFormat()!=ESPSerializationANY);  //for now
 }
 
+#ifdef USE_LIBMEMCACHED
+#include <libmemcached/memcached.hpp>
+#include <libmemcached/util.h>
+
+class ESPMemCached : public CInterface
+{
+    memcached_st* connection = nullptr;
+    memcached_pool_st* pool = nullptr;
+    StringAttr options;
+    bool initialized = false;
+
+public :
+    ESPMemCached()
+    {
+#if (LIBMEMCACHED_VERSION_HEX < 0x01000010)
+        VStringBuffer msg("Memcached Plugin: libmemcached version '%s' incompatible with min version>=1.0.10", LIBMEMCACHED_VERSION_STRING);
+        ESPLOG(LogNormal, "%s", msg.str());
+#endif
+    }
+
+    ~ESPMemCached()
+    {
+        if (pool)
+        {
+            memcached_pool_release(pool, connection);
+            connection = nullptr;//For safety (from changing this destructor) as not implicit in either the above or below.
+            memcached_st *memc = memcached_pool_destroy(pool);
+            if (memc)
+                memcached_free(memc);
+        }
+        else if (connection)//This should never be needed but just in case.
+        {
+            memcached_free(connection);
+        }
+    };
+
+    bool init(const char * _options)
+    {
+        if (initialized)
+            return initialized;
+
+        options.set(_options);
+        pool = memcached_pool(_options, strlen(_options));
+        assertPool();
+
+        setPoolSettings();
+        connect();
+        if (connection)
+            initialized = checkServersUp();
+        return initialized;
+    }
+
+    void setPoolSettings()
+    {
+        assertPool();
+        const char * msg = "memcached_pool_behavior_set failed - ";
+        assertOnError(memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_KETAMA, 1), msg);//NOTE: alias of MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA amongst others.
+        memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_USE_UDP, 0);  // Note that this fails on early versions of libmemcached, so ignore result
+        assertOnError(memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_NO_BLOCK, 0), msg);
+        assertOnError(memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, 1000), msg);//units of ms.
+        assertOnError(memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_SND_TIMEOUT, 1000000), msg);//units of mu-s.
+        assertOnError(memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, 1000000), msg);//units of mu-s.
+        assertOnError(memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, 0), msg);
+        assertOnError(memcached_pool_behavior_set(pool, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1), "memcached_pool_behavior_set failed - ");
+    }
+
+    void connect()
+    {
+        assertPool();
+        if (connection)
+#if (LIBMEMCACHED_VERSION_HEX<0x53000)
+            memcached_pool_push(pool, connection);
+        memcached_return_t rc;
+        connection = memcached_pool_pop(pool, (struct timespec *)0 , &rc);
+#else
+            memcached_pool_release(pool, connection);
+        memcached_return_t rc;
+        connection = memcached_pool_fetch(pool, (struct timespec *)0 , &rc);
+#endif
+        assertOnError(rc, "memcached_pool_pop failed - ");
+    }
+
+    bool checkServersUp()
+    {
+        memcached_return_t rc;
+        char* args = nullptr;
+        OwnedMalloc<memcached_stat_st> stats;
+        stats.setown(memcached_stat(connection, args, &rc));
+
+        unsigned int numberOfServers = memcached_server_count(connection);
+        if (numberOfServers < 1)
+        {
+            ESPLOG(LogMin,"Memcached: no server connected.");
+            return false;
+        }
+
+        unsigned int numberOfServersDown = 0;
+        for (unsigned i = 0; i < numberOfServers; ++i)
+        {
+            if (stats[i].pid == -1)//perhaps not the best test?
+            {
+                numberOfServersDown++;
+                VStringBuffer msg("Memcached: Failed connecting to entry %u\nwithin the server list: %s", i+1, options.str());
+                ESPLOG(LogMin, "%s", msg.str());
+            }
+        }
+        if (numberOfServersDown == numberOfServers)
+        {
+            ESPLOG(LogMin,"Memcached: Failed connecting to ALL servers. Check memcached on all servers and \"memcached -B ascii\" not used.");
+            return false;
+        }
+
+        //check memcached version homogeneity
+        for (unsigned i = 0; i < numberOfServers-1; ++i)
+        {
+            if (!streq(stats[i].version, stats[i+1].version))
+                DBGLOG("Memcached: Inhomogeneous versions of memcached across servers.");
+        }
+        return true;
+    };
+
+    bool exists(const char* partitionKey, const char* key)
+    {
+#if (LIBMEMCACHED_VERSION_HEX<0x53000)
+        throw makeStringException(0, "memcached_exist not supported in this version of libmemcached");
+#else
+        memcached_return_t rc;
+        size_t partitionKeyLength = strlen(partitionKey);
+        if (partitionKeyLength)
+            rc = memcached_exist_by_key(connection, partitionKey, partitionKeyLength, key, strlen(key));
+        else
+            rc = memcached_exist(connection, key, strlen(key));
+
+        if (rc == MEMCACHED_NOTFOUND)
+            return false;
+        else
+        {
+            assertOnError(rc, "'Exists' request failed - ");
+            return true;
+        }
+#endif
+    };
+
+    const char* get(const char* partitionKey, const char* key, StringBuffer& out)
+    {
+        uint32_t flag = 0;
+        size_t returnLength;
+        memcached_return_t rc;
+
+        OwnedMalloc<char> value;
+        size_t partitionKeyLength = strlen(partitionKey);
+        if (partitionKeyLength)
+            value.setown(memcached_get_by_key(connection, partitionKey, partitionKeyLength, key, strlen(key), &returnLength, &flag, &rc));
+        else
+            value.setown(memcached_get(connection, key, strlen(key), &returnLength, &flag, &rc));
+
+        if (value)
+            out.set(value);
+
+        StringBuffer keyMsg = "'Get' request failed - ";
+        assertOnError(rc, appendIfKeyNotFoundMsg(rc, key, keyMsg));
+        return out.str();
+    };
+
+    void set(const char* partitionKey, const char* key, const char* value, unsigned __int64 expireSec)
+    {
+        size_t partitionKeyLength = strlen(partitionKey);
+        const char * msg = "'Set' request failed - ";
+        if (partitionKeyLength)
+            assertOnError(memcached_set_by_key(connection, partitionKey, partitionKeyLength, key, strlen(key), value, strlen(value), (time_t)expireSec, 0), msg);
+        else
+            assertOnError(memcached_set(connection, key, strlen(key), value, strlen(value), (time_t)expireSec, 0), msg);
+    };
+
+    void deleteKey(const char* partitionKey, const char* key)
+    {
+        memcached_return_t rc;
+        size_t partitionKeyLength = strlen(partitionKey);
+        if (partitionKeyLength)
+            rc = memcached_delete_by_key(connection, partitionKey, partitionKeyLength, key, strlen(key), (time_t)0);
+        else
+            rc = memcached_delete(connection, key, strlen(key), (time_t)0);
+        assertOnError(rc, "'Delete' request failed - ");
+    };
+
+    void clear(unsigned when)
+    {
+        //NOTE: memcached_flush is the actual cache flush/clear/delete and not an io buffer flush.
+        assertOnError(memcached_flush(connection, (time_t)(when)), "'Clear' request failed - ");
+    };
+
+    void assertOnError(memcached_return_t rc, const char * _msg)
+    {
+        if (rc != MEMCACHED_SUCCESS)
+        {
+            VStringBuffer msg("Memcached: %s%s", _msg, memcached_strerror(connection, rc));
+            ESPLOG(LogNormal, "%s", msg.str());
+        }
+    };
+
+    const char * appendIfKeyNotFoundMsg(memcached_return_t rc, const char * key, StringBuffer & target) const
+    {
+        if (rc == MEMCACHED_NOTFOUND)
+            target.append("(key: '").append(key).append("') ");
+        return target.str();
+    };
+
+    void assertPool()
+    {
+        if (!pool)
+        {
+            StringBuffer msg = "Memcached: Failed to instantiate server pool with:";
+            msg.newline().append(options);
+            ESPLOG(LogNormal, "%s", msg.str());
+        }
+    }
+};
+#endif //USE_LIBMEMCACHED
+
 #endif

+ 0 - 6
esp/protocols/http/CMakeLists.txt

@@ -24,12 +24,6 @@
 
 project(esphttp)
 
-find_package(LIBMEMCACHED ${LIBMEMCACHED_MINVERSION})
-if(LIBMEMCACHED_FOUND)
-    add_definitions(-DUSE_LIBMEMCACHED)
-    include_directories(${LIBMEMCACHED_INCLUDE_DIR})
-endif()
-
 set(SRCS
     ../../bindings/bindutil.cpp
     ../../bindings/http/client/httpclient.cpp

+ 14 - 14
esp/scm/ws_topology.ecm

@@ -633,25 +633,25 @@ ESPservice [auth_feature("DEFERRED"), noforms, version("1.26"), exceptions_inlin
     ESPuses ESPstruct TpServices;
     ESPuses ESPstruct TpTargetCluster;
 
-    ESPmethod [resp_xsl_default("/esp/xslt/targetclusters.xslt")] TpTargetClusterQuery(TpTargetClusterQueryRequest, TpTargetClusterQueryResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/topology.xslt")] TpClusterQuery(TpClusterQueryRequest, TpClusterQueryResponse);
-    ESPmethod TpLogicalClusterQuery(TpLogicalClusterQueryRequest, TpLogicalClusterQueryResponse);
-    ESPmethod TpGroupQuery(TpGroupQueryRequest, TpGroupQueryResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/machines.xslt")] TpMachineQuery(TpMachineQueryRequest, TpMachineQueryResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/cluster_info.xslt")] TpClusterInfo(TpClusterInfoRequest, TpClusterInfoResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/thor_status.xslt")] TpThorStatus(TpThorStatusRequest, TpThorStatusResponse);
-
-    ESPmethod [min_ver("1.26")] TpDropZoneQuery(TpDropZoneQueryRequest, TpDropZoneQueryResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/services.xslt")] TpServiceQuery(TpServiceQueryRequest, TpServiceQueryResponse);
+    ESPmethod [cache_seconds(180), cache_globel(1), resp_xsl_default("/esp/xslt/targetclusters.xslt")] TpTargetClusterQuery(TpTargetClusterQueryRequest, TpTargetClusterQueryResponse);
+    ESPmethod [cache_seconds(180), cache_global(1), resp_xsl_default("/esp/xslt/topology.xslt")] TpClusterQuery(TpClusterQueryRequest, TpClusterQueryResponse);
+    ESPmethod [cache_seconds(180), cache_global(1)] TpLogicalClusterQuery(TpLogicalClusterQueryRequest, TpLogicalClusterQueryResponse);
+    ESPmethod [cache_seconds(180), cache_global(1)] TpGroupQuery(TpGroupQueryRequest, TpGroupQueryResponse);
+    ESPmethod [cache_seconds(180), cache_globel(1), resp_xsl_default("/esp/xslt/machines.xslt")] TpMachineQuery(TpMachineQueryRequest, TpMachineQueryResponse);
+    ESPmethod [cache_seconds(180), cache_globel(1), resp_xsl_default("/esp/xslt/cluster_info.xslt")] TpClusterInfo(TpClusterInfoRequest, TpClusterInfoResponse);
+    ESPmethod [cache_seconds(180), cache_globel(1), resp_xsl_default("/esp/xslt/thor_status.xslt")] TpThorStatus(TpThorStatusRequest, TpThorStatusResponse);
+
+    ESPmethod [cache_seconds(180), cache_globel(1), min_ver("1.26")] TpDropZoneQuery(TpDropZoneQueryRequest, TpDropZoneQueryResponse);
+    ESPmethod [cache_seconds(180), cache_globel(1), resp_xsl_default("/esp/xslt/services.xslt")] TpServiceQuery(TpServiceQueryRequest, TpServiceQueryResponse);
     ESPmethod TpSetMachineStatus(TpSetMachineStatusRequest, TpSetMachineStatusResponse);
     ESPmethod TpSwapNode(TpSwapNodeRequest, TpSwapNodeResponse);
-    ESPmethod TpXMLFile(TpXMLFileRequest, TpXMLFileResponse);
+    ESPmethod [cache_seconds(180)] TpXMLFile(TpXMLFileRequest, TpXMLFileResponse);
     ESPmethod [resp_xsl_default("/esp/xslt/tplog.xslt")] TpLogFile(TpLogFileRequest, TpLogFileResponse);
     ESPmethod [resp_xsl_default("/esp/xslt/tplogdisplay.xslt")] TpLogFileDisplay(TpLogFileRequest, TpLogFileResponse);
     ESPmethod TpGetComponentFile(TpGetComponentFileRequest, TpGetComponentFileResponse);
-    ESPmethod TpGetServicePlugins(TpGetServicePluginsRequest, TpGetServicePluginsResponse);
-    ESPmethod TpListTargetClusters(TpListTargetClustersRequest, TpListTargetClustersResponse);
-    ESPmethod [min_ver(1.25)] TpMachineInfo(TpMachineInfoRequest, TpMachineInfoResponse);
+    ESPmethod [cache_seconds(180), cache_globel(1)] TpGetServicePlugins(TpGetServicePluginsRequest, TpGetServicePluginsResponse);
+    ESPmethod [cache_seconds(180), cache_globel(1)] TpListTargetClusters(TpListTargetClustersRequest, TpListTargetClustersResponse);
+    ESPmethod [cache_seconds(180), cache_globel(1), min_ver(1.25)] TpMachineInfo(TpMachineInfoRequest, TpMachineInfoResponse);
 
     ESPmethod SystemLog(SystemLogRequest, SystemLogResponse);
 };

+ 34 - 34
esp/scm/ws_workunits.ecm

@@ -1823,28 +1823,28 @@ ESPservice [
     version("1.69"), default_client_version("1.69"),
     noforms,exceptions_inline("./smc_xslt/exceptions.xslt"),use_method_name] WsWorkunits
 {
-    ESPmethod [resp_xsl_default("/esp/xslt/workunits.xslt")]     WUQuery(WUQueryRequest, WUQueryResponse);
-    ESPmethod [min_ver("1.57")] WULightWeightQuery(WULightWeightQueryRequest, WULightWeightQueryResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/wuid.xslt")]     WUInfo(WUInfoRequest, WUInfoResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/wuiddetails.xslt")]     WUInfoDetails(WUInfoRequest, WUInfoResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/wuid.xslt")]     WUGraphTiming(WUGraphTimingRequest, WUGraphTimingResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/graph.xslt")]         WUGraphInfo(WUGraphInfoRequest, WUGraphInfoResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/graph_gvc.xslt")]     WUGVCGraphInfo(WUGVCGraphInfoRequest, WUGVCGraphInfoResponse);
+    ESPmethod [cache_seconds(60), resp_xsl_default("/esp/xslt/workunits.xslt")]     WUQuery(WUQueryRequest, WUQueryResponse);
+    ESPmethod [cache_seconds(60), min_ver("1.57")] WULightWeightQuery(WULightWeightQueryRequest, WULightWeightQueryResponse);
+    ESPmethod [cache_seconds(30), resp_xsl_default("/esp/xslt/wuid.xslt")]     WUInfo(WUInfoRequest, WUInfoResponse);
+    ESPmethod [cache_seconds(30), resp_xsl_default("/esp/xslt/wuiddetails.xslt")]     WUInfoDetails(WUInfoRequest, WUInfoResponse);
+    ESPmethod [cache_seconds(30), resp_xsl_default("/esp/xslt/wuid.xslt")]     WUGraphTiming(WUGraphTimingRequest, WUGraphTimingResponse);
+    ESPmethod [cache_seconds(30), resp_xsl_default("/esp/xslt/graph.xslt")]         WUGraphInfo(WUGraphInfoRequest, WUGraphInfoResponse);
+    ESPmethod [cache_seconds(30), resp_xsl_default("/esp/xslt/graph_gvc.xslt")]     WUGVCGraphInfo(WUGVCGraphInfoRequest, WUGVCGraphInfoResponse);
     ESPmethod [description("Stub for Ajax GVC Graph."), help(""), resp_xsl_default("/esp/xslt/GvcGraph.xslt")] GVCAjaxGraph(GVCAjaxGraphRequest, GVCAjaxGraphResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/result.xslt")]        WUResult(WUResultRequest, WUResultResponse);
-    ESPmethod WUFullResult(WUFullResultRequest, WUFullResultResponse);
-    ESPmethod WUResultView(WUResultViewRequest, WUResultViewResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/wuid_jobs.xslt")]     WUJobList(WUJobListRequest, WUJobListResponse);
+    ESPmethod [cache_seconds(30), resp_xsl_default("/esp/xslt/result.xslt")]        WUResult(WUResultRequest, WUResultResponse);
+    ESPmethod [cache_seconds(30)] WUFullResult(WUFullResultRequest, WUFullResultResponse);
+    ESPmethod [cache_seconds(30)] WUResultView(WUResultViewRequest, WUResultViewResponse);
+    ESPmethod [cache_seconds(60), resp_xsl_default("/esp/xslt/wuid_jobs.xslt")]     WUJobList(WUJobListRequest, WUJobListResponse);
     ESPmethod [resp_xsl_default("/esp/xslt/wuaction_results.xslt")] WUAction(WUActionRequest, WUActionResponse); 
-    ESPmethod [resp_xsl_default("/esp/xslt/scheduledwus.xslt")] WUShowScheduled(WUShowScheduledRequest, WUShowScheduledResponse); 
+    ESPmethod [cache_seconds(60), resp_xsl_default("/esp/xslt/scheduledwus.xslt")] WUShowScheduled(WUShowScheduledRequest, WUShowScheduledResponse);
 
-    ESPmethod WUResultSummary(WUResultSummaryRequest, WUResultSummaryResponse);
+    ESPmethod [cache_seconds(30)] WUResultSummary(WUResultSummaryRequest, WUResultSummaryResponse);
 
-    ESPmethod WUResultBin(WUResultBinRequest, WUResultBinResponse);
-    ESPmethod WUClusterJobQueueXLS(WUClusterJobQueueXLSRequest, WUClusterJobQueueXLSResponse);
+    ESPmethod [cache_seconds(30)] WUResultBin(WUResultBinRequest, WUResultBinResponse);
+    ESPmethod [cache_seconds(60)] WUClusterJobQueueXLS(WUClusterJobQueueXLSRequest, WUClusterJobQueueXLSResponse);
     ESPmethod WUClusterJobQueueLOG(WUClusterJobQueueLOGRequest, WUClusterJobQueueLOGResponse);
-    ESPmethod WUClusterJobXLS(WUClusterJobXLSRequest, WUClusterJobXLSResponse);
-    ESPmethod WUClusterJobSummaryXLS(WUClusterJobSummaryXLSRequest, WUClusterJobSummaryXLSResponse);
+    ESPmethod [cache_seconds(60)] WUClusterJobXLS(WUClusterJobXLSRequest, WUClusterJobXLSResponse);
+    ESPmethod [cache_seconds(60)] WUClusterJobSummaryXLS(WUClusterJobSummaryXLSRequest, WUClusterJobSummaryXLSResponse);
     ESPmethod [auth_feature("OwnWorkunitsAccess:WRITE")] WUCreate(WUCreateRequest, WUCreateResponse);
     ESPmethod [auth_feature("OwnWorkunitsAccess:WRITE")] WUCreateAndUpdate(WUUpdateRequest, WUUpdateResponse);
     ESPmethod WUUpdate(WUUpdateRequest, WUUpdateResponse);
@@ -1866,39 +1866,39 @@ ESPservice [
     ESPmethod WUCompileECL(WUCompileECLRequest, WUCompileECLResponse);
     
     //ESPmethod WUAction(WUActionRequest, WUActionResponse); 
-    ESPmethod WUFile(WULogFileRequest, WULogFileResponse);
+    ESPmethod [cache_seconds(60)]WUFile(WULogFileRequest, WULogFileResponse);
     ESPmethod [resp_xsl_default("/esp/xslt/graphStats.xslt")] WUProcessGraph(WUProcessGraphRequest, WUProcessGraphResponse); 
-    ESPmethod [min_ver("1.57")] WUGetGraphNameAndTypes(WUGetGraphNameAndTypesRequest, WUGetGraphNameAndTypesResponse);
-    ESPmethod WUGetGraph(WUGetGraphRequest, WUGetGraphResponse);
-    ESPmethod WUQueryGetGraph(WUQueryGetGraphRequest, WUQueryGetGraphResponse);
-    ESPmethod WUGetDependancyTrees(WUGetDependancyTreesRequest, WUGetDependancyTreesResponse);
+    ESPmethod [cache_seconds(30), min_ver("1.57")] WUGetGraphNameAndTypes(WUGetGraphNameAndTypesRequest, WUGetGraphNameAndTypesResponse);
+    ESPmethod [cache_seconds(30)] WUGetGraph(WUGetGraphRequest, WUGetGraphResponse);
+    ESPmethod [cache_seconds(30)] WUQueryGetGraph(WUQueryGetGraphRequest, WUQueryGetGraphResponse);
+    ESPmethod [cache_seconds(30)] WUGetDependancyTrees(WUGetDependancyTreesRequest, WUGetDependancyTreesResponse);
 
-    ESPmethod WUListLocalFileRequired(WUListLocalFileRequiredRequest, WUListLocalFileRequiredResponse);
+    ESPmethod [cache_seconds(60)] WUListLocalFileRequired(WUListLocalFileRequiredRequest, WUListLocalFileRequiredResponse);
     ESPmethod WUAddLocalFileToWorkunit(WUAddLocalFileToWorkunitRequest, WUAddLocalFileToWorkunitResponse);
     ESPmethod WUCDebug(WUDebugRequest, WUDebugResponse);
     
     ESPmethod [resp_xsl_default("/esp/xslt/WUPublishWorkunit.xslt")] WUPublishWorkunit(WUPublishWorkunitRequest, WUPublishWorkunitResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/WUQuerysets.xslt")] WUQuerysets(WUQuerysetsRequest, WUQuerysetsResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/WUQuerysetQueries.xslt")] WUQuerysetDetails(WUQuerySetDetailsRequest, WUQuerySetDetailsResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/WUQueryDetails.xslt")] WUQueryDetails(WUQueryDetailsRequest, WUQueryDetailsResponse);
-    ESPmethod WUMultiQuerysetDetails(WUMultiQuerySetDetailsRequest, WUMultiQuerySetDetailsResponse);
+    ESPmethod [cache_seconds(60), resp_xsl_default("/esp/xslt/WUQuerysets.xslt")] WUQuerysets(WUQuerysetsRequest, WUQuerysetsResponse);
+    ESPmethod [cache_seconds(60), resp_xsl_default("/esp/xslt/WUQuerysetQueries.xslt")] WUQuerysetDetails(WUQuerySetDetailsRequest, WUQuerySetDetailsResponse);
+    ESPmethod [cache_seconds(60), resp_xsl_default("/esp/xslt/WUQueryDetails.xslt")] WUQueryDetails(WUQueryDetailsRequest, WUQueryDetailsResponse);
+    ESPmethod [cache_seconds(60)] WUMultiQuerysetDetails(WUMultiQuerySetDetailsRequest, WUMultiQuerySetDetailsResponse);
     ESPmethod WUQuerysetQueryAction(WUQuerySetQueryActionRequest, WUQuerySetQueryActionResponse);
     ESPmethod WUQuerysetAliasAction(WUQuerySetAliasActionRequest, WUQuerySetAliasActionResponse);
     ESPmethod WUQuerysetCopyQuery(WUQuerySetCopyQueryRequest, WUQuerySetCopyQueryResponse);
     ESPmethod WUCopyQuerySet(WUCopyQuerySetRequest, WUCopyQuerySetResponse);
     ESPmethod [resp_xsl_default("/esp/xslt/WUCopyLogicalFiles.xslt")] WUCopyLogicalFiles(WUCopyLogicalFilesRequest, WUCopyLogicalFilesResponse);
     ESPmethod WUQueryConfig(WUQueryConfigRequest, WUQueryConfigResponse);
-    ESPmethod WUListQueries(WUListQueriesRequest, WUListQueriesResponse);
+    ESPmethod [cache_seconds(60)] WUListQueries(WUListQueriesRequest, WUListQueriesResponse);
     ESPmethod [min_ver("1.59")] WUUpdateQueryEntry(WUUpdateQueryEntryRequest, WUUpdateQueryEntryResponse);
-    ESPmethod WUQueryFiles(WUQueryFilesRequest, WUQueryFilesResponse);
-    ESPmethod [resp_xsl_default("/esp/xslt/QueriesUsingFile.xslt")] WUListQueriesUsingFile(WUListQueriesUsingFileRequest, WUListQueriesUsingFileResponse);
+    ESPmethod [cache_seconds(60)]WUQueryFiles(WUQueryFilesRequest, WUQueryFilesResponse);
+    ESPmethod [cache_seconds(60), resp_xsl_default("/esp/xslt/QueriesUsingFile.xslt")] WUListQueriesUsingFile(WUListQueriesUsingFileRequest, WUListQueriesUsingFileResponse);
     ESPmethod WUCreateZAPInfo(WUCreateZAPInfoRequest, WUCreateZAPInfoResponse);
     ESPmethod [resp_xsl_default("/esp/xslt/WUZAPInfoForm.xslt")] WUGetZAPInfo(WUGetZAPInfoRequest, WUGetZAPInfoResponse);
     ESPmethod WUCheckFeatures(WUCheckFeaturesRequest, WUCheckFeaturesResponse);
-    ESPmethod WUGetStats(WUGetStatsRequest, WUGetStatsResponse);
-    ESPmethod [min_ver("1.57")] WUListArchiveFiles(WUListArchiveFilesRequest, WUListArchiveFilesResponse);
-    ESPmethod [min_ver("1.57")] WUGetArchiveFile(WUGetArchiveFileRequest, WUGetArchiveFileResponse);
-    ESPmethod [min_ver("1.61")] WUGetNumFileToCopy(WUGetNumFileToCopyRequest, WUGetNumFileToCopyResponse);
+    ESPmethod [cache_seconds(30)] WUGetStats(WUGetStatsRequest, WUGetStatsResponse);
+    ESPmethod [cache_seconds(60), min_ver("1.57")] WUListArchiveFiles(WUListArchiveFilesRequest, WUListArchiveFilesResponse);
+    ESPmethod [cache_seconds(60), min_ver("1.57")] WUGetArchiveFile(WUGetArchiveFileRequest, WUGetArchiveFileResponse);
+    ESPmethod [cache_seconds(60), min_ver("1.61")] WUGetNumFileToCopy(WUGetNumFileToCopyRequest, WUGetNumFileToCopyResponse);
 };
 
 

+ 7 - 0
initfiles/componentfiles/configxml/esp.xsd.in

@@ -863,6 +863,13 @@
                     </xs:appinfo>
                 </xs:annotation>
             </xs:attribute>
+            <xs:attribute name="memCachedOptionString" type="xs:string" use="optional">
+                <xs:annotation>
+                    <xs:appinfo>
+                        <tooltip>Option string used by ESP memcached client.</tooltip>
+                    </xs:appinfo>
+                </xs:annotation>
+            </xs:attribute>
         </xs:complexType>
     </xs:element>
 </xs:schema>

+ 8 - 0
tools/hidl/hidlcomp.cpp

@@ -5670,6 +5670,14 @@ void EspServInfo::write_esp_binding()
             StrBuffer tmp; 
             outf("\taddMethodHelp(\"%s\", \"%s\");\n", mthi->getName(), printfEncode(val.str(), tmp).str());
         }
+        int cacheSeconds = mthi->getMetaInt("cache_seconds", -1);
+        if (cacheSeconds > -1) {
+            outf("\taddMemCachedSeconds(\"%s\", %d);\n", mthi->getName(), cacheSeconds);
+        }
+        int cacheGlobel = mthi->getMetaInt("cache_globel", 0);
+        if (cacheGlobel > 0) {
+            outf("\taddMemCachedGlobal(\"%s\", 1);\n", mthi->getName());
+        }
     }
     outs("}\n");