Openstreetmap-website: Reduce size of application.js by removing translations?

Created on 16 Aug 2018  路  15Comments  路  Source: openstreetmap/openstreetmap-website

As mentioned in https://github.com/openstreetmap/operations/issues/134 , the application.js bundle is enormous. A quick check shows that ~90% is translations for the browse interface. But nobody needs all of those translations, and at most will only need a couple of languages to show the page.

Even then, I bet that most people only need a few of those strings, unless they do a huge amount of using the browse interface. For example, it's very rare for me to need the "fire_pit" or "battlefield" translations. But all of these are needed for all visits to the website, even just to browse the map and sign up.

A bit of playing around with the interface shows that there's also room to improve the way it works, particularly for the query-feature functionality. It's most noticeable when you're a long way away from the European datacentres. For example, if you click here:

https://www.openstreetmap.org/query?lat=24.7955&lon=121.8043

  • You first get back a "placeholder" html response from osm.org. You need to wait for it to fully load until it fires off two overpass queries
  • Each overpass query returns far more data than is required. For this example, it returns a hundred translations of Taiwan, all but one of which are ignored. Compare the amount of data in https://www.openstreetmap.org/relation/449220 to the output "Country Boundary Taiwan" on the browse pages.

The translations are also used for the regular browse pages, but these exhibit similar problems with requesting more data from the server than is required to show the page. For example, a "relation/full" call grabs all the node ids, way ids, associated usernames, node update timestamps etc as part of the call, and then throws this information away when building what's shown in the interface.

To cut a long story short - why do we do it this way? Could we instead build all of this html output server-side, and just return to the client the minimum of what is needed? This could remove the need for all the js-based translations, and it moves some of the heavy responses away from the end user's connection and keeps them on fast DC-DC links instead.

All 15 comments

The key thing is not removing the translations from application.js, it is splitting them up so you only get the ones for your current locale. As I explained in the operations ticket that appears to be tricky to do but probably not impossible.

Trying to further break them down seems like a bad idea to me - as I also explained on the operations ticket the rails theory in general is not to create dozens of tiny files that need to be downloaded, but to bundle things together.

My goal (for translations) is to remove them from application.js to i18n/<locale>.js or something but it will need to wait a week or two before I have time to look in detail at how to go about it.

Your other suggestion appears to be going in the opposite direction of what I understood to be our direction of travel, by moving things from the client to the server. I'm really reluctant to be tying up server resources on something like that which can be relatively long running and my general goal has been to do the opposite - to the extent that I actually have a branch where I was playing around with moving the geocoder stuff to the client.

I've opened #1950 as a proof of concept of doing it via export but I'd still like to try and do it directly via the pipeline if I can.

That branch is running at https://tomh.apis.dev.openstreetmap.org/ for people to try.

The key thing is not removing the translations from application.js

I disagree. I tried to take the discussion one step further - rather than just "we need to send the translations more efficiently" I tried to examine whether we need to send these translations at all. I'd like to see a compelling reason for doing all this client-side instead of server-side.

Your other suggestion appears to be going in the opposite direction of what I understood to be our direction of travel, by moving things from the client to the server.

I don't know whether there's a direction of travel or not, and I don't have a strong opinion on that. But in my investigations so far it appears that buildling small html pages client-side has moderately large downsides. Again it might not be so noticeable on fast connections in Europe, but when a simple browse page takes multiple EU <-> HK roundtrips it becomes noticeable to me - even on a high bandwidth connection. I expect it will be much worse on low-bandwidth, high-latency connections elsewhere.

I'm really reluctant to be tying up server resources on something like that which can be relatively long running

I don't think putting together the browse or query pages server-side will actually be that expensive, but that's definitely a risk. From what I've seen so far, making one request and getting the compact html in response is going to be much better than multiple xhr back-and-forwards and large json payload responses.

I don't know whether there's a direction of travel or not, and I don't have a strong opinion on that. But in my investigations so far it appears that buildling small html pages client-side has moderately large downsides. Again it might not be so noticeable on fast connections in Europe, but when a simple browse page takes multiple EU <-> HK roundtrips it becomes noticeable to me - even on a high bandwidth connection. I expect it will be much worse on low-bandwidth, high-latency connections elsewhere.

