Browse Source

HPCC-19386 Reset Transaction Seed using pre-configured conditions

A user of ESP transaction logging asks for resetting transaction seed
after transaction ID reaches a limit of the ID length. ESP transaction
logging should allow a user to pre-configure a logging agent to reset
transaction seed and sequence number in order to meet the limit of the
ID length.

Signed-off-by: wangkx <kevin.wang@lexisnexis.com>
wangkx 7 years ago
parent
commit
b7316be17e

+ 67 - 6
esp/logging/loggingagent/espserverloggingagent/loggingagent.cpp

@@ -28,6 +28,9 @@ static const char* const PropServerUrl = "@url";
 static const char* const PropServerUserID = "@user";
 static const char* const PropServerPassword = "@password";
 static const char* const PropServerWaitingSeconds = "MaxServerWaitingSeconds";
+static const char* const PropMaxTransIDLength = "MaxTransIDLength";
+static const char* const PropMaxTransIDSequenceNumber = "MaxTransIDSequenceNumber";
+static const char* const PropMaxTransSeedTimeoutMinutes = "MaxTransSeedTimeoutMinutes";
 static const char* const MaxTriesGTS = "MaxTriesGTS";
 static const char* const appESPServerLoggingAgent = "ESPServerLoggingAgent";
 
@@ -69,11 +72,25 @@ bool CESPServerLoggingAgent::init(const char * name, const char * type, IPropert
         if (!found || !*found)
         {
             uniqueGroupNames.setValue(groupName.str(), true);
+
+            unsigned maxSeq = 0;
+            unsigned maxLength = 0;
+            unsigned seedExpiredSeconds = 0;
+            VStringBuffer xpath("LogGroup/[@name='%s']", groupName.str());
+            IPropertyTree* logGroup = cfg->queryBranch(xpath.str());
+            if (logGroup)
+            {
+                maxLength = logGroup->getPropInt(PropMaxTransIDLength, 0);
+                maxSeq = logGroup->getPropInt(PropMaxTransIDSequenceNumber, 0),
+                seedExpiredSeconds = 60 * logGroup->getPropInt(PropMaxTransSeedTimeoutMinutes, 0);
+            }
+
             StringBuffer transactionSeed, statusMessage;
             getTransactionSeed(groupName.str(), transactionSeed, statusMessage);
             if (transactionSeed.length() > 0)
             {
-                Owned<CTransIDBuilder> entry = new CTransIDBuilder(transactionSeed.str(), false);
+                Owned<CTransIDBuilder> entry = new CTransIDBuilder(transactionSeed.str(), false,
+                    maxLength, maxSeq, seedExpiredSeconds);
                 transIDMap.setValue(groupName.str(), entry);
                 if (iter->query().getPropBool("@default", false))
                     defaultGroup.set(groupName.str());
@@ -83,7 +100,9 @@ bool CESPServerLoggingAgent::init(const char * name, const char * type, IPropert
         }
     }
     createLocalTransactionSeed(localTransactionSeed);
-    Owned<CTransIDBuilder> localTransactionEntry = new CTransIDBuilder(localTransactionSeed.str(), true);
+    Owned<CTransIDBuilder> localTransactionEntry = new CTransIDBuilder(localTransactionSeed.str(), true,
+        cfg->getPropInt(PropMaxTransIDLength, 0), cfg->getPropInt(PropMaxTransIDSequenceNumber, 0),
+        60 * cfg->getPropInt(PropMaxTransSeedTimeoutMinutes, 0));
     transIDMap.setValue(appESPServerLoggingAgent, localTransactionEntry);
 
     readAllLogFilters(cfg);
@@ -235,6 +254,7 @@ bool CESPServerLoggingAgent::getTransactionSeed(StringBuffer& soapreq, int& stat
     if (!pTree)
         throw MakeStringException(EspLoggingErrors::GetTransactionSeedFailed, "Failed to read response from %s", serverUrl.str());
 
+    //statusCode: 0 = success, -1 = failed after retries
     statusCode = pTree->getPropInt("soap:Body/GetTransactionSeedResponse/StatusCode");
     statusMessage.set(pTree->queryProp("soap:Body/GetTransactionSeedResponse/StatusMessage"));
     seedID.set(pTree->queryProp("soap:Body/GetTransactionSeedResponse/SeedId"));
@@ -244,24 +264,65 @@ bool CESPServerLoggingAgent::getTransactionSeed(StringBuffer& soapreq, int& stat
     return true;
 }
 
+void CESPServerLoggingAgent::resetTransSeed(CTransIDBuilder *builder, const char* groupName)
+{
+    StringBuffer transactionSeed, statusMessage;
+    if (builder->isLocalSeed())
+        createLocalTransactionSeed(transactionSeed);
+    else
+    {
+        int statusCode = getTransactionSeed(groupName, transactionSeed, statusMessage);
+        if (!transactionSeed.length() || (statusCode != 0))
+        {
+            StringBuffer msg = "Failed to get Transaction Seed for ";
+            msg.append(groupName).append(". statusCode: ").append(statusCode);
+            if (!statusMessage.isEmpty())
+                msg.append(", ").append(statusMessage.str());
+            throw MakeStringException(EspLoggingErrors::GetTransactionSeedFailed, "%s", msg.str());
+        }
+    }
+
+    builder->resetTransSeed(transactionSeed.str());
+};
+
 void CESPServerLoggingAgent::getTransactionID(StringAttrMapping* transFields, StringBuffer& transactionID)
 {
-    CTransIDBuilder* transIDBuilder = NULL;
-    StringAttr* source = transFields->getValue(sTransactionMethod);
+    const char* groupName = nullptr;
+    CTransIDBuilder* transIDBuilder = nullptr;
+    StringAttr* source = nullptr;
+    if (transFields)
+        source = transFields->getValue(sTransactionMethod);
     if (source)
     {
         CLogSource* logSource = logSources.getValue(source->get());
         if (logSource)
-            transIDBuilder = transIDMap.getValue(logSource->getGroupName());
+        {
+            groupName = logSource->getGroupName();
+            transIDBuilder = transIDMap.getValue(groupName);
+        }
     }
     if (!transIDBuilder && (defaultGroup.length() != 0))
-        transIDBuilder = transIDMap.getValue(defaultGroup.str());
+    {
+        groupName = defaultGroup.str();
+        transIDBuilder = transIDMap.getValue(groupName);
+    }
     if (!transIDBuilder)
+    {
+        groupName = appESPServerLoggingAgent;
         transIDBuilder = transIDMap.getValue(appESPServerLoggingAgent);
+    }
     if (!transIDBuilder) //This should not happen.
         throw MakeStringException(EspLoggingErrors::GetTransactionSeedFailed, "Failed to getTransactionID");
 
+    if (!transIDBuilder->checkMaxSequenceNumber() || !transIDBuilder->checkTimeout())
+        resetTransSeed(transIDBuilder, groupName);
+
     transIDBuilder->getTransID(transFields, transactionID);
+    if (!transIDBuilder->checkMaxLength(transactionID.length()))
+    {
+        resetTransSeed(transIDBuilder, groupName);
+        transIDBuilder->getTransID(transFields, transactionID);
+    }
     return;
 }
 

+ 39 - 2
esp/logging/loggingagent/espserverloggingagent/loggingagent.hpp

@@ -45,7 +45,13 @@ class CTransIDBuilder : public CInterface, implements IInterface
 {
     StringAttr seed;
     bool localSeed;
-    unsigned __int64 seq;
+    unsigned __int64 seq = 0;
+
+    unsigned maxLength = 0;
+    unsigned maxSeq = 0;
+    unsigned seedExpiredSeconds = 0;
+    time_t createTime;
+
     void add(StringAttrMapping* transIDFields, const char* key, StringBuffer& id)
     {
         StringAttr* value = transIDFields->getValue(key);
@@ -62,9 +68,39 @@ class CTransIDBuilder : public CInterface, implements IInterface
 
 public:
     IMPLEMENT_IINTERFACE;
-    CTransIDBuilder(const char* _seed, bool _localSeed) : seed(_seed), localSeed(_localSeed), seq(0) { };
+    CTransIDBuilder(const char* _seed, bool _localSeed, unsigned _maxLength, unsigned _maxSeq, unsigned _seedExpiredSeconds)
+        : seed(_seed), localSeed(_localSeed), maxLength(_maxLength), maxSeq(_maxSeq), seedExpiredSeconds(_seedExpiredSeconds)
+    {
+        CDateTime now;
+        now.setNow();
+        createTime = now.getSimple();
+    };
     virtual ~CTransIDBuilder() {};
 
+    bool checkMaxSequenceNumber() { return (maxSeq == 0) || (seq < maxSeq); };
+    bool checkMaxLength(unsigned length) { return (maxLength == 0) || (length <= maxLength); };
+    bool checkTimeout()
+    {
+        if (seedExpiredSeconds ==0)
+            return true;
+
+        CDateTime now;
+        now.setNow();
+        return now.getSimple() < createTime + seedExpiredSeconds;
+    };
+    bool isLocalSeed() { return localSeed; };
+    void resetTransSeed(const char* newSeed)
+    {
+        if (isEmptyString(newSeed))
+            throw MakeStringException(EspLoggingErrors::GetTransactionSeedFailed, "TransactionSeed cannot be empty.");
+        seed.set(newSeed);
+        seq = 0;
+
+        CDateTime now;
+        now.setNow();
+        createTime = now.getSimple();
+    };
+
     virtual const char* getTransSeed() { return seed.get(); };
     virtual void getTransID(StringAttrMapping* transIDFields, StringBuffer& id)
     {
@@ -124,6 +160,7 @@ class CESPServerLoggingAgent : public CInterface, implements IEspLogAgent
     bool sendHTTPRequest(StringBuffer& req, StringBuffer& resp, StringBuffer& status);
     int getTransactionSeed(const char* source, StringBuffer& transactionSeed, StringBuffer& statusMessage);
     bool getTransactionSeed(StringBuffer& soapreq, int& statusCode, StringBuffer& statusMessage, StringBuffer& seedID);
+    void resetTransSeed(CTransIDBuilder *builder, const char* groupName);
 
     virtual void createLocalTransactionSeed(StringBuffer& transactionSeed);
 

+ 21 - 0
initfiles/componentfiles/configxml/@temp/logging_agent.xsl

@@ -42,6 +42,15 @@ xmlns:set="http://exslt.org/sets">
         <xsl:param name="agentNode"/>
         <xsl:for-each select="$agentNode/LogGroup">
             <LogGroup name="{current()/@name}">
+                <xsl:if test="string(current()/@MaxTransIDLength) != ''">
+                    <MaxTransIDLength><xsl:value-of select="current()/@MaxTransIDLength"/></MaxTransIDLength>
+                </xsl:if>
+                <xsl:if test="string(current()/@MaxTransIDSequenceNumber) != ''">
+                    <MaxTransIDSequenceNumber><xsl:value-of select="current()/@MaxTransIDSequenceNumber"/></MaxTransIDSequenceNumber>
+                </xsl:if>
+                <xsl:if test="string(current()/@MaxTransSeedTimeoutMinutes) != ''">
+                    <MaxTransSeedTimeoutMinutes><xsl:value-of select="current()/@MaxTransSeedTimeoutMinutes"/></MaxTransSeedTimeoutMinutes>
+                </xsl:if>
                 <xsl:for-each select="current()/Fieldmap">
                     <Fieldmap table="{current()/@table}">
                         <xsl:for-each select="current()/Field">
@@ -159,12 +168,24 @@ xmlns:set="http://exslt.org/sets">
             <xsl:if test="string($agentNode/@MaxServerWaitingSeconds) != ''">
                 <MaxServerWaitingSeconds><xsl:value-of select="$agentNode/@MaxServerWaitingSeconds"/></MaxServerWaitingSeconds>
             </xsl:if>
+            <xsl:if test="string($agentNode/@MaxTransIDLength) != ''">
+                <MaxTransIDLength><xsl:value-of select="$agentNode/@MaxTransIDLength"/></MaxTransIDLength>
+            </xsl:if>
+            <xsl:if test="string($agentNode/@MaxTransIDSequenceNumber) != ''">
+                <MaxTransIDSequenceNumber><xsl:value-of select="$agentNode/@MaxTransIDSequenceNumber"/></MaxTransIDSequenceNumber>
+            </xsl:if>
+            <xsl:if test="string($agentNode/@MaxTransSeedTimeoutMinutes) != ''">
+                <MaxTransSeedTimeoutMinutes><xsl:value-of select="$agentNode/@MaxTransSeedTimeoutMinutes"/></MaxTransSeedTimeoutMinutes>
+            </xsl:if>
             <xsl:call-template name="LogBasic">
                 <xsl:with-param name="agentNode" select="$agentNode"/>
             </xsl:call-template>
             <xsl:call-template name="LogSourceMap">
                 <xsl:with-param name="agentNode" select="$agentNode"/>
             </xsl:call-template>
+            <xsl:call-template name="LogGroup">
+                <xsl:with-param name="agentNode" select="$agentNode"/>
+            </xsl:call-template>
             <Filters>
                 <xsl:for-each select="$agentNode/Filter">
                     <Filter value="{current()/@filter}" type="{current()/@type}"/>

+ 51 - 0
initfiles/componentfiles/configxml/esploggingagent.xsd

@@ -100,6 +100,57 @@
           </xs:appinfo>
         </xs:annotation>
       </xs:attribute>
+      <xs:attribute name="MaxTransIDLength" type="xs:nonNegativeInteger" use="optional">
+        <xs:annotation>
+          <xs:appinfo>
+            <tooltip>Maximum length of a transaction ID. The 0 means no limit.</tooltip>
+          </xs:appinfo>
+        </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="MaxTransIDSequenceNumber" type="xs:nonNegativeInteger" use="optional">
+        <xs:annotation>
+          <xs:appinfo>
+            <tooltip>Maximum sequence number in a transaction ID. The 0 means no limit. </tooltip>
+          </xs:appinfo>
+        </xs:annotation>
+      </xs:attribute>
+      <xs:attribute name="MaxTransSeedTimeoutMinutes" type="xs:nonNegativeInteger" use="optional">
+        <xs:annotation>
+          <xs:appinfo>
+            <tooltip>How many minutes a TransSeed is timed out. The 0 means no limit.</tooltip>
+          </xs:appinfo>
+        </xs:annotation>
+      </xs:attribute>
+      <xs:element name="LogGroup" maxOccurs="unbounded">
+        <xs:annotation>
+          <xs:appinfo>
+            <title>Log Group</title>
+          </xs:appinfo>
+        </xs:annotation>
+        <xs:complexType>
+          <xs:attribute name="MaxTransIDLength" type="xs:nonNegativeInteger" use="optional">
+            <xs:annotation>
+              <xs:appinfo>
+                <colIndex>2</colIndex>
+              </xs:appinfo>
+            </xs:annotation>
+          </xs:attribute>
+          <xs:attribute name="MaxTransIDSequenceNumber" type="xs:nonNegativeInteger" use="optional">
+            <xs:annotation>
+              <xs:appinfo>
+                <colIndex>3</colIndex>
+              </xs:appinfo>
+            </xs:annotation>
+          </xs:attribute>
+          <xs:attribute name="MaxTransSeedTimeoutMinutes" type="xs:nonNegativeInteger" use="optional">
+            <xs:annotation>
+              <xs:appinfo>
+                <colIndex>4</colIndex>
+              </xs:appinfo>
+            </xs:annotation>
+          </xs:attribute>
+        </xs:complexType>
+      </xs:element>
       <xs:element name="LogSourceMap" maxOccurs="unbounded">
         <xs:annotation>
           <xs:appinfo>