Stepping Through 1k+ records smoothly ?

I didn’t read the full thread (sort of scanned it), but just looking at your log numbers the following stands out:

1- 47 milliseconds for a single query is a HUGE number.

In most cases (at least with MySQL), indexing will bring that number down to well below 10ms. None of your queries are all that special. They should be going much faster.

2- The number of queries you have going out is fine for a record view but will bring list/table view to it’s knees.

Keep in mind that Servoy doesn’t issue all these queries at the same time – they happen sequentially. 26 queries is a lot of roundtrips for a single action. When you click a record, calcs for each visible row going up and down the list get fired (there’s more to it than that but it’s a good rule of thumb).

For this reason, dynamic (as opposed to “static calcs” – which you update on data entry only) calculations should be used very sparingly in lists.

There’s a bunch of tricks you can do to get a handle on the things. Next post I’ll copy in the notes from a VUG presentation we did a while back on speed.

Servoy and Speed, ca July 2008

INTRODUCTION
	- how to optimize for speed is obvious once you know what you are looking for
	- nice to keep a list around handy
	- speed isn't something you really think about until you get off the LAN
		- when you go to the WAN from the LAN, some recoding may be necessary

TESTING FOR SPEED
	- global timer method wrapper
	- admin performance tab

FACTORS THAT AFFECT SPEED
	- distance to server
		- latency
	- number of queries
		- does servoy execute all at once per form or sequentially?
		- what fires queries?
			- aggregated and related data
	- bandwidth to server
		- effects initial form caching particularly
		- also worth noting when dealing with large blobs
	- amount of data
		- data density on a screen
	- cache
		- understand how servoy "pages" local data
		- understanding how servoy caches solution objects
	- html_area field rendering
	- how many objects you have on a form

SPECIFIC PROBLEM AREAS
	- related lists
	- counts and aggregate data
	- calcs
		- referring to data in other tables
	- first form load
	- first record load
	- data density
	- html_area abuse
	- worst case scenario: all of the above in a list

WHAT YOU CAN DO TO IMPROVE SPEED
	- index database
	- form pre-load routine
		- how client heap affects form caching
	- use one complex query to bring back all the data in one shot
		- displaying takes extra work
			- html or record globals
	- use views to store "flattened" data sets for viewing
		- views calc at the server
		- can use servoy's list and table views to display
	- use holding tables to store "flattened" data sets for viewing
		- holding tables you can manage either from the client or batch processor at the server
			- hsqldb makes for a great data source -- won't clutter up your main data source
		- great if near real-time data views
		- can use servoy's list and table views to display
	- store data at the client
		- great for solution state data
		- serialized (JSON) objects vs data in rows for data storage
		- if relatively static, create at the server and push down to client as needed
	- increment calcs and summaries on data change
		- in effect, remove all calcs and summaries from your solution
	- give user feedback

david:
Keep in mind that Servoy doesn’t issue all these queries at the same time – they happen sequentially. 26 queries is a lot of roundtrips for a single action. When you click a record, calcs for each visible row going up and down the list get fired (there’s more to it than that but it’s a good rule of thumb).

david! don’t lie! ;)

related queries are bundled and now (stupid stupid me) i also know why there are exactly 10 related queries done…

thats the bundling of related queries that servoy does.

What happens is if you access some related data and that relation isnt loaded yet.
then we look at the siblings of that parent record and see if we can also can do that exact same related query for them at once.
We do that in max of 10 (the output of 10 relations is explained here)

so if you then go to a next record then that relation is already loaded by the previous record.
This way if you look at a table view where you show related data and those relations are unique for every record you display in the tableview
you will see that the relations are lazy filled in by blocks of 10.

david:
I didn’t read the full thread (sort of scanned it), but just looking at your log numbers the following stands out:

1- 47 milliseconds for a single query is a HUGE number.

In most cases (at least with MySQL), indexing will bring that number down to well below 10ms. None of your queries are all that special. They should be going much faster.

2- The number of queries you have going out is fine for a record view but will bring list/table view to it’s knees.

