A 70,000-row ticket overview that takes twelve seconds
An internal service desk app has a ticket overview page. Its list view uses a microflow data source that retrieves all Tickets (about 70,000), sorts them with a List Operation activity, and returns the list; the page takes 12 seconds in acceptance. The list view shows 20 items per page and has a search box bound to a string attribute on a non-persistent helper object; an on-change microflow filters the already-retrieved list in memory. Tickets have a calculated attribute DaysOpen (expression) and users want to sort on it. Users on a slow VPN also want the search to filter while they type instead of after pressing Enter.
1. Why is the page slow, and how would you change the data source and search so that only a handful of rows leave the database?
Read the worked answer
Diagnosis The list view is not doing the paging; the microflow is. All 70,000 objects are retrieved, transferred to the client as a list, sorted in the runtime, and only then is the first page of 20 rendered. Sorting with a List Operation after retrieval is the proof that the sorting is happening in memory.
Target design
- Switch the list view to a database data source with an XPath constraint, so sorting and paging happen in the database and the client receives one page.
- A database data source cannot read a page attribute into XPath, so for the search path use a microflow data source whose Retrieve uses XPath with a variable, e.g.
contains(Ticket/Subject, $Search)orcontains(Ticket/Description, $Search), with a range limit (first 500) and an explicit 'refine your search' message when the cap is hit. Passing the term as a variable also avoids the classic bug of concatenating it into the XPath, which breaks on apostrophes. - Remove the in-memory List Operation sort entirely; set sorting on the list view or in the retrieve.
Result Both paths return a bounded set, so the 12 seconds becomes a normal indexed query plus render time.
2. What do you tell the users who want to sort on DaysOpen, and what are the options?
Read the worked answer
The constraint DaysOpen is a calculated attribute. It is evaluated per object in memory, so the database cannot sort or filter on it, and an XPath constraint that references it will not work. Sorting on it therefore requires either a small result set that you can sort in the modeler, or a persisted column.
Options, in order of preference
- Persist it: add a stored DaysOpen integer maintained by a scheduled event (nightly) or on the events that change the relevant date. Then it can be sorted and filtered in the database and indexed. The cost is staleness of up to a day, which you must state explicitly.
- Compute it on retrieval only when the filtered set is small: filter first with the search, then sort the returned bounded list in a microflow. This keeps correctness but breaks as soon as the set grows.
- Replace the need: sort by the underlying 'opened on' date, which is persisted and indexed, and derive DaysOpen for display. Often the business question ('oldest tickets first') is answered by this.
Say plainly which option you picked and what its staleness or scale limit is; users accept a limit they can see, not an unexplained 12-second page.
3. How do you implement search-as-you-type without hammering the server, and what would you check if the search still feels slow?
Read the worked answer
Client-side behaviour
- Use the on-change event of the search field with a delay of roughly 300-500 ms so a fast typist triggers one call, not one per character.
- The call must be a microflow (or a nanoflow if the data is client-side); it retrieves the bounded, filtered list and returns it to the list view.
- Show a busy indicator, and debounce guards against an out-of-order response overwriting a newer one (keep the last query text and compare before applying).
- If the ticket list is genuinely small and already client-side, filter in a nanoflow to avoid a server round trip entirely; that option disappears once the set is large.
If it still feels slow
- Time the retrieve separately from the render; a slow retrieve points at missing indexes on the searched attributes or a leading wildcard
containsthat cannot use an index. - Check the XPath: searching two attributes with OR usually defeats indexes; consider a single concatenated search attribute maintained on save.
- Measure over the VPN before optimising further; the difference between 400 ms of server time and 4 seconds of rendering tells you where the next fix belongs.