How to identify cyclic Issue links or loops in Jira
Platform Notice: Data Center Only - This article only applies to Atlassian products on the Data Center platform.
Note that this KB was created for the Data Center version of the product. Data Center KBs for non-Data-Center-specific features may also work for Server versions of the product, however they have not been tested. Support for Server* products ended on February 15th 2024. If you are running a Server product, you can visit the Atlassian Server end of support announcement to review your migration options.
*Except Fisheye and Crucible
Summary
Issues can be linked in several different ways in Jira. Some types of Issue Links are more strict than others, like the "parent subtask", "Epic Story" and "Parent Link" (with Advanced Roadmaps' Hierarchy configuration).
All other "regular Issue Links" are considered "soft links" in Jira and wouldn't cause any malfunction — unless some 3rd party apps are in use and they make use of these regular Issue Links to calculate Gantt critical paths, sum estimations or Story Points, etc.
In this latter scenario, having a cyclic link or loop may cause malfunctions in the app and in Jira, usually evidenced by a StackOverflowError in the atlassian-jira.log or an OutOfMemoryError, in the worst case.
This article helps identify these cyclic Issue Links in order to quickly break the endless loop while a more stable and long-term solution's implemented (either reconfiguring the 3rd party apps or working with the respective vendors).
Environment
Any Data Center version of Jira Software or Jira Service Management with either:
Advanced Roadmaps installed (through a separate app or bundled since Jira SW 8.15+)
Third party apps with Gantt-like features or JQL functions that traverse Issue Links recursively
Diagnosis
How to identify and fix other kinds of cycles
Some cyclic links are particular to Jira Software Epic Links or Advanced Roadmaps Hierarchy and have different diagnosis and resolutions:
Jira Advanced Roadmaps Plan crashes in blank page after adding Progress column
Issue screen displays error rendering greenhopper-epics-issue-web-panel in Jira server
How to identify circular linked issues from Advanced Roadmaps plan via Har file
Out of scope side note on some 3rd party app JQL functions
Each app vendor should be contacted for specialized support and guidance on their app's usage, but just for the sake of example, here are a few JQL functions provided by some Marketplace apps that have been known to cause StackOverflowError when facing cyclic links in Jira.
The general idea is to use other functions that allow a "depth" argument, so the function stops traversing Issue Links after a number of iterations, thus avoiding the endless loops.
App | Potentially risky JQL Function | Endless-loop-safe JQL function | Vendor documentation |
---|---|---|---|
linkedIssuesOfRecursive(...) | linkedIssuesOfRecursiveLimited(...) | https://docs.adaptavist.com/sr4js/latest/features/jql-functions/included-jql-functions/issue-links | |
linkedIssuesOfAllRecursive(...) | linkedIssuesOfAllRecursiveLimited(...) | https://docs.adaptavist.com/sr4js/latest/features/jql-functions/included-jql-functions/issue-links | |
linkedAllIssues(...) | linkedIssuesByDepth(...) |
(as these apps are not developed nor supported by Atlassian, the list may become stale, but still serve as an example)
Solution
The strategy consists of:
Query the DB for linked Issues and export the results in a CSV (text) format
Collect insights from the file for possible optimizations (optional)
Scan and parse the file, printing the cyclic links identified
Browse one Issue of each identified cycle in Jira's UI and remove the link to break the endless loop condition
Confirm if the endless loop issue persists
It's possible to perform the entire validation through the DB alone, though not every supported DB has builtin recursive features, thus this hybrid solution.
1. Query the DB
Run this query on Jira's DB and save the output as a CSV (comma separated value):
select
il1.id as "Link Id",
ilt.id as "Link Type Id",
ilt.linkname,
il1.source as "Source Issue Id",
concat(p1.pkey, concat('-', i1.issuenum)) as "Source Issue Key",
il1.destination as "Destination Issue Id",
concat(p2.pkey, concat('-', i2.issuenum)) as "Destination Issue Key"
from
issuelink il1
join
issuelink il2 on il2.linktype = il1.linktype
and il2.source = il1.destination
join
issuelinktype ilt on ilt.id = il1.linktype
join
jiraissue i1 on i1.id = il1.source
join
project p1 on p1.id = i1.project
join
jiraissue i2 on i2.id = il1.destination
join
project p2 on p2.id = i2.project
order by
ilt.id,
il1.source;
This will produce an output like:
554928,10000,Blocks,331409,JIRA-115,612509,JIRA-4746
569107,10000,Blocks,342386,JIRA-290,625599,JIRA-4773
569174,10000,Blocks,431799,CONF-2272,625599,CONF-4773
379384,10000,Blocks,468238,CONF-435,472170,CONF-529
2. Collect insights for optimizations (optional)
The exported CSV file may be still too large to parse, so maybe we can filter some Issue Link types unlikely to cause the endless loops (unless they're configured in the 3rd party apps to do so).
This example Linux grep command will print which Link Types appear the most:
cat jira-links.csv | cut -d"," -f3 | uniq -c | sort -nr
Example output:
3538 Cloners
2487 Relates
687 Hierarchy link (custom)
658 Blocks
400 Parent-Child Link
265 Relates
49 Duplicate
27 Issue split
Cloners is a Link Type that's unlikely to be configured in Gantt apps, but may trigger endless loops in JQL functions if somehow there's a cyclic link there.
3. Scan and parse the file
The command below can be used to scan and parse the CSV file and print whenever it spots a cyclic link:
$ grep -E -v "Cloners|Relates" jira-links.csv | sort -t',' -k2 -k4 -k6 | uniq | awk -v FS=',' 'BEGIN {split("", chains); found=false; linktype=""}; (NR > 1) {if ($2 != linktype) {split("", chains); linktype=$2}; if ($4 == $6) {print $3, $5, $7} else if (length(chains[$5]) > 0) {chains[$5] = chains[$5]","$7} else {found=false; for (c in chains) {if (index(chains[c], $5) > 0) {chains[c] = chains[c]","$7; found=true}}; if (!found) {chains[$5] = $5","$7}}; if (length(chains[$7]) > 0) {print $3, chains[$7]}}; (NR % 1000 == 0) {print "."}'
Blocks JIRA1-2506 JIRA1-2506
.
.
Blocks JIRA4-592 JIRA4-345 JIRA4-400 JIRA5-234 JIRA4-592
.
.
.
.
.
You can exclude the Link Types to parse by setting them in the first grep -E -v
:
grep -E -v "Cloners|Relates" jira-links.csv | sort ...
Or you can also only filter the Link Types you want to parse (by removing the -v
):
grep -E "Blocks" jira-links.csv | sort ...
Or disregard this optimization attempt completely and scan the whole file:
cat jira-links.csv | sort ...
4. Browse each Issue and break the link
To avoid the endless loop conditions, you may browse any one Issue on each printed line and remove the reported link.
Like on this Blocks loop:
Blocks JIRA4-592 JIRA4-345 JIRA4-400 JIRA5-234 JIRA4-592
You can remove the Blocks from JIRA4-592 to JIRA4-345; from JIRA4-345 to JIRA4-400, from JIRA4-400 to JIRA5-234 or from JIRA5-234 to JIRA4-592.
5. Confirm if the endless loop issue persists
After removing the Links, if it was done through Jira's UI, the Indexes should've been updated and you may validate if the endless loops continue.
Some 3rd party apps may require a reindex of their own data, not Jira's own reindex.
Please note this article only detects loops of the same Issue Link Type.
Some 3rd party apps may allow admins to configure multiple Link Types to calculate paths or estimation/effort. The app vendors should be contacted to assist on how to reestablish the feature and help trace down which Issues are causing the endless loop.
Was this helpful?