Server: NextCloud is unacceptably slow without asset pipelining

Created on 23 Nov 2016  Β·  121Comments  Β·  Source: nextcloud/server

According to the docs, asset pipelining has been removed in nextCloud 10. While ownCloud/nextCloud has always been very slow, this makes nextCloud extremely slow and practically unusable even on medium-latency connections.

My network connection has ping times of about 250–300Β ms to my nextCloud server. This might seem slow compared to a high-speed fiber-glass cable connection in an American metropolis, but it is quite normal for a mobile connection, and in many rural areas in the world the latency is even higher on a cable connection.

I have about 10 non-standard apps installed on my nextCloud instance, and just the front page of the Files app takes anything from 1 minute up to 5 minutes to load (with enabled browser cache)! It tells me that it had to do over 200 requests!

A good web application does 2 or 3 requests to load the page, maybe a bit more for more clever caching, if not all components are needed in all parts of the application.

nextCloud should really:

  • enable asset pipelining again. If it breaks some apps, those apps should be fixed.
  • use image sprites
  • get rid of AJAX requests that load settings and stuff on page load, and instead put that data into the DOM

Here are two screenshots to give a rough idea about what is loading:

image

image

enhancement high

Most helpful comment

Please find attached my latest version, the pull request will follow in the next days after I figured how to do this here ;)

speedfix_v1.diff.txt
(Please excuse the strange path names...)

It's seems to be quite simple now:

  1. Add a defer to all external scripts injected by apps
  2. Reorder the three templates and add deferto the external scripts

Order of parsing/execution is now:

  1. Fetch css files
  2. Fetch printcss files
  3. Start fetching external js, continue parsing
  4. Start fetching additional external js from apps, continue parsing
  5. Parse and execute inline js, continue parsing
  6. Parse to the end and execute js from 3 and 4 when finished loading in correct order

But: Keeping execution order of the deferred scripts needs IE>9!
Firefox, Chrome and Opera are working great.

All 121 comments

I agree that we should try to combine stuff more again, but honestly with a server that takes 6 seconds to deliver a 1kb css file... what are you using it for? downloading a 1MB file will take 6k seconds which is over 1 hour? 😨

And as a sidenote: no one of us has an american-metropol-fiberglas-connection.

The problem is really just the latency. Downloading a file works fine with 5–10 MBit/s.

hello,

Use of asset pipeline have already been discussed here : https://help.nextcloud.com/t/asset-pipeline-enabled-leads-to-a-blank-calendar-with-error/1122/4 and in some other thread.

The conclusion was (more or less) : "asset pipeline is old and broken and (very likely) nothing will fix that. Don't use it."

I think that for now, your best options are HTTP/2 and maybe gzip compression of .CSS and .js ...

There was the idea to group JS/CSS based on the app it is loaded from. We maybe look into this once the SCSS PR is in - #1805 - cc @skjnldsv

I think it is important to make a separation between minification and concatination here. In the discussion you are linking, the main argument seems to be that the JavaScript minifier is old and broken. I agree with disabling it then, as an easy solution can be that apps deploy minified JavaScript code, as mentioned in the discussion.

The issue I am facing is not related to a lack minification or compression. It is related to the fact that the browser has to send more than 200 HTTP requests to the server in order to load a page. This problem can only to a small part be solved on an app level. The HTTP standard recommends not to make more than 2 simultaneous connections to a server, and Chrome for example has a hard-coded limit of 6 (see this discussion). HTTP/2 is obviously not a practical solution right now.

If JavaScript files and CSS files (and image sprites) would simply be served concatenated, this would drastically reduce the number of requests and increase performance.

@MorrisJobke Indeed, we could combine all the css easily.
The #2050 pr implement css compression by default.

I agree we can improve here. But, havigng said that, the problem should not be that huge. Basically the first page load is painfully slow then. But after that 99% of the assets should be cached anyways.

HTTP/2 is obviously not a practical solution right now.

@cdauth Maybe not right now, but... Browser support: http://caniuse.com/#feat=http2 Server adoption: http://isthewebhttp2yet.com/measurements/adoption.html

@eppfel: Oh, I must admit that I thought browser support was far less advanced. I was under the impression that HTTP/2 is so far only supported by Chrome when enabling a certain flag.

Will look into it and see how much it improves things.

@cdauth It's actually only supported over TLS (https). Here is a great article about it: https://www.troyhunt.com/i-wanna-go-fast-https-massive-speed-advantage/ But using nextcloud over an insecure connection is not a good idea anyway.

Okay, I enabled HTTP/2 on my server and the speed improvement is tremendous.

I am undecided whether I think asset pipelining is still necessary.

I think some improvements can still be made (such as avoiding AJAX calls on page load, for example to load the config).

HTTP/2 should definitely be mentioned in the Server Tuning docs.

@cdauth Lucky you, don't have that option, right now.

I think some improvements can still be made (such as avoiding AJAX calls on page load, for example to load the config).

Totally agree πŸ‘

HTTP/2 should definitely be mentioned in the Server Tuning docs.

You could do it yourself πŸ˜‰

As HTTP2 is coming, asset pipeline will get obsolete. I don't know if image sprites or CSS compression would still be worth the effort, but I would advise opening dedicated issues and closing this one.
I moved @cdauth's proposal to get rid of some AJAX calls to #2447 and added documentation for HTTP2 in the documentation nextcloud/documentation#242

Fresh install on a new server, open Files -> it's 208 requests (!) and 6 sec to load the page ... Just crazy ...

1Gbps, 0.3 ms latency (LAN Connection)
Firefox 50.0.2 x86_64, http/2

NextCloud 11
nginx-1.10.2
php-7.0.14
php-pecl-apcu-5.1.7
mariadb-5.5.52

Performance analysis of files page:

empty_cache
primed_cache

the same for me with chrome 55.

http2 is enabled together with gzip compression.

NextCloud 11
php-5.6.29
mariadb-10.0.28

first request:
noncache

and second cached request:
cache

Loading times are in fact awful atm. I think some form of concatenation is really needed here.
I agree that the previous asset pipeline was broken, but I think there must be some form of replacement for it.

HTTP2 won't fix that issue, it is just not as dramatic as with HTTP/1.x.

