The CreatorCon Call for Content is officially open! Get started here.

Fabian Kunzke
Kilo Sage
Kilo Sage

The series

Breaking ServiceNow can be incredibly easy. It happens to me on accident, on some occasions it reaches the level of absurdity, that i would classify myself as an expert (i know, i know, calling yourself an expert at something seems arrogant). So to share this absolutely useless expertise this series will focus on how not to do ServiceNow.

The one-liner of pure evil

And just to show you how easy it is, let's start this series off with just a single line of code. Just one. Just enough to completely destroy the performance of any instance.

current.insert();

Aaaaand we are done. Now don't be scared. A current.insert() won't break the instances performance on its own. That would be indeed too simple. But under the right circumstances, it does. Let's create those circumstances.

Go to the business rule table (sys_script) and create a new business rule as seen in the screenshot below (NOTE: for the love of god do not, i repeat, DO NOT try this in any more than a personal developer instance. Don't try it on a dev-instance. You will impact the performance of the instance. And so far i did not find an easy way of stopping it. You have been warned.😞

find_real_file.png

Then under the "Advanced" section just add the above mentioned one-liner. Click submit and watch the instances performance crumble.

Why does this happen?

Good question, glad you have asked. What happens is a special interaction of the "async" trigger and the current.insert();

It is important to understand when "async" is triggered. It is triggered, after the "display","before" and "after" has been run. This means, as soon as your business rule is inserted into the database, all "ordinary" business rules trigger. After those are finished, the system checks for "async" business rules. Since you have submitted/saved a business rule which is indeed running "async" it is taken into account and thus will be triggered. What follows now is a good to know interaction of the Glide Record function "insert".

This function will always insert a GlideRecord object. It does not care if the record already exists or not. It will insert it. Since "current" is in fact a GlideRecord object, the current record is inserted. Therefore, a business rule on the "sys_script" table is created and will follow the same principle as the original one. One difference though: since there already is a business rule which does a current.insert() you are now left with twice as many business rules. This will cascade to infinity (aaaand beyooooond).

The little brother of pure evil

Now, i hear you arguing:

1) who is mad enough to create a business rule which triggers on business rules

2) who is mad enough to do a current.insert()

3) what use is this of?

Let me answer the first two questions. Although i may sound mad enough, i hope no one will ever do this (except for scientific reasons of course). In this case, of what use is this then (which leads to question 3). The purpose of this is to show you how incredibly easy it is to create looping business rules. Let me introduce you to the little brother of pure evil (which i actually encountered more than once):

current.update();

Simple, right? A business rule, that updates some property of the record. Now, again, on its own this is not harmful. But, what are the circumstances you encounter this? Lets exclude some possibilities:

1) "before" business rule: here no current.update() is needed, since all value changes are proposed BEFORE changing something in the database

2) a workflow activity: After a workflow activity (e.g. a "execute script" activity) has been executed, an update will be done to the record of the workflows context. This record is in fact the "current" record. (therefore, an explicit current.update() is not needed)

What does that leave us with: 

1) "display" business rule

2) "after" business rule

3) "async" business rule

Now, i hope that everyone can agree, that a database interaction such as an update on "displaying" a record does not make any sense (except if you want to break your instance i guess, in which case i would salute to you). The other two options are risky:

Both these trigger points occur AFTER an update to the database has been made. A current.update() will trigger a new update to the database. In return this will trigger all business rules on its table again INCLUDING all "after" and "async" business rules. It is therefore very easy to create a loop. And loops are (as we now know from the current.inser()) pure evil.

And this is the reason why recommended coding practices will tell you to not use a current.update() in either workflows or business rules. Be aware that moving the current.update into a script include does not get rid of this problem.

What to take away from this?

1) Know about pure evil when it comes to performance (which is current.insert() in business rules and workflows)

2) Remember its little brother (current.update() in business rules and workflows)

3) if your goal is to get rid of your instances performance, use 1) and 2) as much as you can

4) repeat step 3)

5) ???

6) profit?!

7) remember: a .insert() will not check for an existing record