The Now Platform® Washington DC release is live. Watch now!

Help
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Jeremy Box
Tera Contributor

Has this ever happened to you?

Consider the following situation. You have been tasked with developing a user experience in ServiceNow that is a bit outside of the normal UI functionality. You decide to develop a UI Script to handle the requirements. Maybe you are consolidating logic that exists in Client Scripts into one function that is available on all forms. You develop and roll out the initial version which looks something like this:

function doTheStuff(g_form, fldName){
	var ga = new GlideAjax('DoStuffHelper');
	ga.addParam('sysparm_name','getTheStuff');
	ga.getXML(ParseUserData);
	function ParseUserData(response) {
		//parse JSON answer from the response
		var answer = response.responseXML.documentElement.getAttribute("answer");
		if(debugEnabled){
			console.log('doTheStuff: answer ---> ' + answer);
		}
		var ans_obj = JSON.parse(answer);
				if(debugEnabled){
			console.log('doTheStuff: stuff ---> ' + answer.stuff);
		}
		g_form.setValue(fldName, answer.stuff);
	}
}

You save, test, and roll to production. All is well. However, a bug is soon discovered and you need to troubleshoot it. You try calling the function with debug on, but it doesn't work. You research a bit and find that you forgot to declare the parameter for the function. So you fix the issue:

function doTheStuff(g_form, fldName, debugEnabled){
	var ga = new GlideAjax('DoStuffHelper');
	ga.addParam('sysparm_name','getTheStuff');
	ga.getXML(ParseUserData);
	function ParseUserData(response) {
		//parse JSON answer from the response
		var answer = response.responseXML.documentElement.getAttribute("answer");
		if(debugEnabled){
			console.log('doTheStuff: answer ---> ' + answer);
		}
		var ans_obj = JSON.parse(answer);
				if(debugEnabled){
			console.log('doTheStuff: stuff ---> ' + answer.stuff);
		}
		g_form.setValue(fldName, answer.stuff);
	}
}

You test your work thoroughly this time, but it keeps failing. The debug statements simply don't work. After much research, you discover that, although you updated the UI Script, the old version is still being loaded into the form. You try everything you know to no avail, but nothing fixes the issue (cache.do, multiple saves, toggling active, etc...). You become frustrated at this point and flush your browser cache, and voila! It's fixed. But your developers and customers that have previously used the functionality still have the issue. So you turn to workarounds.

To Flush or Not to Flush?

  • One workaround, albeit not a great one, is to have the affected users clear their browser cache (cookies). This is a terrible experience for the customer and basically just kicks the can down the road to the next update.
  • Another workaround you found is to rename the script. This is a better user experience for the customer, but frustrating for developers. You now have to track down every reference to the script and update it. Code search from studio makes this easier, but its still frustrating.

Fear Not; There is a Better Workaround

...and I do mean workaround. This is by no means to be considered a fix. The secret is in the browser cookies. We know that many plugins contain UI Scripts. These applications are updated from time to time (including the UI Scripts) and that works fine. What's the difference? Activating a plugin triggers a forced cache update for the users' browsers. The difference is that updating a UI Script doesn't trigger this. Not even a cache.do triggers it. It simply forces the server to update cache. Installing/updating plugins seems to be the sole trigger that forces the browser to ask the server for updated cache for UI Scripts.

So how do you force this cache update?

The check for building new cache in this instance is controlled by a cookie on the client browser that ServiceNow creates. The cookie is controlled by a property in ServiceNow called glide.lastupdate. To force this update to happen, all you have to do is set that property to a newer dateTime than it currently is (e.g. the current timestamp). You can do this manually, but that can also be a form of kicking the can down the road. You will be back here soon. At Walmart, we wrote a Business Rule to update this whenever a ui_script is updated:

(function executeRule(current, previous /*null when async*/) {
	
	var gdt = new Date();
	var gdtString = gdt.toString().replace(/ /g,'_').replace(/:/g,'_');
	var timeZone = gdtString.replace(/.*[(](.*)[)].*/,'$1');
	var dateFinal = gdtString.slice(0,25) + timeZone;
	gs.addInfoMessage('System Property glide.lastplugin updated. ' + 
					  'Please be sure to push this updated property with the rest of your code. ' +
					  'This will ensure that the JavaScript cache is updated on the client browser.');
	gs.setProperty('glide.lastplugin', dateFinal);
	
})(current, previous);

I have attached an export of this business rule for any who are interested. The info message reminds developers to ensure this updated property is included in their update set, since business rules are not triggered on update commit.

Comments
ARG645
Tera Guru

Very Helpful! 

There can be two possible caches

1. Browser Cache

2. ServiceNow instance Cache. 

Your article is referring to Browser cache, and how to flush this cache by setting the system property glide.lastupdate which is very helpful.