This snippet containing css/js resources was taken from the current nextcloud demo server setup:

    <link rel="mask-icon" sizes="any" href="/core/img/favicon-mask.svg" color="#0082c9">
                    <link rel="stylesheet" href="/core/css/styles.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/inputs.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/header.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/icons.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/fonts.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/apps.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/global.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/fixes.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/multiselect.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/mobile.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/vendor/jquery-ui/themes/base/jquery-ui.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/jquery-ui-fixes.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/tooltip.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files_pdfviewer/css/style.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/share.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files_versions/css/versions.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files_videoplayer/css/style.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/gallery/css/slideshow.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/gallery/css/gallerybutton.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/notifications/css/styles.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/jquery.ocdialog.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files/css/files.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files/css/upload.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files/css/mobile.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files/css/detailsView.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files_trashbin/css/trash.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/activity/css/style.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/comments/css/comments.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files_sharing/css/sharetabview.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files_texteditor/css/DroidSansMono/stylesheet.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files_texteditor/css/style.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/files_texteditor/css/mobile.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/vendor/select2/select2.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/core/css/systemtags.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                    <link rel="stylesheet" href="/apps/systemtags/css/systemtagsfilelist.css?v=9a4ae11b328522aeca1e715f0a55b5b4">
                                    <script src="/index.php/core/js/oc.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/jquery/dist/jquery.min.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/jquery-migrate/jquery-migrate.min.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/jquery-ui/ui/jquery-ui.custom.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/underscore/underscore.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/moment/min/moment-with-locales.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/handlebars/handlebars.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/blueimp-md5/js/md5.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/bootstrap/js/tooltip.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/backbone/backbone.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/es6-promise/dist/es6-promise.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/davclient.js/lib/client.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/clipboard/dist/clipboard.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/placeholders.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/compatibility.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/jquery.ocdialog.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/oc-dialogs.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/js.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/l10n.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/octemplate.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/eventsource.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/config.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/search/js/search.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/oc-requesttoken.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/apps.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/mimetype.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/mimetypelist.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/snapjs/dist/latest/snap.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/backbone/backbone.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/oc-backbone.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/placeholder.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/jquery.avatar.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_pdfviewer/js/previewplugin.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/shareconfigmodel.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/shareitemmodel.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/sharedialogresharerinfoview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/sharedialoglinkshareview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/sharedialogmailview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/sharedialogexpirationview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/sharedialogshareelistview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/sharedialogview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/share.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_videoplayer/js/viewer.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/gallery/js/vendor/bigshot/bigshot-compressed.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/gallery/js/vendor/dompurify/src/purify.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/gallery/js/galleryutility.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/gallery/js/galleryfileaction.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/gallery/js/slideshow.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/gallery/js/slideshowcontrols.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/gallery/js/slideshowzoomablepreview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/gallery/js/gallerybutton.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/notifications/js/app.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/notifications/js/notification.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/files/fileinfo.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/files/client.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/app.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/file-upload.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/newfilemenu.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/jquery.fileupload.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/jquery-visibility.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/fileinfomodel.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/filesummary.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/breadcrumb.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/filelist.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/search.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/favoritesfilelist.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/tagsplugin.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/gotoplugin.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/favoritesplugin.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/detailfileinfoview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/sidebarpreviewmanager.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/sidebarpreviewtext.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/detailtabview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/mainfileinfodetailview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/detailsview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/handlebars/handlebars.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/fileactions.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/fileactionsmenu.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/files.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/keyboardshortcuts.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files/js/navigation.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_sharing/js/app.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_sharing/js/sharedfilelist.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_trashbin/js/app.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_trashbin/js/filelist.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/activity/js/formatter.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/activity/js/activitymodel.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/activity/js/activitycollection.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/activity/js/activitytabview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/activity/js/filesplugin.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/oc-backbone-webdav.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/comments/js/app.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/comments/js/commentmodel.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/comments/js/commentcollection.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/comments/js/commentsummarymodel.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/comments/js/commentstabview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/comments/js/filesplugin.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_sharing/js/share.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_sharing/js/sharetabview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_texteditor/js/editor.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_texteditor/js/vendor/ace/src-noconflict/ace.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_versions/js/versionmodel.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_versions/js/versioncollection.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_versions/js/versionstabview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/files_versions/js/filesplugin.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/vendor/select2/select2.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/select2-toggleselect.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/systemtags/systemtags.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/systemtags/systemtagmodel.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/systemtags/systemtagsmappingcollection.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/systemtags/systemtagscollection.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/core/js/systemtags/systemtagsinputfield.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/systemtags/js/app.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/systemtags/js/systemtagsfilelist.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/systemtags/js/filesplugin.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    <script src="/apps/systemtags/js/systemtagsinfoview.js?v=9a4ae11b328522aeca1e715f0a55b5b4"></script>
                    </head>

In fact, with nextCloud 11, loading times have gone up a lot again, despite HTTP/2. On a low-latency connection where loading times with HTTP/2 were maybe around 4–6 seconds before, they are now around 15 seconds.

I too have noticed drastic increase in overall load times since upgrading to NC 11, not just the initial page. 217 requests, 3,599.67KB, 25s on WiFi LAN, drops to about 15seconds on 1GB wired LAN connection.
Running CentOS 7 and PHP7, haven't figured out an easy way to enable HTTP2 without building from source which I am not really leaning towards.

I noticed this really slow load, too. If you are used to NC10 this will prevent you form upgrading to NC11.

For me this only happens on Firefox (Version 50.1.0) 8.5 sec loading with cache. With Chrominum ( Version 55.0.2883.87) it loads in 2.9 seconds.

I'm experiencing the same problem. (it has become worse since NC 11).
selectie_028

It aren't the PHP requests which are slow (they are fast!) but it's my browser waiting for all the JS and CSS files to load. The screenshot was taken with a fresh profile of Firefox. When the CSS and JS are loaded and you click on a directory in the files app this is very fast. It's just loading all the css and js which is sad because off all the work on the speed up of the PHP side....

I noticed there are some files with 4-5 lines of code and a license header. Also the files aren't minimized or comments are removed. Will it get better because of https://github.com/nextcloud/server/issues/1786 ? Especially the compression part. Maybe the same can be done for the JS?

@LEDfan Did you try the scss integration? If so did you find some improvements?
The js compression could be done yes. The same way the scss function works. Could you work on this?

Well I guess the main reason is the number of files, more then what the content of the files is. Although improving both should help twice.

NC 11 indeed is incredibly slow and not usable in production anymore.

Furthermore it took me more than an hour to notice that mod_pagespeed is disabled via .htaccess.
XSS protection is fine, but the way it is implemented seems to break simple performance optimizations.

XSS protection is fine, but the way it is implemented seems to break simple performance optimizations.

cc @LukasReschke

@webermax Is it every load or just the first? Does it happens in other browsers? Would you test and add some more infos?
I checked the caching headers of NC11 and they look good to me, furthermore they improved from NC10.

On my setup some caches in Firefox were disabled, which lead to the slow load in https://github.com/nextcloud/server/issues/2272#issuecomment-270724438.

So, the first load is slow, up to 10 sec. After this, most of the files are cached, mostly until a NC update. Don't test the load with a "F5" or "CTRL + F5" reload, this way the browser adds Cache-Control "max-age=0" and every file gets renewed.

NC11 resources load is a little slower as with NC10 because it has a few more files and requests. Still the caching improved (9802c5cdd80a81570c3aef35607302164c5c1d81).

As a todo i agree with @nickvergessen https://github.com/nextcloud/server/issues/2272#issuecomment-271238905. This could be tracked in another issue.

Same problem here (280 requests, 30s to connect with an empty cache, 12s afterwards), I thought it was because of my migration from OC, but then I saw #3134 and this issue.

This performance problem is also a problem with the Android app since the connection timeout is too low for such latency (nextcloud/android#601).

@skjnldsv I tested the master branch with SCSS and it's the same.

BTW: the requests where the browser has cached the CSS/JS is still slow, because the browser has to fetch everything from the cache.

Aside from compression, the current scss implementation on master won't change a thing concerning speed.

Are you guys using sharing a lot by chance?

I have similar issues since we upgraded to nextcloud 11 (from owncloud 9, so I don't have a comparison with nc10), but didn't have the time to do a reduced test case to confirm my hypothesis.

Basically, we have one user who owns all the files, and it's shared to others depending on groups. I observed that for this user, I load pages fast enough (ie in a second) while for the other users, initial load times are more than 10 seconds, with opening a folder then taking 3-5 seconds. However what's puzzling me is that loading pages is slow even outside the files app...

Note that in my case the connection quality to the server is fairly good (ie 25 ms ping, 100 mbps connection on the client side) so I'm unsure if my issue is related to this thread.

In my case, I have 8 users and I am the main user with around 30GB of data. My server, although it is not a beast (as seen in my comment https://github.com/nextcloud/android/issues/601#issuecomment-275947663), have a good bandwidth nonetheless since it's a VPS on OVH.

I also noticed that if I go straight to my calendar (https://cloud.example.com/index.php/apps/calendar/), the page is messed up:

capture d ecran 2017-02-02 a 11 36 46

I need to go first on the main page and then I'll have access to my calendar. And I think it is linked to this issue because it's the same as with the main page: the first time you connect to the main page it's really really long, but the other time it works well. As if the first connection to the main page was initializing a bunch of stuff before really be able to use the app properly.

@MightyCreak This could be a RAM issue. Did you check error logs? Had similar issues with the Pi.

I have 4GB of RAM which 300MB is free as /proc/meminfo is telling me:

$ cat /proc/meminfo
MemTotal:        4024576 kB
MemFree:          293020 kB
Buffers:          475596 kB
Cached:          2491872 kB
SwapCached:           96 kB
Active:          1542092 kB
Inactive:        1798364 kB
Active(anon):     133620 kB
Inactive(anon):   397336 kB
Active(file):    1408472 kB
Inactive(file):  1401028 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        523260 kB
SwapFree:         512584 kB
Dirty:               120 kB
Writeback:             0 kB
AnonPages:        373040 kB
Mapped:           104636 kB
Shmem:            157968 kB
Slab:             301196 kB
SReclaimable:     275060 kB
SUnreclaim:        26136 kB
KernelStack:        1264 kB
PageTables:        12100 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     2535548 kB
Committed_AS:     981004 kB
VmallocTotal:   34359738367 kB
VmallocUsed:       81388 kB
VmallocChunk:   34359654720 kB
HardwareCorrupted:     0 kB
DirectMap4k:        9132 kB
DirectMap2M:     4167680 kB

I'm not a sysadmin, but from my understanding of Linux, it's not dangerous since almost no swap is used (I guess there is a lot of cached mem that can be ditched if a process needs some mem space).

I have the same problem and noticed a very significant decrease when updating to Nextcloud 11. The instance is running on a small Digitalocean Droplet around 500km away from me, Ping time is around 22-30ms. Three users, the data directory is around 4GB. One single share. When opening pretty much any app directly - news, files, calendar, contacts - the header is sent almost immediately (at least so the title tag is rendered in the tab; hard to tell how quick exactly, but far below 1s), but it takes around 15s until the page is rendered. I run the latest PHP 7.0 and nginx with HTTP2 enabled. Without caching it's even worse, Firefox tells me it needs 223 requests and takes almost 30 seconds (!). Maybe Immutable Caching can help somehow? All the assets are versioned anyways. What about async or defer on the script tags?

If I can provide any additional information I'm happy to help. Also of course if there's any obvious fix that I'm blind to I'd be very grateful.

Maybe Immutable Caching can help somehow? All the assets are versioned anyways. What about async or defer on the script tags?

@LukasReschke Are we brave enough to do this? πŸ˜‰ But this seems to be a nice way to cache harder on the client side.

capture d ecran_2017-02-03_07-10-33

Yes, current browser support is ridiculously low. But in the article they list a few other big projects (Facebook, IPFS, Squid Proxy) that adopted it and reported good results. It's also on a standards track, so other browsers should be quick to follow and it's at least worth keeping an eye on (it already landed in Safari nightly). The question is, of course, how to deal with it in the meantime. Browsersniffing for different caching headers? Seems like a really inelegant solution, also because It would have to be frequently updated to keep up with current support. Maybe there's a way to set the header that would not be detrimental for non-supporting browsers while still giving advances to the ones with support.

I'm just wondering if this is a good idea to work on this if it only apply to Firefox. :/

I added the immutable caching header to assets on my instance (via apache config) but it did not really give me a performance boost, or at least not that I could notice a difference.

I mean, it's a problem by design. There is a reason why there are a lot of tools like webpack out there to concatenate and compress the assets. The issue is only partly resolved with caching since you will have to read many files from disk.

I agree with @go2sh, it is a design problem. There are simply to many different js/css files loaded. Asset pipelining took care of this problem.

How realistic is it, that this problem will be solved with Nextcloud 12 (e.g. by implementing something like webpack)?

This is what I get with Firefox and Nextcloud on nginx with php7-fpm and HTTP/2 enabled:
screenshot_20170203_131514

And apparently Firefox loads the resources only one after another in a sequential way:
screenshot_20170203_131556

In contrast to that Chromium seems to load the resources in a rather parallel sort of way:
screenshot_20170203_132032

@MorrisJobke Is that normal?

at the moment I cannot enable HTTP2 on my server so I'm trying everything else. Sorry for slightly OT but are all assets sent without gzip?

We don't use HTTP2 atm

Is it stupid to add the asyncattribute to some script tags?

http://www.w3schools.com/tags/att_script_async.asp

I changed my nginx configuration from add_header Cache-Control "public, max-age=10080"; to add_header Cache-Control "public, max-age=10080, immutable";, the results are (this is on a Macbook Air with an SSD by the way, where I thought file system access would be almost non-noticable) 222 requests and 15.61 seconds for the first hit 221 requests, 14.06 seconds for the second, cached request. Of course, fluctuations appear as usual. More than half of the resources loaded from the local filesystem, but the improvement is, after multiple tests, really not that spectactular.

@uok Gzip is not activated in my case, though I don't see why you couldn't activate it in the php.ini.

Adding "query_cache_type = on" to /etc/mysql/my.cnf did the trick for me. Thats the Cache Part in my Config:

# * Query Cache Configuration
#
query_cache_limit       = 2M
query_cache_size        = 32M
query_cache_type        = on

I'll try that... (reading the doc before: https://dev.mysql.com/doc/refman/5.7/en/query-cache-configuration.html)

On Debian 8.7, mysql default config already has the cache type on with a 1M limit and a 16M size:

mysql> SHOW VARIABLES LIKE 'query_cache_limit';
+-------------------+---------+
| Variable_name     | Value   |
+-------------------+---------+
| query_cache_limit | 1048576 |
+-------------------+---------+
1 row in set (0.01 sec)

mysql> SHOW VARIABLES LIKE 'query_cache_size';
+------------------+----------+
| Variable_name    | Value    |
+------------------+----------+
| query_cache_size | 16777216 |
+------------------+----------+
1 row in set (0.00 sec)

mysql> SHOW VARIABLES LIKE 'query_cache_type';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| query_cache_type | ON    |
+------------------+-------+
1 row in set (0.00 sec)

So cache being already enabled, I'll try with 2M and 32M, but I'm not doubtful that will help a lot...

Edit:
Well... it seems to have improved the situation quite well (I hope it won't starve the other services I run). I'm still unsure if it works because of this or because I just went there recently. I'll keep you posted after running with these settings for a few days.

Edit 2:
As @cinemast said, it didn't change the problem and my Nextcloud instance is still very slow to log in. Reverting to the default mysql config then.

An important note on my side: I also have a Sonerezh instance on my side and on the client side (i.e. the browser), Sonerezh caches the next song in the browser cache, but each song has takes it's own cache, meaning that quite rapidly, I ran out of cache in my browser and it start to clear the "oldest" cache, and thus the Nextcloud cached resources.

That is why I often see the problem on my side. My cache (350 MB) is filled up at least 3 times a day.

This issue has nothing to do with mysql. Its about the way ressources like JavaScript, CSS and images are loaded.

You're right @cinemast, I edited my previous comment.

@cinemast

Yes, I have the same problem wit my NC11:
Fast asynchronous loads with Chromium (81 requests, 0.5 sec), very slow synchronous loads with Firefox (84 requests, 3.5 sec). Both clients linux, latest versions, HTTP2, 100 MBit via WAN, nginx server@freebsd, mysql db, php7, loading login page.

@MightyCreak

Maybe async won't work due to dependencies between the loaded js resources?
And: Why does Chromium do async loads and Firefox not...?

It's completely possible that Chromium is a bit smarter and does the loading in two passes: first load the files asynchronously, second parse them and resolve dependencies.

It's actually so simple that I wonder why Firefox wouldn't do that actually.. maybe it's part of their electrolysis feature as well?

Even though this is slightly off topic - I guess it's still somehow related to the overall "How do we get NC faster"-problem: The HTTP 2 behavior seems to be independent of scripts (which rules out the electrolysis explanation I guess). I just tested it on this page and while Chrome and Safari have a speed improvement of close to 50%, Firefox has an improvement of only around 10%.

@Gomez I can confirm that performance issues regarding page loading seem to be related to Firefox.

@heyarne I don't have your results, at all, on Firefox (on OSX). I have a loading time of 7s in HTTP1 ans 1s in HTTP2. The latency is around the same for both: 110ms

@MightyCreak
Hm, if Chromium is really so smart to optimize resource loading then IMHO Nextcloud should be so smart, too.
If it's really necessary to load hundred of different files then these files should be requested the same way Chromium does. Maybe we need to manually reorder these request and flag them with "async" if possible?

@michaelletzgus You missed a word I think, but I'll guess you meant "Firefox"

if Chromium is really so smart to optimize resource loading then IMHO should be so smart, too.

I hope so, I love Firefox and think it's far better than Chrome in a lot of different places (but obviously not all). But that's not really the point here πŸ˜‰

I agree with you that there must be some scripts that can be set "async", they can't be all interdependent! or there is a real modularity problem πŸ™‚

@MightyCreak
Oups, Γ„hh, no, I meant "Nextcloud" should be smarter in delivering its files.
I think it's not a good idea to rely on client side optimization, even the dumbest but standards-compliant browser should be able to load Nextcloud reasonably fast.
But it's still possible there's a bug in Firefox' engine, I don't know.

Hi,
According to what you described, i have the same issue.
Firefox is very slow loading the file app in NextCloud, making more than 200 requests, and taking ~35sec to load the page, whereas Google Chrome does it in 5sec.
At the beginning i thought it was a performance issues related to my nginx server, so i tried a workaround by enabling HTTP2, but it just improved the performance for Google Chrome.

So should we make a bug report to Firefox ?
Or is there something to do in NextCloud server too ?

Thanks !

I can confirm that page loading has slowed down significantly after upgrading from Nextcloud 10 to Nextcloud 11. My internet connection is quite fast, but loading takes several seconds. Currently, I can't use HTTP2 and I don't want to use Chrome.

In my view, this has to be fixed directly in Nextcloud in reducing the number of request required for CSS and JavaScript.

Is there any developer who is working on this issue?

Is there any developer who is working on this issue?

Doesn't seem so. We are happy for every helping hand to implement some CSS/JS concatenation. As a first step it would be good to have the files we always include concatenated automatically:

https://github.com/nextcloud/server/blob/200a28255e058843465c63080d917170de293ee6/lib/private/legacy/template.php#L109-L151

@ChristophWurst had a first idea over here: #3389

@korelstar
I think reducing the number of requests isn't necessary since http2 can handle many requests very well.
The order of the requests is the crucial point, Chromium is able to parallelize them, Firefox not.

Maybe I've found a naive but simple solution:
I added the defer attribute to all the <script> tags in /core/templates/layout.*.php.

With defer Firefox is able to do all these requests in parallel and page load is way faster (~6x) now. There is only drawback so far:
The theming app throws an error in ThemingController.php, line 14798:
"ReferenceError: OCA is not defined"

Other apps like contacts, calendar and files work without any problem.

By the way, the async attribute does not work, it does not keep the order of script execution, so defer seems to be preferable.

So we need an expert in the theming app to fix the OCA.theming problem...

Does anyone know what

<?php print_unescaped($_['headers']); ?>

in e.g. /core/templates/layout.guest.php
does and where it's defined?

It creates this output:
<script src="/apps/theming/js/theming?v=40" [...]>

I don't know where this comes from so I can't add my defer to the <script>.

This causes the problem described in my previous post because this /apps/theming/js/theming?v=40 is not deferred after page load.

@michaelletzgus Currently, I can't use HTTP2. So that's no solution for me (and others?). I still think, that automatic concatenation would be helpful.

Regarding your approach: You can find the definition of print_unescaped here: https://github.com/nextcloud/server/blob/master/lib/private/legacy/template/functions.php#L45

Even with http1.n parallel loading is faster then serial - and http2 will be the future! ;-)

The problem is not the print function itself - it's the argument $_['headers'].

I don't know whether 'defer' is the optimal solution since IE<9 doesn't like it.
Maybe we need a script base loading algorithm as proposes here?

https://www.html5rocks.com/en/tutorials/speed/script-loading/
https://www.nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/

Found it, in the theming apps app.php:

\OCP\Util::addHeader( 'script', [ 'src' => $linkToJs, 'nonce' => \OC::$server->getContentSecurityPolicyNonceManager()->getNonce() ], '' );

After adding a defer after 'script' (OK, the script closing tags is also affected by this...) loading is now fast and without any errors so far. :-)

Firefox@Linux(latest version); Server nginx@FreeBSD@vHost via medium latency (30-150 ms) link:

84 requests (55 js)

login page http1.1: 7.1 sec
login page http1.1, with defer: 1.9 sec

login page http2: 6 sec
login page http2, with defer: 1.5 sec

Hi @michaelletzgus , very interesting !
i would like to test this modification on my nextcloud instance.
could you be more specific in which app.php file it is ?
~
find /var/www/nextcloud -name app.php | wc -l
47
~

And, as i understood, the modification is the following:
~
OCPUtil::addHeader( 'script', 'defer', [ 'src' => $linkToJs, 'nonce' => OC::$server>getContentSecurityPolicyNonceManager()->getNonce() ], '' );
~

Is that correct ?

Thanks !

1.
/core/templates/layout.base.php
/core/templates/layout.guest.php
/core/templates/layout.user.php

Change all <script nonce=...> to <script defer nonce=...> except the ones after <?php if (isset($_['inline_ocjs'])): ?> because this is inline js, not from external file.

2.
apps/theming/appinfo/app.php

Changed:

\OCP\Util::addHeader(
        'script defer',
        [
                'src' => $linkToJs,
                'nonce' => \OC::$server->getContentSecurityPolicyNonceManager()->getNonce()
        ], ''
);
  1. Restart your webserver or flush all php caches and try...

this is a summary of your modifications : PR files changes
I hope that it's correct.

I restarted my Nginx server, as well as php5-fpm, and here are the results (using HTTP2 only):
I loaded the page without the cache (CTRL + F5)
Firefox
firefox

  • 238 requests
  • 11.90 sec
    the previous result was 35 sec

Chromium
chromium

  • 240 requests
  • 4.18 sec
    the previous result was ~5 sec

So this clearly improves the loading speed for Firefox !!

Is this more a hack, or a valid fix for this issue ?
What should we do now ? NextCloud developers ?

Thanks to @michaelletzgus !

For me this started as a hack this morning, this was my first contact with the nextcloud server code... G

The modification in the theming plugins app.php should be improved because it generates code like this:
<script defer> </script defer>
This must be improved.

Since no other app requires script loading like the theming app and there is no obvious problem (no script errors in browser console) with the deferred js loading this MIGHT be a fix to this this issues.

I think the more experienced nextcloud developers should comment this...

I think we schould call this a "HotFix".

IMHO nextcloud should have control over the mode of the <script> tag. Currently, all 3rd party apps might be in trouble if the use \OCP\Util::addHeader() for script loading without defer.

A small improvement for the app.php change:

\OCP\Util::addHeader(
        'script',
        [
                'defer' => '',
                'src' => $linkToJs,
                'nonce' => \OC::$server->getContentSecurityPolicyNonceManager()->getNonce()
        ], ''
);

I have tried out this hint by @michaelletzgus and made screenshots of the median out of 5 runs on the login page with webpagetest.org. The left one is before and the right one after the modification. My web server is nginx with http2 enabled.

Apparently there is a measurable benefit.

@michaelletzgus Would you like to open a Pull Request to nextcloud/server? (e.g. by using this Patch)

@benediktg
Yes I'll create a pull request, but I want to check some possible problems with 3rd party apps first.

In the meanwhile you can test another improvement fixing a problem with the empty string value of the defer attribute:

--- lib/private/legacy/template.php.orig        2017-01-15 17:03:11.000000000 +0100
+++ lib/private/legacy/template.php.test        2017-02-26 11:44:24.458491000 +0100
@@ -236,7 +236,10 @@
                        foreach(OC_Util::$headers as $header) {
                                $headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']);
                                foreach($header['attributes'] as $name=>$value) {
-                                       $headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"';
+                                       $headers .= ' '.\OCP\Util::sanitizeHTML($name);
+                                       if ($value != null) {
+                                               $headers .= '="'.\OCP\Util::sanitizeHTML($value).'"';
+                                       }
                                }
                                if ($header['text'] !== null) {
                                        $headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>';
--- apps/theming.orig/appinfo/app.php   2017-01-15 17:03:11.000000000 +0100
+++ apps/theming.test/appinfo/app.php   2017-02-26 11:16:45.556200000 +0100
@@ -46,6 +46,7 @@
 \OCP\Util::addHeader(
        'script',
        [
+               'defer' => NULL,
                'src' => $linkToJs,
                'nonce' => \OC::$server->getContentSecurityPolicyNonceManager()->getNonce()
        ], ''

Another small optimization gaining a few percent. This places the blocking inline script after all other css and deferred js loads:

diff -Naur core/templates.orig/layout.base.php core/templates.test/layout.base.php
--- core/templates.orig/layout.base.php 2017-01-15 17:03:11.000000000 +0100
+++ core/templates.test/layout.base.php 2017-02-26 12:26:48.370187000 +0100
@@ -18,15 +18,16 @@
                <?php foreach($_['printcssfiles'] as $cssfile): ?>
                        <link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>" media="print">
                <?php endforeach; ?>
+               <?php foreach ($_['jsfiles'] as $jsfile): ?>
+                       <script defer nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script>
+               <?php endforeach; ?>
+               <?php print_unescaped($_['headers']); ?>
                <?php if (isset($_['inline_ocjs'])): ?>
                        <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" type="text/javascript">
                                <?php print_unescaped($_['inline_ocjs']); ?>
                        </script>
                <?php endif; ?>
-               <?php foreach ($_['jsfiles'] as $jsfile): ?>
-                       <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script>
-               <?php endforeach; ?>
-               <?php print_unescaped($_['headers']); ?>
+
        </head>
        <body id="body-public">
                <?php include('layout.noscript.warning.php'); ?>
diff -Naur core/templates.orig/layout.guest.php core/templates.test/layout.guest.php
--- core/templates.orig/layout.guest.php        2017-01-15 17:03:11.000000000 +0100
+++ core/templates.test/layout.guest.php        2017-02-26 12:26:25.448414000 +0100
@@ -19,15 +19,15 @@
                <?php foreach($_['printcssfiles'] as $cssfile): ?>
                        <link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>" media="print">
                <?php endforeach; ?>
+               <?php foreach($_['jsfiles'] as $jsfile): ?>
+                       <script defer nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script>
+               <?php endforeach; ?>
+               <?php print_unescaped($_['headers']); ?>
                <?php if (isset($_['inline_ocjs'])): ?>
                        <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" type="text/javascript">
                                <?php print_unescaped($_['inline_ocjs']); ?>
                        </script>
                <?php endif; ?>
-               <?php foreach($_['jsfiles'] as $jsfile): ?>
-                       <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script>
-               <?php endforeach; ?>
-               <?php print_unescaped($_['headers']); ?>
        </head>
        <body id="<?php p($_['bodyid']);?>">
                <?php include('layout.noscript.warning.php'); ?>
diff -Naur core/templates.orig/layout.user.php core/templates.test/layout.user.php
--- core/templates.orig/layout.user.php 2017-01-15 17:03:11.000000000 +0100
+++ core/templates.test/layout.user.php 2017-02-26 12:27:12.633109000 +0100
@@ -26,15 +26,15 @@
                <?php foreach($_['printcssfiles'] as $cssfile): ?>
                        <link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>" media="print">
                <?php endforeach; ?>
+               <?php foreach($_['jsfiles'] as $jsfile): ?>
+                       <script defer nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script>
+               <?php endforeach; ?>
+               <?php print_unescaped($_['headers']); ?>
                <?php if (isset($_['inline_ocjs'])): ?>
                        <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" type="text/javascript">
                                <?php print_unescaped($_['inline_ocjs']); ?>
                        </script>
                <?php endif; ?>
-               <?php foreach($_['jsfiles'] as $jsfile): ?>
-                       <script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script>
-               <?php endforeach; ?>
-               <?php print_unescaped($_['headers']); ?>
        </head>
        <body id="<?php p($_['bodyid']);?>">
        <?php include('layout.noscript.warning.php'); ?>

This works fine with almost all app enabled. There was a warning when enabling the notifications app, but this seems not to be reproducible...

This places the blocking inline script after all other css and deferred js loads

How about placing the inline Javascript at the end of the body element?

Edit: it does not make that much of a
difference (red: inline js script tag at the end of body, blue: just swapped the script tags so that the inline js script tag is placed after the external js one)

I think it would be interesting to have the point of view of a Firefox dev. From my experience, Firefox strictly follows the standards while Chrome takes some liberties.

I opened a bug in Bugzilla, hopefully we'll have some insights on that matter:
https://bugzilla.mozilla.org/show_bug.cgi?id=1342781

In the meanwhile you can test another improvement fixing a problem with the empty string value of the defer attribute:

Another small optimization gaining a few percent. This places the blocking inline script after all other css and deferred js loads

Here is my result. Blue is the one with this additional change.

Test environment, NC 11.0.1, my latest changes

  • Loading: ~7 times faster than original 11.01.
  • Problem with calendar app an IE11.0.9600, "XPathResult undefined" error while loading calendar. BUT: I get the same problem with original NC, I'll check this later
  • jsxc: [WARN] Unable to create user prefix, but same message with original NC
  • Audio Player app: "TypeError: callback is not a function" in app.js:1575:6, same problem with original NC
  • l10n.js: "Missing plural form in language file" => unrelated...?

> sudo -u www php /DATA/WWW/VHOST03-nctest1/occ app:list
Enabled:

  • activity: 2.4.1
  • admin_audit: 1.1.0
  • admin_notifications: 1.0.0
  • announcementcenter: 3.0.0
  • apporder: 0.3.3
  • audioplayer: 1.4.1
  • bookmarks: 0.9.1
  • calendar: 1.5.0
  • comments: 1.1.0
  • contacts: 1.5.3
  • dav: 1.1.1
  • deck: 0.1.1
  • direct_menu: 0.10.0
  • encryption: 1.4.1
  • external: true
  • federatedfilesharing: 1.1.1
  • federation: 1.1.1
  • files: 1.6.1
  • files_accesscontrol: 1.1.2
  • files_automatedtagging: 1.1.1
  • files_downloadactivity: 1.0.0
  • files_external: 1.1.2
  • files_markdown: 1.0.1
  • files_opds: 0.8.2
  • files_pdfviewer: 1.0.1
  • files_reader: 0.8.4
  • files_retention: 1.0.1
  • files_sharing: 1.1.1
  • files_texteditor: 2.2
  • files_trashbin: 1.1.0
  • files_versions: 1.4.0
  • files_videoplayer: 1.0.0
  • firstrunwizard: 2.0
  • gallery: 16.0.0
  • gpxedit: 0.0.4
  • gpxpod: 2.0.1
  • issuetemplate: 0.2.1
  • keeweb: 0.3.0
  • logreader: 2.0.0
  • lookup_server_connector: 1.0.0
  • mail: 0.6.2
  • news: 10.1.0
  • nextant: 1.0.6
  • nextcloud_announcements: 1.0
  • notes: 2.2.0
  • notifications: 1.0.1
  • ocsms: 1.11.5
  • ojsxc: 3.1.1
  • ownbackup: 16.11.0
  • ownpad: 0.5.7
  • passman: 2.1.1
  • password_policy: 1.1.0
  • piwik: 0.2.0
  • previewgenerator: 1.0.1
  • provisioning_api: 1.1.0
  • qownnotesapi: 16.12.0
  • rainloop: 4.26.1
  • registration: 0.2.3
  • serverinfo: 1.1.1
  • sharebymail: 1.0.1
  • spreed: 1.2.0
  • survey_client: 0.1.5
  • systemtags: 1.1.3
  • tasks: 0.9.4
  • templateeditor: 0.2
  • theming: 1.1.1
  • twofactor_backupcodes: 1.0.0
  • updatenotification: 1.1.1
  • user_external: 0.4
  • user_saml: 1.2.2
  • workflowengine: 1.1.1
    Disabled:
  • ocr
  • theming.orig
  • theming.test
  • user_ldap

IMHO better and more flexible because app must not be changed.
Every <script> injected from apps into the header is marked with defer now.
No changes to any app (theming, app.php so far) are necessary now.

So please try the following modification and move /apps/theming/appinfo/app.php back to the original version without any defer.

--- lib/private/legacy/template.php.orig        2017-01-15 17:03:11.000000000 +0100
+++ lib/private/legacy/template.php.test        2017-02-26 22:09:36.313352000 +0100
@@ -235,6 +235,9 @@
                        $headers = '';
                        foreach(OC_Util::$headers as $header) {
                                $headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']);
+                               if (strcasecmp($header['tag'], 'script') == 0) {
+                                       $headers .= ' defer';
+                               }
                                foreach($header['attributes'] as $name=>$value) {
                                        $headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"';
                                }

So please try the following modification and move /apps/theming/appinfo/app.php back to the original version without any defer.

The result is about the same. Blue is after this modification so that only core/templates/layout.*.php and lib/private/legacy/template.php are modified.

Please find attached my latest version, the pull request will follow in the next days after I figured how to do this here ;)

speedfix_v1.diff.txt
(Please excuse the strange path names...)

It's seems to be quite simple now:

  1. Add a defer to all external scripts injected by apps
  2. Reorder the three templates and add deferto the external scripts

Order of parsing/execution is now:

  1. Fetch css files
  2. Fetch printcss files
  3. Start fetching external js, continue parsing
  4. Start fetching additional external js from apps, continue parsing
  5. Parse and execute inline js, continue parsing
  6. Parse to the end and execute js from 3 and 4 when finished loading in correct order

But: Keeping execution order of the deferred scripts needs IE>9!
Firefox, Chrome and Opera are working great.

Thanks @michaelletzgus @benediktg and @MightyCreak for your continuous research on this πŸ‘ We really appreciate this and would be happy to see a pull request πŸ˜ƒ

@michaelletzgus I can help you with the PR if you want

From skimming this thread I believe there is a fundamental misunderstanding here. The problem is not compression, the problem is that the web interface makes hundreds of HTTP requests for files less than 5 KB in size. You can enable gzip compression very trivially at the web server (Apache/nginx) level. This will reduce total page size but leaves the number of requests intact. Performance is suffering from the network overhead of establishing a TCP connection and waiting for the server to deliver a resource, not the transfer speed of the resource. Most of us have direct LAN connections to Nextcloud and the actual transfer time is a fraction of a second, especially for these JS/CSS resources < 5 KB in size; the cumulative time spent establishing the connection causes the total load time to be orders of magnitude higher.

Dismissing OP's problem with statements such as "but honestly with a server that takes 6 seconds to deliver a 1kb css file... what are you using it for? downloading a 1MB file will take 6k seconds which is over 1 hour?" is completely unreasonable. The core problem lies in the fact that Nextcloud makes no use of any bundling whatsoever, causing the frontend to make 207 HTTP requests just to load the page. It's appalling that this is deliberate behavior.

@LINKIWI
207 requests are no problem when they are performed in parallel, especially when using http2.
With http2 the overhead this should be far far away from 6 seconds. Even with my medium-latency radio internet link I can load hundreds of css/js reasonably fast - if the are done in parallel!
Compression does't really matter here, full ack!

The problem discussed above is caused by firefox which loads all the assets strictly after one other.

yes, http/2 solves the current problem with firefox – a little bit.

nextcloud still does not feel very snappy. not only when loading for the first time, but when switching apps e.g. from files to calendar, too.

207 requests are no problem when they are performed in parallel

yeah, and http/3 will save us by allowing to download in 3D, with moar dimensions of parallelism! No need to optimize a damn thing, the app will teleport itself onto the machine!

On the serious side, these numbers are, well, insane. And I'm puzzled by how little consideration this issue is dealt with. OK there are many other cool things to be developed, and this specific one touches a rather fundamental piece of current nextcloud's architecture which is not so trivial to fix. But common, guys, this is the user-facing side of your whole app, it deserves some care.

Also, the proposed workarounds are just that, band-aid: http/2 is not an option for most servers, and all it will do in the end is to remove a handful of SSL handshakes… but, in the real world, at the receiving-end, people will still be waiting for 4MB of js and 1MB of assets to be pushed down the pipe for very dubious reasons.

I don't know http/3 yet (quic?) and downloading in 3D sounds interesting (6G?), but http/2 is supported by popular web server software and is easy to configure - at least if you know what you are doing, which is always a good idea if you build your own server. :-)

IMHO parallel loading of the current mass of js/css is a reasonably good quick-fix.
It yields, depending on link latency, a factor of ~3x with pipelinig http/1.1, http/2 yields another factor of ~2.

I'm dealing with the nextcloud/server code for ~3 weaks, so I don't know if megabytes of js code (moment-with-locales.js:443kB, jquery-ui.custom.js:422 kB, handlebars.js:155 kB ...) is really required - bit I guess it's not. I think there are a lot of things which must be optimized - and in the meanwhile a speed improvement of 3x-6x is a nice-to-have but not an optimal solution.

[…] but http/2 is supported by popular web server software and is easy to configure - at least if you know what you are doing, which is always a good idea if you build your own server. :-)

This assumes that most users have control over the server to the extent that they are allowed to install and configure software. That's "optimistic" to say the least (many people just run on shared instances), and it tells nothing about their willingness or technical abilities to do so. This also assumes that, in case they have root access and all, the server is running a recent-enough distribution to support http2. This implies, I think, having httpd24, which leaves aside any debian

I'm dealing with the nextcloud/server code for ~3 weaks, so I don't know if megabytes of js code (moment-with-locales.js:443kB, jquery-ui.custom.js:422 kB, handlebars.js:155 kB ...) is really required - bit I guess it's not. I think there are a lot of things which must be optimized

That's a relief! More than the raw numbers, I believe there are many small things and clever approaches to be taken in order to have a more responsive UX and reduce the time-to-something-functional. Like replace the infuriating lazy loading of everything by pre-rendering/caching, for the main elements at least. Maybe this is just me, but when I open NC, I expect to be able to browse my files right away, and not see the UI getting stuck for tens of seconds because something I'm not even seeing the benefit of is loading in the background.
Anyway, thanks for the effort!

IMHO parallel loading of the current mass of js/css is a reasonably good quick-fix.
It yields, depending on link latency, a factor of ~3x with pipelinig http/1.1, http/2 yields another factor of ~2.

This is really not a solution. It is, at best, an attempt to work around the core problem by over-optimizing another layer of the application stack.

I don't disagree with the claim that gzip compression and HTTP/2 help overall performance, but these optimizations do not address the actual problem. The problem here is the absence of module bundling, which goes against every reasonable and sane principle of designing performant client-side SPAs. Tools like webpack and countless CSS minifiers exist for exactly this purpose.

@LINKIWI

This is really not a solution. It is, at best, an attempt to work around the core problem by over-optimizing another layer of the application stack.

Full Ack, That's why I called it "nice-to-have" and "quick-fix" :-)

Module bundling? Yes, but f we need "module tidy out"! More than 1 MB (uncomressed) js code - this is enough for an whole operating system. ;)

I agree with @rom1dep. I have my own server, but since I installed stable Debian on it, on which nginx's package doesn't handle http/2, and that there are quite a few services running at the same time, I just can't update my server to testing just like that.

Because of that and that I guess I'm not the only one in this situation (as @rom1dep pictured), it would really be nice to have a proper fix on the app side. For sure, HTTP/2 will always be a nice to have, but if Nextcloud doesn't handle this problem seriously, it will only hide it under the rug.

Nextcloud 12 will allow combining JS in apps as per https://github.com/nextcloud/server/pull/3988, and also CSS merging.

@LukasReschke Did you (or someone else) made some evaluation (performance measurement) that shows that #3988 is sufficient for everybody and #3696 is really not needed anymore?
Maybe, combining both approaches would provide further enhancement.

Testing the JS Combiner (current master, 4ea79a5) by hard reloading the files app with Firefox 52.
Enabled apps:

  • admin_audit: 1.2.0
  • admin_notifications: 1.0.0
  • calendar: 1.5.2
  • comments: 1.2.0
  • contacts: 1.5.3
  • dav: 1.2.0
  • deck: 0.1.1
  • federatedfilesharing: 1.2.0
  • federation: 1.2.0
  • files: 1.7.2
  • files_external: 1.3.0
  • files_external_sia: 0.1.0
  • files_markdown: 1.0.1
  • files_opds: 0.8.2
  • files_reader: 1.0.1
  • files_sharing: 1.2.0
  • files_trashbin: 1.2.0
  • files_versions: 1.5.0
  • issuetemplate: 0.2.1
  • lookup_server_connector: 1.0.0
  • nextant: 1.0.6
  • notes: 2.2.0
  • ownbackup: 17.3.0
  • passman: 2.1.1
  • previewgenerator: 1.0.5
  • provisioning_api: 1.2.0
  • rainloop: 4.28.1
  • registration: 0.2.3
  • sharebymail: 1.1.0
  • spreed: 1.2.0
  • spreedme: 0.3.8
  • systemtags: 1.2.0
  • tasks: 0.9.5
  • theming: 1.3.0
  • twofactor_backupcodes: 1.1.0
  • twofactor_totp: 1.1.0
  • twofactor_u2f: 1.1.0
  • updatenotification: 1.2.0
  • workflowengine: 1.2.0

Results:

(1) debug=true (no js combine) =>"classic"
198 total requests, 3.4 MB, 26 sec
123 js requests, 2.9 MB, 21 sec

(2) debug=false (js combine enabled) =>"combined"
117 total requests, 3.4 MB, 8.5 sec
42 js requests, 2.9 MB, 5.7 sec

(3) debug=false (js combine enabled) with additional deferdark magic =>"combined dark magic"
116 total requests, 3.3 MB, 4.5 sec
42 js requests, 2.9 MB, 2.1 sec

(4) deferdark magic only =>"dark magic"
197 total requests, 3.4 MB, 6.5 sec
123 js requests, 2.9 MB, 4.2 sec

One XHR request is missing in (3) due to a problem with the files markdown app, see #3696.

Chromium needs ~4 sec in either case.

So I would say we need both. πŸ˜‰

I'm still very much against this, this changes a core behaviour and has the potential to break apps. This changes the behaviour of loaded JavaScript totally and may make some stuff (e.g. apps that do redirects) even break totally.

πŸ‘Ž – Sorry πŸ˜‰

The only reproducible problem I observed so far is the one with files_markdown mentioned above.

If we manage to combine all scripts there should be virtually no difference between "combining" and "dark defer magic". Combining does not rely on the transport protocol an the correct implementation of defer - but adds more complexity to the NC code and maybe higher load to the server.

So far - I don't know what is really better...

@LukasReschke
What do you mean with redirecting apps?

Maybe, the new combination approach can still be enhanced. According to the tests by @michaelletzgus, the approach decreased the number of requests from 198 to 117. However, 117 is still very much. Is the approach not yet applied consequently enough or is there something which prevents the number of requests to go down to below, e.g., 20 requests?

@michaelletzgus thanks for your approach! It worked fine on my nextcloud 11.0, but i updated to 11.0.3 and now my instance is slow again. How can i apply your patch on nextcloud 11.0.3?

just update to nextcloud 12 and everything works fine.

Native 12 does not include the defer mechanism, this is scheduled for 13.

You can patch your 11 or 12 instance by downloading pull 4854 as a patch:
cd <base directory of nextcloud>
wget https://patch-diff.githubusercontent.com/raw/nextcloud/server/pull/4854.patch

Then apply the downloaded patch file to your instance:
patch -p1 < 4854.patch

Thank you very much for this work. I'm still on NC11 and this makes a big difference. FYI 4854 doesn't apply cleanly on NC11 as a few lines in the layout.{base,guest,user}.php are in a different order. Easy to fix by hand though.

I guess the best option, for who can, is to hop and upgrade to NC12 ASAP. If I understand correctly NC12 includes JS combine work. If this is applied on top of it the benefit should be even bigger.

For NC11 you can apply #3696. ;)

Everyone, thanks for working on this. It is indeed needed to improve loading times.

Maybe many of you are on powerful computers or VPS, but just imagine being on lower end hardware, such as a Raspberry Pi. Think NextCloudPi/Nextcloudbox

From NextCloudPi, we are going steady for 1000~2000 downloads a day, which means at least hundreds of people trying Nextcloud everyday on SBCs. I get many complaints on how slow it is and I can tell that really puts people off. Many decide to stay with Dropbox for this reason.

This screenshot was taken from a NextCloudPi instance on a Raspberry Pi 2 after logging out and logging in again, so browser cache, PHP7, PHP cache, HTTP2 pipelining... and all the stuff

nc-slow

I develop and test on a QEMU ARM virtualized system... just imagine the pain x_x

I am glad to see movement on the right direction. This is just to make you guys aware of this user base that you might not be aware you have.

Good work, and please, don't forget users on low end devices!

edit: I know its a 35$ board, but as someone mentions before, it is quite capable of serving dynamic pages, and I've seen it load faster on PHP5 + owncloud than it does now, so the design can be improved (it is being improved already it seems)

Hey nachoparker. I didn't know the existence of your project (Nextcloudpi). I installed first owncloud and now nextcloud in my pi2 first and now in my pi3. I suppose that are a lot of people that install nextcloud in this machines for a single use. The server is cheap and the power consuption is very low.

For a perfect personal pi server i think that the project need a mail server. I installed iredmail to make the pi a full server, but i love the simplicity of mail-in-a-box. Please, consider this in future relases.

And yes... when the assets were enabled in previous versions, owncloud was very much faster that now in this machines. I think that this might be solved as soon as possible (is a know error and very evident to forget it).

Nice suggestions! I also want to add email self hosting to the whole thing, but haven't even started investigating.

Would you like to open a feature request / discussion here?

Hi guys,

i applied 4854.patch to my nextcloud 12.0.0, but this makes updating to 12.0.1 impossible.
How can i remove the patch or is there an updated version that works with 12.0.1?

Hi guys,

i applied 4854.patch to my nextcloud 12.0.0, but this makes updating to 12.0.1 impossible.
How can i remove the patch or is there an updated version that works with 12.0.1?

I removed the patch file in the NC directory and the update went well. I guess you'll have to reapply the patch after update though.

Sorry to resume this old thread. This is also not a direct issue of Nextcloud, but it's related. Just want to point out I think HTTP/2 is not really an option at the moment. I've tried with nginx and found it's not ready. HTTP/2 large downloads will simply deadlock. This is not a fault of nextcloud, it's the same with static files if they are big enough (>10 MB can be enough, but it's easier to see with > 100 MB or more).

Apache httpd works, but connection multiplexing doesn't work as I expected. When downloading a large file it's not possible to navigate nextcloud pages. I think (but am not sure at all) this is due to connection multiplexing in HTTP/2, which is done kind of incorrectly in this case. The new requested seems to wait for the big download to finish, but it's supposed to happen concurrently.

@enricotagliavini Downloading a 112 MB file worked pretty fine here (with nginx 1.13 and HTTP/2).

I have been using HTTP2 apache for many months, and it seems very much ready from here :P

@nachoparker so you can download a 10 GB file while surfing to other nextcloud pages at the same time? Which version of apache httpd if I may ask?

Could you please move this discussion to the forum or (if there's a bug) to a new issue? Everyone in this thread will be notified by your comments although it's been fixed. Thanks.

Was this page helpful?
0 / 5 - 0 ratings