Business Rule Ordering and a 4,500-Row Nightly Import
Incident handling has three business rules: a before-update rule that sets assignment group from category, an after-update rule that sends an email and updates the related problem record, and an async rule that recalculates priority. Users report duplicate emails, occasional maximum-callstack errors, and nightly imports of 4,500 records timing out. A developer proposes moving everything into one before-update rule with current.update() to guarantee order, and adding setWorkflow(false) to speed up the import. The change board wants a recommendation before the next release.
1. Evaluate the current rule placement. Where should the email and problem update live?
Read the worked answer
Before-update rule setting assignment group: correct. It changes a field on the same record, and doing it before the write means one database operation instead of two.
Email: move to an after-update rule (or a notification). Sending from a before rule means you can email about a change that then fails to commit. Duplicate emails almost always mean the after rule fires on updates that did not actually change anything - add a changes() condition or restrict to the fields that matter.
Related problem update: after-update is acceptable, but guard it. If it writes back to the incident, or if the problem update re-triggers a rule that touches the incident, you get recursion and eventually the callstack error.
Async priority recalculation: fine if it does not need to block the transaction. Remember async rules cannot abort the save, so do not put validation there.
2. Assess `current.update()` inside a before rule and `setWorkflow(false)` on the import.
Read the worked answer
current.update() in a before rule: reject. The record is about to be written anyway; calling update re-enters the business rule engine, re-runs the same rule, and recurses until the stack dies. It also produces extra audit entries and confusing sys_mod_count values. Remove it and set fields directly on current.
setWorkflow(false): use deliberately, never as a speed toggle. It suppresses business rules and workflows for that operation, which means validations, notifications, and assignment logic silently do not run. If the import legitimately must skip them, scope it to the import's GlideRecord, log that it happened, and set the expected business state explicitly afterward. If the goal is speed, first find the actual cost - usually per-record queries inside a loop, not rule execution.
3. The nightly import times out. What is the most likely cause and how do you fix it?
Read the worked answer
Most likely cause: N+1 queries. A rule or transform script runs a GlideRecord lookup for each of 4,500 rows - for example, resolving the assignment group by name per record. That is 4,500 extra round-trips plus any dot-walking.
Fixes, in order:
- Hoist lookups out of the loop: build a map of name → sys_id once, or use
GlideAggregateto precompute counts. - Cut dot-walking inside loops; query the referenced record once.
- Make sure the async priority rule is not enqueuing thousands of events that then compete for workers.
- Use a transform map with coalesce so re-runs do not duplicate records.
- Batch the import and set a realistic timeout, or move it to a scheduled job with progress logging.
What not to do: setWorkflow(false) as the first response. It may hide the timeout by skipping the expensive rule, and it also skips the logic that made the records correct.