But your last code snipped may cause a possible impact, executing gs.setProperty on any system property will flush the Instance Cache(Not the browser cache). That may have performance Impact. Instead using a GlideRecord Operation on sys_properties table may be a safe proof approach.

 

References: 

Cache flushing performance impact in ServiceNow

Use gs.setProperty with EXTREME caution. It causes cache flushes which can lead to performance issue...

Jeremy Box
Tera Contributor

Aman,

Thanks for the tip! I will review this and make adjustments. It is important to note that the types of activities that would trigger this business rule (creating/updating a ui_script or installing an update set that contains a ui_script) would typically trigger a system cache flush anyway. I will experiment with your suggestion as I agree it might prevent an additional flush.

 

Thanks,

Jeremy

mholmes
Tera Expert

I was kicked in the teeth by this problem today, and so was excited to read about your solution.

Unfortunately, it doesn't seem to work in the Service Portal on Kingston Patch 9 for a UI Script included via a "theme." I implemented the Business Rule, but when I checked the page source in my Service Portal, I found this, where the value of "lp" is the value of the "glide.lastplugin" property and "CatalogItemUtils" is my UI Script.

<script type="text/javascript"
        src="/scripts/js_includes_sp.jsx?v=12-04-2018_0932&amp;lp=Wed_Jan_30_2019_10_06_41_PST&amp;c=9_128">
</script>
<script language="javascript" src="CatalogItemUtils.jsdbx">
</script>

Apparently, this is a Known Issue, fixed in London. Interestingly, the Known Issue alludes to using "cache.do" as a workaround, but everything else I've read about cache.do implies that it has no impact on the browser cache, so I'm not sure what to make of that.

mholmes
Tera Expert

For reference, in case anyone else was curious, this seems to be how it works in the main UI:

<script type="text/javascript" src="/scripts/js_includes_customer.jsx?v=12-04-2018_0932&amp;lp=Wed_Jan_30_2019_10_06_41_PST&amp;c=9_128"></script>

One's UI Script is "baked in" to js_includes_customer.jsx, and the "lastplugin" property value is tacked on to the end of that URL.

Jeremy Box
Tera Contributor

Just now saw this, sorry. I haven't been on communities for a while. We are on Kingston patch 10 hotfix 1 and the workaround still works for us. However, I did notice 1 bug with the workaround. I had previously added a condition global=true to the business rule, which I later found to cause it to not fire on some ui script updates where it was needed. Another important point is that ui scripts installed by update sets won't trigger the business rule. Its important to capture the change to the property in an update set and move it also, or update the ui script once imported to trigger the rule.

mholmes
Tera Expert

The problem in my case is not so much that the property isn't updating, but that the Service Portal code doesn't apply the property to script URLs for UI Scripts included via themes.

n_boswell
Tera Contributor

Has this changed in Madrid? None of my UI Script changes are included in js_includes_customer.jsx any longer.

mholmes
Tera Expert

Interesting. I just checked my Madrid instance, and saw the same thing. My UI Script ("CatalogItemUtils") has been split off into its own script tag.

 

<script type="text/javascript" src="/scripts/js_includes_sp_libs.jsx?v=09-06-2019_0925&amp;lp=Fri_Sep_13_08_23_37_PDT_2019&amp;c=8_95"></script>
<script type="text/javascript" src="/scripts/js_includes_sp.jsx?v=09-06-2019_0925&amp;lp=Fri_Sep_13_08_23_37_PDT_2019&amp;c=8_95"></script>
<script language="javascript" src="CatalogItemUtils.jsdbx?c=39"></script>

 

However, there is a "c=39" parameter on the end of the src attribute.

"39" is the value in the "Updates" [sys_mod_count] attribute of my UI Script [sys_ui_script] record, so it looks like this is an elegant way to force the cache to refresh whenever the individual UI Script is updated.

n_boswell
Tera Contributor

There are 2 new Client Scripts on the UI Script table which set the "Global" field to false and keep the Global field hidden.  Essentially trying to prevent us from using the UI Script the way we were using it.

Jeremy Box
Tera Contributor

I've heard rumors that the work around is no longer needed post Madrid. I haven't had the opportunity to test this myself.

The SN Nerd
Mega Sage
Mega Sage

As long as you do a cache.do it seems to update the UI Script.

Jeremy Box
Tera Contributor
 

For anyone who happens across this post. I have confirmed that this method is no longer if your instance is on Madrid or later.

The SN Nerd
Mega Sage
Mega Sage

The only way i could solve this problem definitively is by disabling script caching in the browser entirely and having the browsers developer tools open.

For example, in Chome DevTools preferences:

 find_real_file.png

midjoule
Tera Guru

I confirm this worked for me. Thank you Paul.

catwood
Tera Contributor

I renamed the script, updated, and then put it back to the original name. Issue resolved (Utah). Appreciate the article!

Version history
Last update:
‎05-10-2018 11:09 AM
Updated by: