Web 2.0 - when using HTTPS initial page load is 500msec slower

Regading discussions of Web2.0 speed - I think it’s quite a bit faster than web 1.0 in general operation, and can handle much higher load. My old web 1.0 app would fall to pieces with 30+ users, whereas web 2 can handle 80+ with ease on the same hardware.

However, the initial page load time is pretty poor in Web 2.0.

Doing some debugging in Safari, I quickly see a major problem. It appears as if the initial page load pulls in a lot of inidividual files, and even if these files are cached, the 304 “unchanged” server response is stalling for up to 1 second.

Here’s an example - note that even though these items are all fully cached, it’s taking the Xojo web engine up to 1.0 seconds to respond with a simple 24-byte “304 Unchanged” response.

Here’s a detail showing the stall:

image

What’s causing the stall?

I notice one script is not like the others - CustomColumnTypes.js is always returning a 200 response, never a 304, and this is also the slowest item to load, and the final script that loads.

Could there be a framework bug with CustomColumnTypes.js which is hanging up the entire initial page load?

This is testing a Web 2.0 app built with Xojo 2021 R 2.1 on local ethernet. Server = macOS 10.13, client = macOS 11.6 / Safari 15.0. I see the same behavior in Chrome, so I don’t think it’s browser-specific.

1 Like

CustomColumnTypes.js is an oddball, look how all other scripts are loaded from /framework, but this one is loaded from _files:

While loading several files could be a performance issue with HTTP 1.1, normally you will setup your Xojo Web App behind Nginx or Apache anyway, and they will serve those files using HTTP 2.

That request has been stalled in your browser for 1s, not on Xojo’s, because of using the old protocol. HTTP 1.1 has a limit on the amount of files your browser can download in parallel (six I think, depends on the browser).

CustomColumnTypes.js seems to be a session specific file, so it won’t be cached.

Agree that HTTP 1.1 is a problem here. One easy solution would be if Xojo simply concatenated the scripts into a single javascript file, so it could be done with a single request. Or, even better, just inline the scripts with the initial page load as suggested here: https://developers.google.com/speed/docs/insights/BlockingJS

I don’t agree - Xojo web tool should be easy to use and not require additonal tools for good performance.

This file appears to be due to inclusion of https://documentation.xojo.com/api/user_interface/web/weblistbox.htmlCellRenderer - however the contents is identical for every session, so I’m not sure why it’s being delivered this way.

1 Like

More testing reveals an interesting pattern: running the compiled web app on my server using HTTPS (on a local network but not on the same machine) gives these 1000+msec stalls.

However, if I run the debug app (HTTP on localhost), it’s much faster - the stalls are ~63msec instead 1000.

image

So, I wonder what the difference is? If I ping the server (which is on the same LAN) it’s under 1msec ping time.

Could this be some sort of HTTPS overhead bug?

Edit: here’s a like-for-like comparison.

First, accessing the server via HTTPS on the LAN:

image

Next, accessing the server via HTTP on the LAN:

image

The only difference between the 2 is whether it’s HTTP or HTTPS.

So it looks like Xojo web app’s built in SSL is adding 600msec of latency!

[Edit: updated thread title to better reflect the issue]

1 Like

Regarding the initial page load being slow due to 25+ file requests, I’ve created a feature request to optimize this: <https://xojo.com/issue/66147>

Regarding the SSL (HTTPS) slowdown: <https://xojo.com/issue/66149>

Also, when I sample the Web app running on the server, when it’s handling a SSL request I see a weird stack trace:

This kind of looks like a recursive call ? notice how the function keeps calling itself over and over. I don’t see this patten with HTTP requests.

I wonder if there’s some bug within SSLSocket where it’s re-entrantly calling itself, perhaps blocking other sockets from getting CPU time?

maybe dont “requiere” aditional tools, but the single threaded nature of Xojo make it really bad to serve files so the use of aditional tools to help serving files is a must on most real world apps.

Sorry Mike, but inlining every JS file in the HTML isn’t suggested by that article. Also, improving performance on Single Page Applications is not the same as improving plain marketing websites.

If you’re deploying an internal web app, Xojo standalone is easy and fast enough.

There is a ticket for HTTP/2 support, if you want to give it some points: Feedback #38227

But again, a proper web server is more suitable for public web apps. They will take care of SSL offloading/termination (so Xojo won’t have to deal with SSL), HTTP/2, caching headers and much more. Anything else is just trying to go upstream, I prefer Xojo to fix other things than trying to compete with industry standard dedicated tools.

Tim’s Lifeboat or Xojo Cloud can also help deploying public web apps.

It says

If the external scripts are small, you can inline their contents directly into the HTML document and avoid the network request latency.

and

However, note that inlining also increases the size of the HTML document and that the same script contents may need to be inlined across multiple pages

Since Xojo web app is a SPA (single page app) this warning is not relevant.

In fact, there’s advice for this type of app:

What if I’m using a JavaScript framework to construct the page?
If the content of the page is constructed by client-side JavaScript, then you should investigate inlining the relevant JavaScript modules to avoid extra network roundtrips.

(emphasis mine)

The files range in size from 521 bytes to 300KB with a median size of about 10KB. “Small” of course is relative, but making 25+ requests for ~10KB files is terribly inefficient.

Nobody is suggesting that Xojo Web should replace a full-fledged web server. It is what it is.