Keep in mind that Servoy doesn’t issue all these queries at the same time – they happen sequentially. 26 queries is a lot of roundtrips for a single action. When you click a record, calcs for each visible row going up and down the list get fired (there’s more to it than that but it’s a good rule of thumb).

For this reason, dynamic (as opposed to “static calcs” – which you update on data entry only) calculations should be used very sparingly in lists.

There’s a bunch of tricks you can do to get a handle on the things. Next post I’ll copy in the notes from a VUG presentation we did a while back on speed.

Thanks for the feedback David - all good stuff and that pres was very helpful.

Right now it seems that the update query for the dynamic calcs are actually taking zero time anyway - so it seems that is not the issue here.

Secondly, though you are right about the long query process time I’m really concerned that even if you collectively take all the running queries they don’t add up to a 3 second lag in record selection in the UI so I suspect there is something else awry!

Doubtless Johan will prove me to be a complete ‘plonker’ - which will at least be a step forward LOL!

I’ll take your speed tips on board too. We’re actually at a stage where we need to prove the structure of what we have right now and then fine tune it where necessary (which is why this came up this week).

Cheers

Kahuna:
Right now it seems that the update query for the dynamic calcs are actually taking zero time anyway - so it seems that is not the issue here.

Yes, there are calcs that don’t impact speed much. Rule of thumb: calcs that refer to a data source will generate a query resulting in at least one round trip to the server per calc row.

Kahuna:
Secondly, though you are right about the long query process time I’m really concerned that even if you collectively take all the running queries they don’t add up to a 3 second lag in record selection in the UI so I suspect there is something else awry!

The performance tab shows times only for the sql queries. Then you need to take into account distance and bandwidth traveling to the client, and the time it takes for the client to generate a response.

If one click on your UI is causing close to a second of SQL query times on the performance tab, three seconds at the client is the minimum I would expect. I would guess that you are on the server’s LAN and using a speedy client machine.

The basic performance facts are:

1- 26 queries for a single UI click is very high (for general usage anyway, for a report or something not so bad).

2- 47 ms query times is super high. We optimize ours to 5ms or less.

You have to bring those numbers down. That’s the deal, nothing awry going on here :)

One more thing I should highlight from my VUG notes:

HTML area fields can kill speed in a hurry. The html renderer Servoy/Java is using is about 50 years old and sucks beyond belief.

So if your table has a bunch of html area columns that will cause the UI to be “sticky”. Especially if it is html generated from a dynamic calc.

Lastly, the more data is in an html area field, the slower it takes to render. Around 5 to 10k characters (varies according to client processor speed) equals a second delay. This is why when you do an html area report, print preview can grind away forever on large data sets. Servoy isn’t doing anything – it’s the html area field with its three hamsters taking its time.

jcompagner:

david:
Keep in mind that Servoy doesn’t issue all these queries at the same time – they happen sequentially. 26 queries is a lot of roundtrips for a single action. When you click a record, calcs for each visible row going up and down the list get fired (there’s more to it than that but it’s a good rule of thumb).

david! don’t lie! ;)

related queries are bundled and now (stupid stupid me) i also know why there are exactly 10 related queries done…

thats the bundling of related queries that servoy does.

What happens is if you access some related data and that relation isnt loaded yet.
then we look at the siblings of that parent record and see if we can also can do that exact same related query for them at once.
We do that in max of 10 (the output of 10 relations is explained here)

so if you then go to a next record then that relation is already loaded by the previous record.
This way if you look at a table view where you show related data and those relations are unique for every record you display in the tableview
you will see that the relations are lazy filled in by blocks of 10.

“Lazy loading” was exactly the “lot more going on” I was referring to. Glad you explained the details, I wasn’t about to tackle that one :)

I believe this is the only place where queries are bundled though, is this correct?

david:
I believe this is the only place where queries are bundled though, is this correct?

yes the only thing that is bundles are related queries of siblings of the same relation (but with other keys)
But if you use servoy with many relations (instead of loads of custom queries) then this is the bulk load of a normal servoy app.

jcompagner:

david:
I believe this is the only place where queries are bundled though, is this correct?

yes the only thing that is bundles are related queries of siblings of the same relation (but with other keys)
But if you use servoy with many relations (instead of loads of custom queries) then this is the bulk load of a normal servoy app.

I agree up to a certain point. For performance metrics, you essentially treat a “bundled” related query as a single query. But it has a downside: you don’t control when that query fires. And this leads to what we call “uncontrollable query explosion”. :)

As data density on a screen increases, it becomes more and more important to have better control of how and when data is pulled in. An example: a screen showing a calendar month view with 31 related tab panels showing day events. That’s at least 31 bundled queries. Not exactly fast to begin with. Throw in a few html display type calculations on the events table. Now you just tagged on 31 times number of calculations to the query list. We’ve had to rewrite screens for clients that were generating 100’s of queries due to this kind of uncontrollable query explosion. A month view we can get down to less than 10 queries. With Marcel’s calendar bean, it’s one query.

The downside is that it takes considerable experience to know how to write optimized screens (and a large code library). Usually the first time a Servoy developer has to start thinking about this issue is when a seeming simple looking table view grinds to a halt. For an excellent example, see this thread :)

david:
Usually the first time a Servoy developer has to start thinking about this issue is when a seeming simple looking table view grinds to a halt. For an excellent example, see this thread :)

David many thanks for your input on this issue - I’m beginning to get the ‘jist’ of your point - and I can see we have some challenges.

It seems the way Servoy handles these data interactions is very different to other development environments. FYI we do not have any HTML fields on any of the data display forms or in the tables feeding them, but what we do have is a lot of data being displayed in nested tab forms. In other environments we have not seen this speed issue - though perhaps because we would not have had so much data being processed at one time - e.g. the forms would be shown as required rather than being loaded all at once as is the case with nested tabs.

I discovered two things - first if the records have been touched (selected) in the table - regardless of their position in the table then they can be quickly re-selected (guess the data is then in memory), so perhaps some method to quickly step through all of the records in code before display in the table could speed the apparent UI selection.

Secondly - if I use our preferred filtering tool, which is the TableFilterParam, the the entire table becomes slow - not just the >200 set. I assume because none of the filtered records were in the initial <200 set! Again - perhaps a code step through the filtered set might speed the apparent UI selection?

Three questions David:

  1. Was your presentation recorded - I’d love to look more deeply into your comments.
  2. Apart from the obvious indexing aspects - in this instance (I know you only have the barest of info on the solution) where would you suggest we begin to address the speed issue?
  3. Its a hack I know, but would my thoughts on initialising the data with a code ‘step-thru’ help alleviate the situation without major restructuring of the way the forms are nested, and is it appropriate?

Appreciate your experienced feedback David

Cheers

Ian

Kahuna:

david:
Secondly - if I use our preferred filtering tool, which is the TableFilterParam, the the entire table becomes slow - not just the >200 set. I assume because none of the filtered records were in the initial <200 set! Again - perhaps a code step through the filtered set might speed the apparent UI selection?

if that is the caes then you have to set indexes or something on your tables on the columns the tablefilter params works on.
Because tablefilter params are for servoy nothing more then a column = value in the sql. If that is slow then you really should look at your db first.

jcompagner:

Kahuna:

david:
Secondly - if I use our preferred filtering tool, which is the TableFilterParam, the the entire table becomes slow - not just the >200 set. I assume because none of the filtered records were in the initial <200 set! Again - perhaps a code step through the filtered set might speed the apparent UI selection?

if that is the caes then you have to set indexes or something on your tables on the columns the tablefilter params works on.
Because tablefilter params are for servoy nothing more then a column = value in the sql. If that is slow then you really should look at your db first.

Thanks Johan - we are looking into this at the moment - but I’d really appreciate your feedback on the solution I sent - as after reading David’s comments I’m very concerned that we have taken a dead end track using nested tab forms, based on the amount of data Servoy is loading?

Could you comment on the idea of stepping through the table rows in code to pre-load the records (perhaps if that is done on the server then there would be no benefit from a UI angle?).

Cheers

Ian

