Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Custom Script Listener Email Spam Upon Issue Update

david_lebrun August 8, 2018

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) 

 

1 answer

Suggest an answer

Log in or Sign up to answer
0 votes
Joshua Yamdogo @ Adaptavist
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 15, 2018

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)

https://docs.atlassian.com/software/jira/docs/api/7.10.0/com/atlassian/jira/event/type/EventDispatchOption.html#DO_NOT_DISPATCH

david_lebrun August 15, 2018

Good suggestion to use the DO_NOT_DISPATCH EventDispatchOption. I'll try that.  Thank you.

Joshua Yamdogo @ Adaptavist
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 17, 2018

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

david_lebrun September 26, 2018

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

Joshua Yamdogo @ Adaptavist
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 26, 2018

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

TAGS
AUG Leaders

Atlassian Community Events