Well surely the point is that we're not really building them client side - we are fetching them from the server and then augmenting. If they were truly built on the client (as horrible as that is to write) then there wouldn't be multiple round trips.

I don't think putting together the browse or query pages server-side will actually be that expensive, but that's definitely a risk. From what I've seen so far, making one request and getting the compact html in response is going to be much better than multiple xhr back-and-forwards and large json payload responses.

My concern is not CPU or anything but rather tying up a rails server for N seconds while it waits for an API response. Obviously one solution is to spin up more rails server, subject to memory constraints.

That branch is running at https://tomh.apis.dev.openstreetmap.org/ for people to try.

This looks very promising. In addition to reducing the download size, we also got rid of about 1.2s of Javascript parsing for all those translations. In Firefox, this affected every page where application.js was included, Chrome somehow cached the parsed result already.

Chrome Dev Tools - Performance audit results

Before (osm.org)

bildschirmfoto von 2018-08-16 19-57-12

After (tomh.apis.dev)

bildschirmfoto von 2018-08-16 19-56-01

Well surely the point is that we're not really building them client side - we are fetching them from the server and then augmenting.

Maybe I phrased it badly. By multiple round trips I mean:

  • Client requests query page (trip 1)
  • Client receives placeholder html (trip 2)
  • Client makes js requests to overpass (trip 3)
  • Client receives json responses from overpass (trip 4)

I think we're very much building these responses client-side.. I'd prefer to see the server making the requests to overpass, to cut down on the number of round trips (and incidentally, this would cut out unnecessary https sessions to different servers too).

(Even the browse pages involve multiple trips, since we do make the html part server-side, but then make the client make a entirely separate request for the node XML just to get the coordinates in order to put it on the map)

My concern is not CPU or anything but rather tying up a rails server for N seconds while it waits for an API response.

Yep, that's what I was thinking of too. OSMF would prefer to be doing this in a multithreaded setup rather than having processes blocked on external responses. Puma (default rails server) runs multithreaded by default, but passenger (what OSMF uses) only supports multithreading in Passenger Enterprise. But that is a decision that can be made later, if we decide that this idea has merit in the first place and if (big if) necessary.

I'd prefer to see the server making the requests to overpass, to cut down on the number of round trips

This will probably not work without major issues at the moment due to ip based rate limiting on the overpass server.

Right, but trip 1 and 2 should be removable is my point.

I've always wanted to move browser to the client side so that we don't read all the data twice, but the API response doesn't actually have everything that is needed to build the HTML at the moment.

I've always wanted to move browser to the client side so that we don't read all the data twice

That's a non-sequitur though. We could avoid reading all the data twice by just responding once with all the html (and some json for the map view highlighting) in the first place.

Making the browse pages entirely reliant on the API means that for a given request, the browser will be requesting info from the server in deliberately the wrong format and then having to convert it, instead of the server just responding with the same information in the format required in the first place.

I feel that this whole "make everything client side" and "sling around API responses and parse them on the client using javascript" has few tangible advantages and is mainly being done "because architecture". We can already see the downsides of this with the translations - even split up per-language, we still need to get each client to download a bunch of stuff that will likely never be shown to them.

I've opened #1950 as a proof of concept

By the way, this approach could also be extended to https://tomh.apis.dev.openstreetmap.org/export/embed.html (for which the operations issue has been opened originally). export*.js also includes all translations.

Doesn't work for export because that determines the locale to use on the client side: https://github.com/openstreetmap/openstreetmap-website/blob/master/app/assets/javascripts/embed.js.erb#L6

I18n.t('javascripts.embed.report_problem') seems to be the only string in use. For some reason, it always shows the English text, probably because the I18n.t function gets called a bit too early. With +10mio accesses/month that quickly adds up to a few TB of traffic that doesn't add value.

This was implemented by merging #1950 along with some extra work in 32a400c5af2bd709f05caad751f4f97dcadc96ce to minimise the size of embed.js.

Thanks! It seems like this change also had quite some impact on some Munin charts, like Apache data volume: https://munin.openstreetmap.org/openstreetmap.org/www.openstreetmap.org/apache_volume.html

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eehpcm picture eehpcm  路  4Comments

kgreenek picture kgreenek  路  6Comments

jidanni picture jidanni  路  3Comments

grischard picture grischard  路  3Comments

pangoSE picture pangoSE  路  7Comments