Kahuna:
Thanks Johan - we are looking into this at the moment - but I’d really appreciate your feedback on the solution I sent - as after reading David’s comments I’m very concerned that we have taken a dead end track using nested tab forms, based on the amount of data Servoy is loading?

Could you comment on the idea of stepping through the table rows in code to pre-load the records (perhaps if that is done on the server then there would be no benefit from a UI angle?).

if you have 1 tableview where you step through and below that a tabpanel where you display 1 relation
that shouldnt really be a problem at all. That we query even in bunches of 10 so you only would have a small hick up (if they related query is fast) for the next 10

But if you have many visible tabs besides it or tabs in tabs in tabs so that relations also need to be filled in deeper into the hierarchy then yes this could slow up stuff.

Show only the really needed data. and if users wants to dig in some more then show the rest.

jcompagner:
But if you have many visible tabs besides it or tabs in tabs in tabs so that relations also need to be filled in deeper into the hierarchy then yes this could slow up stuff.

Show only the really needed data. and if users wants to dig in some more then show the rest.

I have only 1 tab of detail visible Johan - but obviously there are other tabs and nested tabs which are not currently visible - though being nested they are available to be shown with a button click.

I guess my real question with this feedback Johan - is - if these tabs are not yet visible in the ui is the data still loaded and is this the reason for slow table selection?

Did you have a chance to look at the sample data / solution?

Cheers

Ian

Hi Ian,
I’ve been watching this very busy thread and thought I’d better chip in.
I have a customer that has a very large data set that they look at in a table view, then click a button to go to a detail view with lots of tabs bringing in related data that takes a while to load up.
At one point to try and speed things up I added this code in the startup/open method

// Turnoff the initial form foundset record loading
// this has to be called in the solution open method
databaseManager.setCreateEmptyFormFoundsets() ;

This sort of stops the tabs from loading up initially, which you can then do a manual load if/when they really need to see that data.
I stopped using it as it had a negative impact in other areas where I really needed the stuff to load on first view automatically, but you could give it a go.
Also, as David said in relation to the tablefilterparam, you must also make sure that you index all foreign keys for any of your relationships used in the tabs so that the back-end can get them faster.

Maybe you also could reconsider what database you are using for the back-end if you haven’t got any stored procedures etc. sitting in it, or any specific need for it? Some SQL dbs are better than others :)

Hope this might help. Don’t give up, there is always a solution.

Rafi

[Update]
Ian just saw your post come in in between.

I think the other tabs do try and load, which is what I was trying to fix with code above.

Rafi

Kahuna:
I guess my real question with this feedback Johan - is - if these tabs are not yet visible in the ui is the data still loaded and is this the reason for slow table selection?

no not visible tabs shouldnt load anything.
only visible stuff should do something.
You can test this pretty easy. if you go to a record you never has shown
then click on a tab that was not visible
is something loaded? (see admin performance tab)

what rafi is saying. if you first hit the tab. then the form doesnt know at creation time that it is used in a tab and it will get a relation
so it first tries to load the default shared foundset… With rafi’s tip this doenst happen. It is initialized empty and then the tab will get the relation.

Kahuna:
Did you have a chance to look at the sample data / solution?

not yet.

rafig:
[Update]
Ian just saw your post come in in between.

I think the other tabs do try and load, which is what I was trying to fix with code above.

Rafi

Thanks for that feedback Rafi. Still digging!

Cheers

jcompagner:
no not visible tabs shouldnt load anything.
only visible stuff should do something.
You can test this pretty easy. if you go to a record you never has shown
then click on a tab that was not visible
is something loaded? (see admin performance tab)

Thats pretty weird then Johan - the biggest query that you see in the perfornmance data (the one that ran 47ms) is actually a query for one of the non-visible tab-forms and does not show any data on the first, initially, visible form? Why would it be firing on record selection in the table?

BTW With some judicious indexing the record selection time is much reduced, but is still in the order of 1 full second. This still appears slow in terms of UI when just moving from one record to an adjacent record? And from the performance data now it appears its only that initial query we discussed (runs 10 times) that has any time left against it at all 62ms in the case of the 10 instances?

