Why does a server call from the client take longer than the server code takes to run?

I have this problem too.
The server code only takes ~200ms to run, yet on the client side it took ~2000ms to return. Very frustrating!
How exactly can I solve this? Do you guys have some example code?

Hi, @shanhewang, and welcome to the Anvil Forum!

It depends on what your code is actually doing. Each program creates its own distinct patterns of activity. Some steps can be slower than others, especially if they’re repeated many times.

And it’s not just database activity that can slow things down. For example, my own server-side code was needlessly importing some very large files. Now, I postpone importing them until absolutely necessary. That reduced server-side startup time on every server call.

You might want to Search this forum for similar topics, and see if they describe patterns found in your own code.

2 Likes

Some tips for improving performance are collected here:

My understanding is that a round trip from client to server and back just inherently takes some time. Aside from that, it may help to understand more about what happens when you run a server call: see the “Move slow server imports inside the callable function” section of the Wiki post linked above. When you time the server code, are you including the time it takes for the imports?

[p.s. I moved your question to a separate post, since the cause is likely different from the one discussed there.]

3 Likes

Thank you all for providing tips.

I timed all the imports on the server side it took only single-digit ms.
This is the server code that I needed to run, it take about 30ms.
Also, I found that between the end of init and form_show, there is a huge time gap. Not sure why this is happening. I tried to move things around but I’m getting nowhere.

Here is the console printout for the time usage:

MainApp import time: 367
check_signed_in_status time: 26
MainApp check sign in time: 1773
Start of formshow: 3009
MainApp ShowForm time: 3013

Server-side code:

@anvil.server.callable()
def check_signed_in_status(require_user=True):
    start = time.time()
    
    user = anvil.users.get_user()
    if user is None:
        print("User is not signed in.")
        print("check_signed_in_status time:", int((time.time() - start)*1000))
        return None, None
    
    latest_app_version = app_tables.appconfig.get(key='app_version')['value']

    print(f"User [{user['email']}] is signed in")
    if user['user_id'] is None:
        
        print(f"Setup new user [{user['email']}]")
        setup_new_user_info(user)
        setup_new_user_shipping_address(user)
    
    print("check_signed_in_status time:", int((time.time() - start)*1000))
    
    return (UserAccountInfoViewModel(**dict(user)).dict(), latest_app_version)

Code from the client-side:

import time
import_start = time.time()

from ._anvil_designer import MainAppTemplate
from anvil import open_form
import anvil.server
import anvil.users
from anvil_extras import routing

#from ..MultiLanguages import MultiLangTranslation
#from Translations import Translations

#from .NewPackageForm import NewPackageForm
#from .GroupStatus import GroupStatus
#from .MyPackagesForm import MyPackagesForm
#from .MyOrdersForm import MyOrdersForm
from .UserAccountForm import UserAccountForm

from .side_menu_utils import setup_sidelinks
import app_config

print("MainApp import time:", int((time.time()-import_start)*1000))

@routing.main_router
class MainApp(MainAppTemplate):
    def __init__(self, **properties):
        global start
        start = time.time()
        self.init_components(**properties)
        try:
            self.account_info, self.latest_app_version = anvil.server.call_s('check_signed_in_status')
        except anvil.users.AuthenticationFailed as e:
            self.account_info = None
        
        print("MainApp check sign in time:", int((time.time()-start)*1000))

    def form_show(self, **event_args):
        print("Start of formshow:", int((time.time()-start)*1000))
            
        if self.account_info is None:
            self.clear_all_slots()
            self.content_panel.clear()
            self.sign_out_process()
            routing.clear_cache()
            open_form('SignIn')
        else:
            self.app_version.text = f"ver. {app_config.app_version}"
            app_config.check_app_version(self.latest_app_version)
            self.sign_in_process()
            
        print("MainApp ShowForm time:", int((time.time()-start)*1000))
1 Like

I think that that’s when Anvil renders the components to HTML.

Perhaps you have too many components or you have some components that are slow to render.

Perhaps one of your components has something in the databinding that triggers roundtrips.

Can you try to remove repeating panels, databindings, anything that may cause the rendering to HTML to be slow?

1 Like

There’s definitely a lot not being shown that could affect things. What happens in self.sign_in_process or self.sign_out_process?

What happens inside any repeating panel rows? There are often hidden server roundtrips in those, as Stefano says.

You also seem to be using Anvil Extras routing, but then are using open_form to get replace your main routing form if they aren’t signed in. While unlikely to contribute to the time delay, it’s a red flag.

1 Like

Thank you for pointing that out. Indeed, the component inside the MainApp form at the home page route is taking up that chunk of time between init and form_show().

1 Like
self.sign_in_process
self.sign_out_process

don’t have any related to data, they are just for showing components and clearing components. They have no impact on the performance.

Why using open_form to replace my main form is a red flag? I would love to know more. I used open_form because I want to remove the side panel and the side panel hamburger button when a user signed out. I could not find a way to remove them when a user signed in and add them back when the user sign back in. open_form is a hack but it sounds like a bad idea.

I forgot to mention. The server call took a long time to get the data back, like >1200ms, while the server code actually took ~30ms. Is this normal?

My guess is that some roundtrips are triggered after __init__ and before form_show, while working on some databinding.

Can you try to dig in all the components with databinding and print their values and see how long it takes for that?

(do you have any data binding?)

Those were just examples. Beyond the code that you’re showing us, there’s a lot that happens between your timings. You need to consider all of it. Stefano’s suggested data bindings and code in row templates, those are both excellent places to look for hidden server roundtrips.

I don’t think I have any data binding.
But to further ensure, I commented out the routing so the main form only loads a bare bone, only calls that check_user_sign_in server call, but it still took ~1700ms.

That’s the only call that you know of.
There may be other calls, for example because you are working with data table row or search iterator objects.

For example, if you do self.label1.text = user['email'] and user is a row of a data table, you could trigger a roundtrip to fetch whatever data needs to be fetched.

This is an example. In this specific case it wouldn’t, trigger a round trip.

Sneaky round trips are not triggered when reading a text column like email, all text values come with the row object, but are usually triggered when a row needs to access a simple object column, a media column, a linked table column, etc. or when consuming a long search iterator.

Without seeing your app I shooting blindfolded.

1 Like

Here is my app,

OOPS!

1 Like

A quick reminder: when you clone an app you also clone all the database tables.
If you have something sensitive, you should remove this clone link.


I don’t have your secrets, so I commented some lines out to try to run it.

After commenting these lines out, so the app starts:

image

I get these times:

image

This seems faster than yours… I’m in Missouri - USA, not so close to UK.

I’m in the SF Bay Area. So is the long server call time likely related to network delay?

It’s possible the commented out lines are contributing, too. You might time those.