I'm running into a sporadic, not-testably-reproducible issue regarding a Custom Script Listener that updates an issue's due-date upon Priority change.
For some issues, upon changing the priority, there seems to be an surge of Issue Update events that mirror the originally-triggering event and causes a huge amount of email spam (looking at the logs, the Custom Listener is being triggered every 5ms when this happens). From the debug text in my script, when this occurs, the event where the priority was first change seems to continually get registered as a new event (they all show the same ChangeItems list referring to the initial change).
This doesn't always happen—in most cases, the custom listener updates the issue's due date without causing spam or triggering more events that trip off the same custom listener. But when it does happen, it cause a huge CPU spike and floods our mail queue.
Since the custom listener is configured to trigger off of "Issue Update" events via the ScriptListener config, I'm assuming the following code (in particular, the last one where issueManager.updateissue is used) that updates the issueEvent with the updated dueDate somehow triggers an edge case that causes this bug to surface:
void updateIssueWithNewDueDate(Timestamp dueDate, IssueEventevent, String fieldName, StringBuffer debugText){
def issue = event.getIssue();
MutableIssue mutableIssue = (MutableIssue) issue;
if(!dueDate){
return
}else{
IssueManager issueManager = ComponentAccessor.getComponent(IssueManager.class)
mutableIssue.setDueDate(dueDate)
issueManager.updateIssue(event.getUser(), mutableIssue, EventDispatchOption.ISSUE_UPDATED, false)
}
}
Question: Should I be using a different method other than issueManager.updateissue(...) for persisting the due-date change to the issue? Or is there some other bug underfoot here?(see: https://docs.atlassian.com/software/jira/docs/api/7.1.0-m01/com/atlassian/jira/issue/IssueManager.html#updateIssue-com.atlassian.jira.user.ApplicationUser-com.atlassian.jira.issue.MutableIssue-com.atlassian.jira.event.type.EventDispatchOption-boolean-)
Here is the entire custom scriptRunner for more details. A run down of the code:
The CustomListener is triggered against "Issue Updated" events on a project called SEC. The script should update the due-date of the issiue when the priority of the issue changes (based off an irrelevant SLA calculation), but only for issues of type "Vulnerability":
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.ComponentManager
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.event.issue.IssueEvent
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.model.ChangeItem
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.ModifiedValue
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.event.type.EventDispatchOption
import java.sql.Timestamp
import java.util.HashMap
import java.util.Map
def String FIELD_PRIORITY = "priority"
def String FIELD_DUE_DATE = "Due Date"
def String ISSUE_TYPE = "Vulnerability"
def String PRIORITY_CRITICAL = "Critical";
def String PRIORITY_MAJOR = "Major";
def String PRIORITY_MEDIUM = "Medium";
def String PRIORITY_MINOR = "Minor";
def String PRIORITY_TRIVIAL = "Trivial";
def int SLA_DAYS_CRITICAL = 1;
def int SLA_DAYS_MAJOR = 7;
def int SLA_DAYS_MEDIUM = 30;
def int SLA_DAYS_MINOR = 120;
def int SLA_DAYS_TRIVIAL = 120;
boolean wasFieldChanged(IssueEvent event, String fieldName, String issueType, StringBuffer debugText) {
Issue issue = event.issue
String issueTypeName = issue.getIssueType().getName()
boolean isVulnerability = issueType.equals(issueTypeName)
addFieldToDebugText(debugText, "isVulnerability", String.valueOf(isVulnerability))
def changeItems = event?.getChangeLog()?.getRelated("ChildChangeItem")
addFieldToDebugText(debugText, "changeItems", changeItems.toString())
return isVulnerability && event?.getChangeLog()?.getRelated("ChildChangeItem")?.find{it.field == fieldName}
}
String getPriorityChangeValue(IssueEvent event){
event.issue.getPriority().name
}
Timestamp getCreateDateFromIssue(IssueEvent event){
Timestamp createDate = event.issue.getCreated();
return createDate;
}
Timestamp getDueDateFromPriority(String priority, Timestamp createDate, Map<String, Integer> priority2SLAMap){
Integer slaDays = priority2SLAMap.get(priority);
if(!slaDays){
return null
}
Calendar cal = Calendar.getInstance();
cal.setTime(createDate);
cal.add(Calendar.DAY_OF_WEEK, slaDays);
Timestamp dueDate = new Timestamp(cal.getTime().getTime());
return dueDate;
}
void updateIssueWithNewDueDate(Timestamp dueDate, IssueEventevent, String fieldName, StringBuffer debugText){
def issue = event.getIssue();
MutableIssue mutableIssue = (MutableIssue) issue;
if(!dueDate){
return
}else{
IssueManager issueManager = ComponentAccessor.getComponent(IssueManager.class)
mutableIssue.setDueDate(dueDate)
issueManager.updateIssue(event.getUser(), mutableIssue, EventDispatchOption.ISSUE_UPDATED, false)
}
}
void addFieldToDebugText(StringBuffer debugText, String fieldName, String fieldValue){
debugText <<= " " + fieldName + ": " + (fieldValue ?: "null") + " "
}
def Map<String, Integer> priority2SLAMap = new HashMap<String, Integer>();
priority2SLAMap.put(PRIORITY_CRITICAL, SLA_DAYS_CRITICAL);
priority2SLAMap.put(PRIORITY_MAJOR, SLA_DAYS_MAJOR);
priority2SLAMap.put(PRIORITY_MEDIUM, SLA_DAYS_MEDIUM);
priority2SLAMap.put(PRIORITY_MINOR, SLA_DAYS_MINOR);
priority2SLAMap.put(PRIORITY_TRIVIAL, SLA_DAYS_TRIVIAL);
def StringBuffer debugText = 'DEBUG TEXT: ' << " "
def wasPriorityChanged = wasFieldChanged(event, FIELD_PRIORITY, ISSUE_TYPE, debugText)
addFieldToDebugText(debugText, "Was priorty Changed", String.valueOf(wasPriorityChanged))
if (wasPriorityChanged) {
String priority = getPriorityChangeValue(event);
addFieldToDebugText(debugText, "priority", priority)
Timestamp createDate = getCreateDateFromIssue(event)
addFieldToDebugText(debugText, "Create Date", createDate.toString())
Timestamp dueDate = getDueDateFromPriority(priority, createDate, priority2SLAMap)
addFieldToDebugText(debugText, FIELD_DUE_DATE, dueDate.toString())
updateIssueWithNewDueDate(dueDate, event, FIELD_DUE_DATE, debugText);
}
log.warn(debugText)
Hi David,
My best guess would be that this line that updates the issue
issueManager.updateIssue(event.getUser(), mutableIssue, EventDispatchOption.ISSUE_UPDATED, false)
is causing the listeners to be continually invoked because of the event that it dispatches. Your listener listens for the "Issue Updated" event - that's OK. However, when you set the due date and call updateIssue, you're firing another another "Issue Updated" event with the parameter: EventDispatchOption.ISSUE_UPDATED. That seems like it would again trigger the listener over and over and over.
Perhaps try dispatching no event when you update the issue?
issueManager.updateIssue(event.getUser(), mutableIssue, EventDispatchOption.DO_NOT_DISPATCH, false)
Good suggestion to use the DO_NOT_DISPATCH EventDispatchOption. I'll try that. Thank you.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi David,
I know you said the problem was sporadic, though I would like to follow up to see if you've experienced the same problem within the last couple of days. Has the DO_NOT_DISPATCH EventDispatchOption seemed to make a difference?
Thanks,
Josh
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi Josh,
I thought so at first, but unfortunately we are experiencing the same issue again, with watchers of tickets receiving hundreds of emails in succession after priority change. Is there another approach to updating the issue programmatically here? Perhaps a different method? Or is there something else you could think would be causing this issue that I should be looking into?
Best,
David
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi David,
Sorry to hear that it didn't work. Would you mind raising a support request with our dedicated support team? I think that they will be able to give your issue a deeper look. You can find the link to raise a support request through our portal here:
https://marketplace.atlassian.com/apps/6820/scriptrunner-for-jira?hosting=server&tab=support
You might want to include a link to this Community post when you raise the support request.
Thanks,
Josh
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.