Either this 10 instances of the query or some other aspect of the UI is generating this latency in the UI update of the selected record?

Any further suggestions where I might target to test?

Kahuna:
I discovered two things - first if the records have been touched (selected) in the table - regardless of their position in the table then they can be quickly re-selected (guess the data is then in memory), so perhaps some method to quickly step through all of the records in code before display in the table could speed the apparent UI selection.

This is correct. Servoy “lazy loads” stuff as needed. This includes forms (first time you hit a form it is loaded into memory) and records (first time you hit a record all of it’s related data is loaded). Related data is loaded if a related field is showing, you have a calculation referencing a relation, and/or if you have code referencing a relation on a form event that fires when the record is selected (onRecordSelection being the most obvious one).

There are ways to preload both forms and data. Form preloading is fairly well discussed around the forum and if I remember correctly, Scott’s site has an article on it.

Keep in mind Servoy “garbage collects” (dumps out of memory) unused objects and data. You have very little control when this process occurs. You can only increase the client heap size in the admin application. At the default “servoy.maxClientHeap=64mb” there is a hard cap of how many forms Servoy keeps in memory (~30?). At “servoy.maxClientHeap=128mb” the hard cap is around 75 forms (I’m going on 2+ year old memory here so Johann please correct!). Increasing the servoy.maxClientHeap further, Servoy holds as many forms and layout objects in memory as the size allows.

Data that is shown in the client is also somewhat affected by the servoy.maxClientHeap. But there is no hard rules for how long it stays that we’ve figured out. You can have the servoy.maxClientHeap set very high – do nothing on the client for 30 seconds – and some of the records showing will still reload when you next click.

Kahuna:
Secondly - if I use our preferred filtering tool, which is the TableFilterParam, the the entire table becomes slow - not just the >200 set. I assume because none of the filtered records were in the initial <200 set! Again - perhaps a code step through the filtered set might speed the apparent UI selection?

Johann answered this one. Filtering just adds a WHERE clause to the queries – need to take that into account when indexing. Performance tab will show you what needs to be done.

Kahuna:
Was your presentation recorded - I’d love to look more deeply into your comments.

Sorry, no recording. But we could present it again if Servoy is up for putting it on their schedule and hosting it. Maybe use your solution as examples. Get a few other people to throw their thoughts and experiences in. Big round table discussion type deal.

Kahuna:
Apart from the obvious indexing aspects - in this instance (I know you only have the barest of info on the solution) where would you suggest we begin to address the speed issue?

1. Basic. The design pattern of your layouts. When and where you show various related data. Move dynamic calculations to “static calcs”. Move from showing data through relations to selectively controlling per event. Remove duplicate and unused relationships and calculations. Remove duplicate code firing on multiple form events (ie. onLoad, onShow…then finally onRecordSelection). Calculate all client state information only once for the session and only modify as it changes (i.e. we’ve seen the navigation scheme get recalculated for every form navigated to).

2. Advanced. Base complex layouts on views (put calcs in the view), custom queries (bring back complex related data in one shot), solution model (show what is needed on the fly based on the situation). Store complex data locally JSONed objects in the “.Servoy” directory (if the object is garbage collected, load it from the client machine instead of going back to the server). Go completely nuts like the adBlocks guys and do a pixel-perfect rendition of a page of data in html and page it all yourself.

3. In addition to the performance tab, speed test everything with a timer:

/*
 *	TITLE    :	CALLBACK_timer
 *			  	
 *	MODULE   :	rsrc_CODE_frameworks
 *			  	
 *	ABOUT    :	starts/stops timer for debugging speed issues
 *			  	
 *	INPUT    :	1- 'start'/'stop'
 *			  	
 *	OUTPUT   :	
 *			  	
 *	REQUIRES :	solutionPrefs
 *			  	
 *	USAGE    :	CALLBACK_timer(startStop) Starts/stops timer.  Displays value in status area when stopped
 *			  	
 *	MODIFIED :	July 21, 2008 -- Troy Elliott, Data Mosaic
 *			  	
 */

var startStop = arguments[0]