However, Xojo Web 2.0 is an app which has bad performance, (for initial page load) and it would be very straightforward to optimize.

The lack of optimization is causing the initial page load to take 3x to 5x as long as it needs to take. In my opinion, fixing this is a no-brainer.

If I were in charge, I would do this:

  • combine the CSS and javascript files into no more than 6 separate files (most web browsers allow only 6 simultaneous connections)
  • see what the problem is with the 304 results taking so long - from my initial investigation, I think the xojo server is doing something wrong with multiple requests (is it serving them up in LIFO order perhaps?) Something doesn’t seem right there.
  • fix the performance bug with SSL sockets. I think there may be an actual bug, or at best, a poor implenetation of cooparative threading as described in <https://xojo.com/issue/66149>
2 Likes

Well, it is relevant. Inline them in HTML and they won’t be cached. You will be sending the same bundle again and again, every time your returning user loads the app.

You’re seeing those 304s stalled because of the protocol, not because Xojo isn’t serving them fast enough. Try to grab those URLs directly with curl, they surely won’t be stalled for seconds.

HTTP/2 can help, as I said. Another cache policy (time-based) can also help, so the browser won’t even have to ask the server if the file has changed or not.

There’s some bug here. Using HTTP/2 instead of HTTP/1.1 is not the cure.

That stack seems a bit weird for sure.
Not behaving like that when using HTTP instead of HTTPS may say something to the investigators.

HTTP/2 can help for sure for the original question, before Mark’s title and content edits.

Enabling HTTPS will change the behaviour. You will start seeing SSL stuff in the stack, not a big surprise.

There are tons of bugs to fix in Xojo Web, but this looks like pursuing ghosts to me.

For clarity, the CustomColumnTypes.js file is generated on-the-fly at runtime because they can be different per session. They also have a high possibility of change and when gzipped they tend to be tiny anyway.

I’m surprised at the 1 second delay, but it should be noted that when a page first loads, most browsers will only make four simultaneous requests to a server to retrieve assets (JavaScript, CSS, images, etc) at a time and the others are queued, so it may just be that.

In terms of the rest of the framework, obviously there are a lot more assets to retrieve in web2 vs web1. That’s what we get for using standardized libraries and publicly licensed controls, but the benefits outweigh in terms of speed.

In web1, we generated and delivered the portion of the framework that was needed for a particular session and each time you made a change, the whole thing need to be pulled to the browser again. We also didn’t always take advantage of gzip so the framework transfer could be quite large. The original system also generated a lot of html snippets on the server and delivered them to the browser over the wire, now html is generated completely client side.

With web2, the framework files won’t change unless we (Xojo) changes something in a major release and even then, only the files that have been changed will be delivered again once per browser, but we check every time because we can’t tell when the page loads if you’ve changed the version of Xojo that you’re using. This is however part of the reason the framework can handle more users per instance… less startup overhead.

In terms of deployment, no, you don’t have to use a load balancer like Apache or Nginx, but if you are deploying your app on the public internet, you certainly should. Not only that you’ll be able to host many more users, they provide you an extra level of protection against bad actors because they can filter out requests that don’t need to go to your app.

2 Likes

See https://forum.xojo.com/t/xojo-web-2-0-faststart/ for a proof-of-concept of inlining the HTML

1 Like

To circle back here - I think the big issue is that the framework files (JS and CSS) are being served with improper cache-control flags.

Here’s an example response header:

Note especiallly this line:

Cache-Control: no-cache, private, max-age=0, must-revalidate, no-transform

These items really are immutable and won’t change (until the Xojo version changes), so no-cache and must-revalidate are really hurting performance here.

A quick fix would be to add the Xojo IDE version number to the filename, e.g.

bootstrap-datepicker-2021r2.1.min.js instead of bootstrap-datepicker.min.js

and then use a proper immutable cache flags with a long lifetime, e.g.

Cache-Control: max-age=365000000, immutable

See Bits Up!: Cache-Control: immutable for a discussion of what the immutable flag does.

Basically, with immutable set, the client does not need to ask the server if the file has changed - this avoids a round-trip network request waiting for the 304 response.

And if the framework version is built into the filename, there won’t be any problems when the user re-builds with a newer version of Xojo.

5 Likes

No need to change anything, just ask for caching and add a GET var like “xojocache” and change that (not used) value every time you need to force a reload, like:

<script src="/framework/bootstrap-datepicker.min.js?xojocache=4321"></script>

Probably a “random number” created at each app loading time, for most of them, is enough. And for those needing changing the contents all the time change them to a not repeating one at runtime, like:

<script src="/session-whatever/custom-whatever.js?xojocache=4321-53773"></script>
3 Likes

Good points - it really depeneds on several factors (public vs. private, proxy vs. naked, etc.) but I think the basic issue is that Xojo Web 2.0 is not delivering optimal cache-control values, and this has non-trivial impacts on performance.

Dont get me wrong: In actual use, I find Xojo Web 2 much faster than 1.0, and it can handle a much higher # of users with ease.

The only issue I’m focusing on here is inital load time, for which Web 2 shows some serious issues, which I feel I’ve diagnosed well and offered good suggestions.

7 Likes

I wasn’t invalidating your points. I was agreeing, adding more information, and enhancing the proposed solutions.

1 Like

The No-cache header doesn’t apply to the browser. It applies to intermediary proxies that may want to cache content and we definitely don’t want them holding onto these files.