Core Apps: Very long TTFB for patientDashboard.page when many encounters

Hi all,

In my quest to work around the loading issues of patientDashboard.gsp I realised that there were in fact two problems happening at the same time when loading that page for a visit with many encounters (say 100+). The first issue is discussed here and is addressed by RA-1244.

The other problem happens before anything else and I can’t quite put my finger on what is happening, look:

After that almost 12 sec waiting time, and only then, the first lines of the controllers are reached and other pieces of code (JS… etc). But what may be causing this initial loading time?

Could anyone give pointers as to which directions to look at?

Can you debug Spring’s DispatcherServlet ? Maybe you can see where the time is spent.

Thanks @lluismf. Something happens in between those two steps in the thread:

The webapp lands right away at the breakpoint in doService(HttpServletRequest, HttpServletResponse) and needs another 12 secs to reach doDispatch(HttpServletRequest, HttpServletResponse).

I’m not familiar with all this but I’ll try to understand what happens inside doService(..)

After debugging further I can see that everything is fine up to that point:

Any clues?

What’s the value of handlerMethodToInvoke ? It should be the controller’s method with bad performance.

But I doubt that VisitDetailsFragmentController is the culprit, because it receives Visit as a parameter and is populated before. What’s the controller that obtains the Visit from DB, is it PatientDashboardPageController ? Maybe you should implement paging there … but the visit persistent already contains a set of encounters so maybe it’s impossible.

Tip: setting show sql in the hibernate config file will log all the SQL queries performed by Hibernate.

I am almost certain that it is not VisitDetailsFragmentController either. When debugging while loading the problem page, I can see that I go twice to handlerMethodToInvoke and that the second time is not problematic (no delay) and corresponds to VisitDetailsFragmentController (I can see that visually from the state of the page).

So it “should” be PatientDashboardPageController, but in fact it actually takes 12 sec to get to the first line of this controller as well. I was initially wondering if the Spring injection of some of its parameters could be the issue?


Just restarted everything to answer your actual question, here is what I see just before the delay:

So: org.openmrs.module.uiframework.PageController@7ee3fc35 which could make sense with PatientDashboardPageController.

And the second time (so when there is no delay) it is: org.openmrs.module.uiframework.FragmentActionController@c1469e8 which is consistent with VisitDetailsFragmentController.

If this was the case then it should take 12 seconds regardless of the patient, right ?

Both PatientDashboardPageController and VisitDetailsFragmentController have no ancestor class, I ignore its relationship with PageController and FragmentActionController.

If I were you I will keep debugging, enter the invoked method until you detect the performance penalty.

By debugging further inside UI Framework (v 3.4 here) I could nail down to this exact instruction:

boundTemplate.toString()

That is what takes 12 sec, the Groovy rendering of the view for that page…

The only difference between two visits (one that loads normally and the one that takes 12 sec) would be how the model is filled right? So by default that will be my next line of investigation: observe the model differences between two visits that have very different loading times…

But of course if something obvious comes to the mind of anyone following this thread, please jump in!

Thanks so far @lluismf!

I would guess that somewhere in the gsp is a line that triggers visit.encounters to be lazy-loaded and this is what gets slow for visits with many encounters.

(It has to be something in the GSP itself, for the slowness to be coming from that line.)

Correct! And the culprit is this loop in visits.gsp:

visits.eachWithIndex { wrapper, idx ->
  def primaryDiagnoses = wrapper.getUniqueDiagnoses(true, false)

Not the loop itself of course but the repeated call to:

wrapper.getUniqueDiagnoses(true, false)

Before/while I look into it in more depth, does that ring a bell that this method may be suboptimal?

So this guy takes up to 10 sec to run for a visit with ~1,000 encounters (sorry @lluismf, I gave misleading numbers earlier but here we go):

@darius, what do you think, in the context of Core Apps’ visits.gsp and our need of a quick fix, should we look at optimising that specific method and open an EA ticket, or would there be an easier/faster way to patch just that page?

After looking real quick, it seems that the main source of inefficiency is the 1,000+ calls of

getDiagnosesFromEncounter(encounter)

Not sure if it’s a quick fix, but I would replace these nested loops by a single SQL query.

Other methods in this class have the same potential problem e.g. getDiagnosesFromMostRecentDispositionByType

Hmm, this is tricky to find a quick fix for.

One option is making it asynchronous. (This may still leave a bunch of server load.)

Better option is to write an explicit SQL or HQL query to get primary diagnoses by visit, as Lluis suggests.

-Darius (by phone)

@darius, @lluismf, could you point me to an example where something similar is done? I’m talking about the plumbing involved in creating a new API function whose implementations rests on a SQL or HQL query.

Check out

As well as the related service.

-Darius (by phone)

Thanks Darius.

And what’s your recommended approach to test (while developing) those queries? If possible on a real database. Or I guess my question is: how can I play around with this without launching the whole webapp?

@mksd if you are using IntelliJ - you can use the Hibernate Console.

  1. Checkout whatever branch of OpenMRS core you are using and setup the console against the hibernate-cfg.xml file.

  2. Create a Data source connection to your MySQL database running openmrs so that you can query it

  3. Follow the steps here https://www.jetbrains.com/help/idea/2016.2/hibernate-console-tool-window.html

2 Likes

Ah thanks @ssmusoke, exactly what I was looking for! I will try this out soon and report back.

@darius, @lluismf

Ok I almost have got an HQL query that returns the primary diagnoses, I have further questions:

Q1. Would it be ok to replace the implementation of VisitDomainWrapper.getPrimaryDiagnoses() then? Also, any idea why this method was not used in visits.gsp, is it because it may not return a list of unique diagnoses?

Q2. In your opinion, which DAO class and service should I hook my new DAO method to in EMR API?