//check for solutionPrefs
if (!application.__parent__.solutionPrefs) {
	solutionPrefs = {config : {timer: new Object()}}
}
//check for config node
else if (!solutionPrefs.config) {
	solutionPrefs.config = {timer : new Object()}
}
//check for timer node
else if (!solutionPrefs.config.timer) {
	solutionPrefs.config.timer = new Object()
}


//start timing
if (startStop == 'start') {
	solutionPrefs.config.timer.timeStart = new Date().getTime()
}
//stop timing
else if (startStop == 'stop') {
	if (solutionPrefs.config.timer.timeStart) {
		var endTime = solutionPrefs.config.timer.timeEnd = new Date().getTime()
		
		var elapsed = solutionPrefs.config.timer.timeEnd - solutionPrefs.config.timer.timeStart
		
		//only set when demo mode not expired
		if (!solutionPrefs.config.demoModeExpired) {
			application.setStatusText('Elapsed time is: '+ elapsed +' ms.  Finished '+utils.dateFormat(endTime, 'H:MM:ss'))
		}
	}
	else {
		plugins.dialogs.showErrorDialog('Timer error','The timer has not been started yet')
	}
}

4. Give user feedback

A second for something to happen isn’t so bad if you give the user feedback. We do this in several ways: turn the cursor into the the busy cursor (I think I posted a basic plugin for this and recently Patrick put one out that even works in webclient), show a busy progress bar (bar just spins), and/or a progressing progress bar (if it is a very long process).

/*
 *	TITLE    :	CALLBACK_progressbar_start
 *			  	
 *	MODULE   :	rsrc_CODE_frameworks
 *			  	
 *	ABOUT    :	sets up the progressbar
 *			  	
 *	INPUT    :	1- initial value (usually 0); if null, bar will be indeterminate; if -273, use gif
 *			  	2- explanation text; if empty, text will not be available
 *			  	3- explanation toolTip
 *			  	4- minimum (defaults to 0)
 *			  	5- maximum (defaults to 100)
 *			  	
 *	OUTPUT   :	
 *			  	
 *	REQUIRES :	
 *			  	
 *	USAGE    :	CALLBACK_progressbar_start(startPosition,explanation,explanationTooltip,minValue,maxValue)
 *			  	
 *	MODIFIED :	June 26, 2008 -- Troy Elliott, Data Mosaic
 *			  	
 */

if (application.__parent__.solutionPrefs) {

	var formName = 'TOOL_progress_bar'
	var baseForm = solutionPrefs.config.formNameBase
	
	var initialValue = arguments[0]
	var explanationText = arguments[1]
	var explanationToolTip = arguments[2]
	var minimum = (typeof arguments[3] == 'number') ? arguments[3] : 0
	var maximum = (typeof arguments[4] == 'number') ? arguments[4] : 100
	
	//only run if progressbar not added
	if (forms[baseForm + '__header'].elements.tab_toolbar.getTabFormNameAt(forms[baseForm + '__header'].elements.tab_toolbar.getMaxTabIndex()) != formName) {

		//save down active toolbar to restore
		solutionPrefs.config.lastSelectedToolbar = forms[baseForm + '__header'].elements.tab_toolbar.tabIndex
		
		//load scrollbar tab
		forms[baseForm + '__header'].elements.tab_toolbar.addTab(forms[formName],'')
		forms[baseForm + '__header'].elements.tab_toolbar.tabIndex = forms[baseForm + '__header'].elements.tab_toolbar.getMaxTabIndex()
		
		//hide toolbar controls
		forms[baseForm + '__header'].elements.btn_toolbar_toggle.visible = false
		forms[baseForm + '__header'].elements.btn_toolbar_popdown.visible = false
	}
	//hide bean stuff to make sure the passed values are obeyed
	else {
		forms[formName].elements.lbl_progress_text.visible = false
		forms[formName].elements.bean_progress.indeterminate = false
		forms[formName].elements.bean_progress.value = 0
		forms[formName].elements.bean_progress.visible = false
		forms[formName].elements.gfx_progress.visible = false
	}
	
	//turn on progressbar elements
	forms[formName].elements.bean_progress.visible = true
	forms[formName].elements.lbl_progress_text.visible = (explanationText) ? true : false
	forms[formName].elements.gfx_progress.visible = false
	
	//indeterminate gif
	if (initialValue == -273) {
		forms[formName].elements.bean_progress.visible = false
		forms[formName].elements.gfx_progress.visible = true
		
	}
	//indeterminate scrollbar
	else if (initialValue == null && typeof initialValue == 'object') {
		forms[formName].elements.bean_progress.indeterminate = true
	}
	//normal scrollbar
	else {
		//initial value
		if (typeof initialValue == 'number') {
			forms[formName].elements.bean_progress.value = initialValue
		}
		else {
			forms[formName].elements.bean_progress.value = 0
		}
		
		//min/max
		forms[formName].elements.bean_progress.minimum = minimum
		forms[formName].elements.bean_progress.maximum = maximum
	}
	
	
	//set text
	if (explanationText) {
		forms[formName].elements.lbl_progress_text.text = explanationText
		forms[formName].elements.lbl_progress_text.toolTipText = explanationToolTip
	}
	
	//two updates (maybe more required)
	application.updateUI()
	application.updateUI()
	application.updateUI()
}
/*
 *	TITLE    :	CALLBACK_progressbar_stop
 *			  	
 *	MODULE   :	rsrc_CODE_frameworks
 *			  	
 *	ABOUT    :	reset toolbars and select last toolbar user was viewing
 *			  	
 *	INPUT    :	
 *			  	
 *	OUTPUT   :	
 *			  	
 *	REQUIRES :	
 *			  	
 *	USAGE    :	CALLBACK_progressbar_stop()
 *			  	
 *	MODIFIED :	June 26, 2008 -- Troy Elliott, Data Mosaic
 *			  	
 */


