How To Use Session Affinity in Data Center Tests or Automation

Description

Atlassian Jira and Confluence have Data Center (Cluster) editions. Many Data Center installations use a load balancer that uses session affinity to distribute work to nodes in the cluster. This can present a challenge when writing testcases or automation that may be impacted by inter-node latency. For example, an issue created on one node may not immediately show up in a JQL search run on another node. The latency is usually very low (seconds) but any automation can be impacted in these cases. While a simplistic approach is to put in sleep delays in the hopes of mitigating this problem, there are better ways.

Some documentation refers to session affinity as sticky sessions. See Atlassian documentation like Load Balancer Configuration Options for more information.


Use Session Affinity

Both the Jira Command Line Interface (CLI) and Confluence Command Line Interface (CLI) support session authentication. Any actions run as part of a run or runFrom action automatically take advantage of session authentication and therefore support session affinity based clusters - namely running all the actions in the script on the same node.

However, GINT based test or automation scripts normally use a separate CLI action per testcase and therefore there is no session affinity unless some additional work is done. Here is what to add to the script to enable this. Basically, all you need to do is login and use the session token provide in subsequent actions.

These examples require Release 2.8 or higher.

Example - Jira

...
includeTool << GintForJira
...
def info = gint.getServerInfoWithVerify()  // verify server is available, otherwise end test
...
gint.add(
    action: 'login', description: 'Get a login token for session affinity and change the default jira command generator to use it',
    neededBy: true, // make sure this testcase runs before any other testcase
    finalClosure: { testcase ->
        def parameters = gint.getCmdGeneratorParameters() + [cli: helper.getParameterValueWithExtendedLookup('cli') + ' --login ' + testcase.outData[0]]
        gint.setCmdGenerator(gint.getGintCmdGenerator().getGenerator('confluenceMacro', parameters))
    },
)

// Clean-up the session at the end
target(name: 'final', description: 'Run this last', prehook: [], posthook: []) {
    helper.runCmd(cmd: gint.getCmd(action: 'logout')) // getCmd uses the jira command generator 
}

Example - Confluence

...
includeTool << GintForConfluence
...
def info = gint.getServerInfoWithVerify()  // verify server is available, otherwise end test
...
gint.add(
    action: 'login', description: 'Get a login token for session affinity and change the default confluence command generator to use it',
    neededBy: true, // make sure this testcase runs before any other testcase
    finalClosure: { testcase ->
        def parameters = gint.getCmdGeneratorParameters() + [cli: helper.getParameterValueWithExtendedLookup('cli') + ' --login ' + testcase.outData[0]]
        gint.setCmdGenerator(gint.getGintCmdGenerator().getGenerator('confluenceMacro', parameters))
    },
)

// Clean-up the session at the end
target(name: 'final', description: 'Run this last', prehook: [], posthook: []) {
    helper.runCmd(cmd: gint.getCmd(action: 'logout')) // getCmd uses the confluence command generator 
}