Maik Skoddow
Tera Patron
Tera Patron

MaikSkoddow_0-1689183664788.png

 

Initial Situation

 

In my current project, we have different custom applications which are deployed via the application repository. During the last deployment session I was wondering about an extreme high number of artifacts for one of the applications because none of the features contained in it could justify this high number.

So I started to investigate the app and grouped by the class first to find the candidates that have an above-average number of artifacts.

 

MaikSkoddow_0-1689086502725.png

 

That listing was an unexpected surprise! Yes, we have custom tables with some fields in two languages. But this can never result in almost 1300 field labels! And all the artifacts that have anything to do with Flows don't make sense to me either. And since I absolutely hate it when I don't understand something, I started digging deeper. This article describes the interesting findings related to Flows from this effort and provides a solution on how to prevent your applications from being bloated with unwanted Flow artifacts.

 

 

Analysis

 

First of all, I wanted to understand where all the field labels are coming from. After opening the sys_documentation table, I could find tons of items with "strange" table names - starting with "var__m_"

 

MaikSkoddow_0-1689137125885.png

 

I'm still not sure whether these tables really exists or just act as identifiers. But what you can see in the above screenshot are the real Flow-related tables within the first column like sys_hub_flow_input or sys_hub_flow_variable

 

Therefore, the next step was taking a look at these tables, for example sys_hub_flow_variable:

 

MaikSkoddow_1-1689137470640.png

 

Interesting! Also in that table you can find the "strange" names within the "Table" column and furthermore it seems that many records are referring to the same Flow variable. Okay then let's open one of these records:

 

MaikSkoddow_2-1689137760598.png

 

Does this screenshot remind you of anything? Yes, it looks like the column definition of a table within the dictionary. But there is no reference to a table. So what is the purpose of such a record if there is no relation to any "container"? 

 

Next step is opening the XML view of that record

 

MaikSkoddow_3-1689138005190.png

 

Scrolling around, I came across some interesting fields starting with "model". Especially the "model_table" is of interest as this seems to be the container I was looking for. Going back to the list view with "model" columns enabled, the picture starts to become clearer

 

MaikSkoddow_4-1689138699923.png

 

The reference to the Flow table makes sense, as this is the place where the "test_date" variable comes from. But what about all the redundant records referencing to the "Flow Snapshot" table?

I do not want to go into too much detail at this point. In simple terms, a Flow snapshot represents a compiled version of a Flow. At the end, Flows are nothing else than precompiled Java programs, and that's the reason why they are so fast.

 

A search on ServiceNow's support portal reveals an interesting article: https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB1117831 

At the end of that article, you can find important information:

 

sys_hub_flow_snapshot only holds non-update-to-date flow information. That's why it's named 'snapshot' and the table has updateSync=false. 

sys_hub_flow holds the published flow information. It will be captured by Update Sets

 

This is quite irritating! So, if the records from table sys_hub_flow_snapshot are not captured in an update set, why does the table have a sys_scope column whereby its records are captured and transported in an application? And can we really deduce from the article's statement that only the records from the sys_hub_flow table are important, since they contain all the information of a Flow?

 

It's time for a small test!

 

 

Reproducing the scenario

 

I created a "Test" application and the following small Flow within it:

 

MaikSkoddow_5-1689140461635.png

 

After Flow activation, I opened the list of all artifacts for my test application:

 

MaikSkoddow_6-1689140591457.png

 

You can see that even with such a simple Flow many artifacts are created in the background and except the Flow itself they are all not captured in an update set (column "Update name" is empty). 

 

Now I was curious to see what the sys_update_xml record for the Flow contains.

 

MaikSkoddow_0-1689145490941.png

 

The above screenshot displays only a small part of the XML payload, which is rather huge. Most interesting are the records with pure data, like the static values of my flow variables. This proves that really everything about a Flow is stored in a single record at the sys_update_xml table. Conversely, this could also mean that all Flow artifacts without a dedicated sys_update_xml record could be removed from the app. 

 

However, instead of deleting the useless records, which is always a bad idea, I just moved them to the "Global" application. Then I published my "Test" application with the single remaining sys_hub_flow record to the application repo. After installing the "Test" app on a different instance, I opened the Flow and saw that everything looked fine. Nothing was missing. The reason for this is that ServiceNow could not find a snapshot of this Flow on the target instance and therefore compiled it instantly.

 

Now the question is how to prevent all the useless Flow-related artifacts from being captured in an app in the future.

 

 

Solution

 

In my article My collected list of useful Business Rules I included a Business Rule which "redirects" the creation of some artifact types to the "Global" scope. Based on that idea, I created a "before" Business Rules on table sys_metadata for update & insert operations which does the same for Flow-related artifacts and records of table sys_documentation which refer to any Flow-related artifacts. That Business Rule has no condition, as the complete logic is handled within the script:

 

 

(function executeRule(current) {

	var _strClassName  = current.getValue('sys_class_name') || '';
	var _strScope      = current.getValue('sys_scope') || '';
	var _strUpdateName = current.getValue('sys_update_name') || '';


	//do nothing if the artifact is already in Global scope or
	//if there is a related record in the sys_update_xml table
	if (_strScope === 'global' || _strUpdateName !== '') {
		return;
	}

	//do nothing in case the Flow itself is inserted/updated
	if (_strClassName === '' || _strClassName === 'sys_hub_flow') {
		return;
	}
	
	//filter out two special tables and some candidates from the Flow area
	if (_strClassName === 'sys_push_notif_app_install' ||
		_strClassName === 'sys_filter' ||
		_strClassName.startsWith('sys_hub_flow_') || 
		_strClassName.startsWith('sys_flow_'))
	{
		current.setValue('sys_scope', 'global');
		current.setValue('sys_package', 'global');
		return;
	}

	// filter out field labels which are related to Flows
	if (_strClassName === 'sys_documentation') {
		var _strName = current.getValue('name') || '';

		if (_strName.startsWith('var__m_sys_hub_flow_')) {
			current.setValue('sys_scope', 'global');
			current.setValue('sys_package', 'global');
		}
	}		

})(current);

 

 

 

This code is a first draft version, which already works like a charm. Now, only the Flow record from table sys_hub_flow is captured in an application. For all other Flow-related artifacts, the scope is set to "Global".

 

 

Comments
Renat Akhmedov
Tera Contributor

Wow, thank you, Maik, I found a lot of answers to my questions in this article. Thank you for all your hard work in the community!

Maik Skoddow
Tera Patron
Tera Patron

Thank you @Renat Akhmedov for the feedback!

Adam Warner
Tera Contributor

@Maik Skoddow This is great, thanks! Found this article via a google search after noticing the same thing in application file lists.

 

Just as a note, I needed to tweak the script slightly to grab the actual sys_documentation record as otherwise the name was just coming back empty.

 

	// filter out field labels which are related to Flows
	if (_strClassName === 'sys_documentation') {
		// get the sys_documentation record else the `name` field is not exposed properly
		var docGr = new GlideRecord('sys_documentation');
		if (docGr.get(current.sys_id)) {
			var _strName = docGr.getValue('name') || '';
			if (_strName.startsWith('var__m_sys_hub_flow_')) {
				current.setValue('sys_scope', 'global');
				current.setValue('sys_package', 'global');				
			}
		}
	}

 

Version history
Last update:
‎07-12-2023 10:41 AM
Updated by:
Contributors