if (application.__parent__.solutionPrefs) {
	var baseForm = solutionPrefs.config.formNameBase
	var formName = 'TOOL_progress_bar'
	
	//remove progress toolbar if it is present
	if (forms[baseForm + '__header'].elements.tab_toolbar.getTabFormNameAt(forms[baseForm + '__header'].elements.tab_toolbar.getMaxTabIndex()) == formName) {
		forms[baseForm + '__header'].elements.tab_toolbar.removeTabAt(forms[baseForm + '__header'].elements.tab_toolbar.getMaxTabIndex())
		
		//set toolbar to previous if it wasn't the last tab
		if (solutionPrefs.config.lastSelectedToolbar != forms[baseForm + '__header'].elements.tab_toolbar.getMaxTabIndex()) {
			globals.TOOLBAR_cycle(solutionPrefs.config.lastSelectedToolbar)
		}
		
		//show toolbar controls
		forms[baseForm + '__header'].elements.btn_toolbar_toggle.visible = true
		//forms[baseForm + '__header'].elements.btn_toolbar_popdown.visible = false
	}
	
	//clear out lastSelectedToolbar
	solutionPrefs.config.lastSelectedToolbar = null
	
	//hide bean stuff to be ready for next time
	forms[formName].elements.lbl_progress_text.visible = false
	forms[formName].elements.bean_progress.indeterminate = false
	forms[formName].elements.bean_progress.value = 0
	forms[formName].elements.bean_progress.visible = false
	forms[formName].elements.gfx_progress.visible = false
}

I’ve attached a couple of pictures of the busy progress bar indicator flipping on when a fairly dense form is first loading. User feedback is so important we have our frameworks setup to be able to flip this on for each form in our navigation engine (“notifications” section, top right of screen shot). Additionally, the two callback functions above can be used in workflow code as needed.

Kahuna:
Its a hack I know, but would my thoughts on initialising the data with a code ‘step-thru’ help alleviate the situation without major restructuring of the way the forms are nested, and is it appropriate?

Unfortunately, Servoy’s default solution design approach easily creates query explosion situations when you start building data-dense/complex forms. The only easy and fast fixes are indexing, not showing calculations, and not showing data multiple relations deep. Squeezing more performance after this can be done but your first few attempts will be time consuming. But well worth the exploration.

/End brain dump 8)