
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
NOTE: MY POSTINGS REFLECT MY OWN VIEWS AND DO NOT NECESSARILY REPRESENT THE VIEWS OF MY EMPLOYER, ACCENTURE.
DIFFICULTY LEVEL: INTERMEDIATE
Assumes good knowledge and/or familiarity of Orchestration, Workflows, and Scripting in ServiceNow. Assumes having taken the class SSNF and has good intermediate level of knowledge and/or familiarity with Scripting in ServiceNow.
There is something I have observed as a performance issue: Hung Workflow jobs. A lot of administrators simply don't know that there may be literally thousands of these sitting there in the Active Contexts list. It depends on the age of the instance. A worker process has to be assigned every few seconds to go spin through all of these and see if there has been a state change. If so, then the worker handles the change. If not, it keeps looking until it reaches the end, and goes off to do other work. Enough of these and that worker may be tied down for a considerable the duration.
Therefore I have wanted a way to kill hung (executing) contexts en masse for some time. This feature is not available in ServiceNow out-of-the-box (OOtB). However, I have found code on the web that allows you to blow everything out of the Executing Workflows list (not my desired functionality).
Now I have been mentioning this for awhile in my ServiceNow Scripting classes (trying to get someone else to write it!). One class recently flat out challenged me to write one and publish it here. I finally broke down and took the challenge! Here you are!
I started by investigating how the Cancel Workflow UI Action from the executing Workflow Context works. It uses GlideAjax and a couple of Glide dialogs to manage the cancelling of an active workflow. The GlideAjax function calls an Ajax Script Include (WorkflowCancelKill) on the server-side. This script is passed the current workflow context sys_id and attempts to cancel the job. If it fails, it then prompts the user if they want to attempt to kill the context. If so, then another call is made to the Ajax Script Include to fire a broadcastKill call and try to take it out that way. If that fails then, well, you manually need to go delete that job.
So, borrowing heavily from the OOtB Cancel, I used essentially the same functionality, but adapted it to a List View UI Action.
Scripting Features Utilized:
Client Script
g_list object
this.bind
callback function
List Choice functionality from a UI Action
Script Include
Workflow object
cancelContext
broadcastKill
Lab 1.1: Creating the Ajax Script Include
First we will need to create our script include that carries out our Cancel and Execute commands.
1. From your ServiceNow instance navigate to System Definition > Script Includes. The list view of Script Includes will be displayed.
2. Click on the New button. The new Script Include form will be displayed.
3. Fill in the form with the following:
a. Name: CancelWorkflowContextsAjax
b. Accessible From: All application scopes
c. Client Callable: Checked
d. Active: Checked
e. Description: Kill context from the executing workflows list view
f. Script:
var CancelWorkflowContextsAjax = Class.create();
CancelWorkflowContextsAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {
cancelContexts: function () {
var cancelIds = this.getParameter('sysparm_contextIds').split(',');
// loop through all of our workflow context sys_ids
for (var i = 0; i < cancelIds.length; i++) {
this.cancelSingleContext(cancelIds[i]);
}
gs.info('[{0}]:\n {1}', this.type, this.message);
},
// Cancel OR Kill a single workflow context
cancelSingleContext: function (contextId) {
try {
this.message += '\n---> Attempting to cancel: ' + contextId + '\n';
var wfContext = new GlideRecord('wf_context');
wfContext.get(contextId);
new Workflow().cancelContext(wfContext);
this.message += '---> Workflow canceled: ' + contextId + '\n';
} catch (err) {
this.message += '---> ERROR: ' + err + '\n';
// If we fail and it was due to a ModelRunTimeException, then attempt a kill
// Interestingly since we do not have "instanceOf" available in the language
// definition we have to play the game of searching for it in the message itself
// (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof)
//com.glideapp.workflow.model.ModelRunTimeException
if (err.toString().indexOf('ModelRunTimeException') > -1) {
this.killContext(contextId);
}
}
},
// This fires a kill message to the specified workflow context
killContext: function (contextId) {
this.message += '---> Attempting to kill context: ' + contextId + '\n';
var wfContext = new GlideRecord('wf_context');
wfContext.get(contextId);
var workFlow = new Workflow();
workFlow.cancel(wfContext); // first cancel the context
workFlow.broadcastKill(wfContext); // now send the kill message to the context
this.message += '---> Workflow context killed: ' + contextId + '\n';
},
type: "CancelWorkflowContextsAjax",
message: "" /* this is our general message container */
});
g. Click on the Submit button to save your work. When the Role pop-up shows select Admin and Okay.
We are now ready to build the browser-side code.
Lab 1.2: Creating the Ajax UI Action
Now we will create our List View List Choice UI Action to carry out actions against a list of chosen executing Workflows
- From your ServiceNow instance navigate to System UI > UI Actions. This will display the List View of all UI Actions.
- Click on the New button. This will bring up a blank UI Action form.
- Fill in the form with the following:
- Name: Cancel Workflows
- Table: Workflow Context [wf_context]
- Order: 250
- Action Name: blank
- Active: checked
- Show insert: checked
- Show update: checked
- Client: checked
- List choice: checked. All other check boxes: unchecked
- Comments: Cancel the selected list of workflows. If cancel fails attempt to automatically force the kill.
- Hint: Cancel the selected list of workflows
- Onclick: cancelWorkflowContexts();
- Condition: gs.hasRole('admin')
- Script:
function cancelWorkflowContexts() {
var contextIds = g_list.getChecked().split(',');
if (!contextIds || contextIds.length == 0) {
return;
}
// our callback function from the dialog that executes the ajax call
var callback = function () {
var cancelContexts = new GlideAjax("CancelWorkflowContextsAjax");
cancelContexts.addParam("sysparm_name", "cancelContexts");
cancelContexts.addParam("sysparm_contextIds", contextIds.join(','));
// Our Async callback handler
var handleResponse = function () {
reloadWindow(window); // reload the list view
};
// We really don't care about the answer, and throw it away
// Our callback here is to fire the reloadWindow
cancelContexts.getXMLAnswer(handleResponse.bind(this));
return true;
};
// Glide dialog window - this is fancier than JavaScript prompt
var dialogClass = window.GlideModal ? GlideModal : GlideDialogWindow;
var dialog = new dialogClass('glide_confirm_standard');
dialog.setTitle('Confirmation');
dialog.setPreference('warning', true);
var title = 'Are you sure you want to cancel the selected Worklfow Contexts?';
dialog.setPreference('title', title);
dialog.setPreference('onPromptComplete', callback.bind(this));
dialog.render();
}
o. Click on the Submit button to save your work.
We are now ready to set up our test, and test our our new UI Action!
Lab 1.3: Testing
Finally we will create bad workflow that will hang, and fire it off a few times.
1. Navigate to Workflow > Workflow Editor. The Workflow Editor will be displayed in a new browser tab.
2. From the Workflows tab on the right, click on the "+" button to create a new workflow. The New Workflow form will be displayed.
3. Fill in the form with the following:
a. Name: Broken Workflow
b. Table: Global
c. Click on the Submit button to create your new workflow
4. From the Workflow Editor navigate to Core > Utilities and drag out a new Run Script Activity
a. Name: Initialize
b. Script:
gs.info('---> Broke Workflow!');
c. Click on the Submit button to save your activity.
5. From the Workflow Editor navigate to Core > Approvals and drag out a new Approval - User Activity
a. Name: Do Nothing Approval
b. Users: Add Beth Anglin to the list
c. Click on the Submit button to save your activity
6. Wire up your Workflow to look like this:
7. Run the workflow five times. Since Beth is not around to approve things this effectively "hangs" the workflow.
8. From your ServiceNow instance navigate to System Logs > All. The list view of log entries will appear. Filter on Source contains Broken. You should see your workflow logging itself.
8. From your ServiceNow instance navigate to Workflow > Active Contexts. The list view of active contexts should appear.
9. Filter on Workflow Version = Broken Workflow. Your five test workflows should be shown with State of Executing
10. Check a couple of the list view check boxes to select the workflows you want to cancel.
11. Click on the Actions on Selected Rows dropdown at the bottom of the list view. A menu will appear displaying list action choices.
12. Click on the Cancel Workflows link. A dialog box will appear asking if your are sure you want to cancel the chosen workflows.
13. Click on the Ok button to confirm you want to cancel the workflows.
14. The list view will be refreshed, and you should have fewer executing workflows!
15. Navigate to: System Logs > All and filter on Message Contains Attempting. This will show you the logging from the Script Include.
16. Go ahead and play around with cancelling the remaining workflows using Steps 10 through 12.
So there you have it! A List Choice UI Action that allows you to cancel multiple executing workflow contexts from a List View.
Enjoy!
Steven Bell.
If you find this article helps you, don't forget to log in and mark it as "Helpful"!
Originally published on: 11-01-2015 05:59 PM
I updated the code, fixed broken links, and brought the article into alignment with my new formatting standard.
- 2,503 Views
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.