Very low meteor performance in production

My comments will not be agreed by all.

Some of the parameters/ scores make relevance(or need to be prioritised ) depending on your app and its purpose. If you are using your meteor app site as a means of customer conversion or stickiness then yes quick loading is important otherwise they may loose interest.

In other words take a call based on your end goal. This does not mean performance is not important, it is upto you to decide which parameter your end user looks for and focus on it.

I found the link responsive and it seems fine. But then performance depends on so many other things and they change over a period of time.

Meteor Framework itself brings its constraint so focus on solving that is possible to be solved. And not all things have to be done on day one.

Also, note gtmetrix server is based out of Canada, not sure where your server is …it will add to latency.

3 Likes
  1. Use the bundle-visualizer package to find what is affecting how big your client bundle size is, which slows download speed.
  2. Use the chrome devtools profiler to see what is taking up time and CPU cycles while the app is rendering, it might just be taking awhile to render.
  3. Use MontiAPM to help analyse the response times of your server methods and subscriptions. It seems like waiting for data may be why it’s slow.
  4. Make sure any Mongo queries have proper indexes set up on the collections so your queries aren’t slow.
  5. Use Meteor.defer() and this.unblock() where possible in your methods to increase throughput.
2 Likes

HI @jkuester , Is this applicable to Blaze also?

@mixmatric yes, totally! I use Blaze and apply all of these techniques.

@afrokick
I did not understand the import dynamics exactly, if you can, follow the example below. thanks again .

import React from 'react';
import {render} from 'react-dom';
import {BrowserRouter as Router} from "react-router-dom";
import {HelmetProvider} from 'react-helmet-async';
import Routes from "../../Routes/Routes";
const helmetContext = {}
render(
    <HelmetProvider context={helmetContext}>
        <Router>
            <Routes/>
        </Router>
    </HelmetProvider>
    ,
    document.getElementById('App')
);


I’ve been working on dynamic import for the past couple of days, and this is an example of what I’ve just done,

A perfect example of dynamic import

Dynamic import of the specified route, For example: / user/center

const Routes = (props) => {
    const staticRouter = [
        <Route exact path="/" component={Home} key="s1"/>,
    ];

    const [loading, setLoading] = useState(false);
    const [routes, setRoutes] = useState(staticRouter);
    const account = props.location.pathname === '/user/center';

    const dynamicRouter = async () => {
        setLoading(true);
        if (account) {
            const dynamic = await import('./event/dynamic');
            const resultRouter = dynamic.Generate(props);
            setRoutes([...staticRouter, ...resultRouter]);
        } else {
            setRoutes([...staticRouter]);
        }
        setLoading(false);
    }

    useEffect(async () => {
        await dynamicRouter();
    }, [account]);

    if (loading) {
        return <Loading/>
    }

    return (
        <Layout>
            <Switch>
                {routes}
                <Route component={NotFound} key='n1'/>
            </Switch>
        </Layout>
    )

Dynamically import the contents of the routing file
./event/dynamic

import React from 'react';
import { Route } from 'react-router-dom';
import UCenter from '../../pages/UCenter';

export const Generate = props => {
    return [
        <Route path="/user/center" component={UCenter} {...props} key="d1"/>,
    ];
};

Refer to this post,go

See this post on how to tune meteor, go

Using bundle-visualizer I just managed to find a serious problem with react-icon, which made the packaged file shrink by 40% and reduced its speed by 1 second for the first opening and 600 milliseconds for the second opening

1 Like

However, I also have a problem to solve, using the above code, SSR function is invalid, SSR is not supported, according to the @nathanschwarz prompt, do not use react on the server side, but I do not understand, has not been successful

Here’s how he did it, go

1 Like

First of all, you should run bundle visualizer meteor --extra-packages bundle-visualizer --production and go to http://localhost:3000 to understand what exactly should be extracted from main js bundle.

Probably your ../../Routes/Routes file contains many imports, which can be dynamically loaded.

The second improvement which we’r now implementing - cut off React+React-dom from the main bundle and load it after DOM ready.

Lets see example, based on your code.

I move your code from project_name/client/index.jsx(maybe another file, IDK) to project_name/client/react.jsx;

project_name/client/react.jsx:

import React from 'react';
import {render} from 'react-dom';
import {BrowserRouter as Router} from "react-router-dom";
import {HelmetProvider} from 'react-helmet-async';
import Routes from "../../Routes/Routes";

const helmetContext = {};

render(
    <HelmetProvider context={helmetContext}>
        <Router>
            <Routes/>
        </Router>
    </HelmetProvider>
    ,
    document.getElementById('App')
);

Rename index.jsxindex.js and add a new code:

import { Meteor } from 'meteor/meteor';

// Custom splash screen/loading indicator
// You can use css animation, more complex HTML etc.
const splashScreen = document.createElement('div');
splashScreen = `Loading...`;

const showSplashScreen = () => {
  document.body.appendChild(splashScreen);
}

const hideSplashScreen = () => {
  document.body.removeChild(splashScreen);
}

Meteor.startup(async () => {
  showSplashScreen(); //also we can call it in DOMContentLoaded event

  await import('./react.jsx'); //import our react's stuff dynamically

  hideSplashScreen(); //hide loading indicator
});

react-dom should be removed from the main bundle after these changes. If not - it means that some meteor package uses it and should be lazy loaded, as I described above.

To understand my approach, I suggest to create an empty project and try to improve the bundle size.

For our project, we expect that bundle size should be 400-500kb after such optimisation, and our routes file with react-dom - another 200-300kb.

2 Likes

I followed exactly this example, but it did not change.
I checked the packages, packages like react-bootstrap and ostrio: files are relatively large.
Now exactly how can I implement them with lazy?
As I understand it, I do not have to do anything special, but that package must take the necessary measures.
Thank you for explaining on a package with an example

@saeeed you should import() it dynamically, for example with FlowRouter using .waitOn() method, like in our example repo loading all necessary files dynamically, and upon routing/navigation event, and even conditionally dynamic 404

2 Likes

Hi @dr.dimitru , Can you share some examples in blaze also?

1 Like

Also an example with a react router.
I created and used a collection exactly as follows :

Importing File Collection :

import EditorImg from "../../../../Collections/Files/Editor/EditorImg.js";

EditorImg Collection :

import { Meteor } from 'meteor/meteor';
import { FilesCollection } from 'meteor/ostrio:files';

const EditorImg = new FilesCollection({
    collectionName: 'EditorImg',
    storagePath : 'assets/uploads/editor/img/',
    allowClientCode: true, // Disallow remove files from Client
    onBeforeUpload(file) {
      // Allow upload files under 10MB, and only in png/jpg/jpeg formats
      if (file.size <= 1697400 && /png|jpg|jpeg/i.test(file.extension)) {
        return true;
      }
      return `please upload image, with size equal or less than 1MB ,,, ${file.size} `;
    }
});
export default EditorImg;

Now exactly how can I load this package lazy, of course I use a react router

I need an example and a full explanation about lazy.
How is it used in different packages?
How exactly should I start?
An example and explanation that can clarify the issue.

@mixmatric demo-app is entirely made of Blaze

1 Like

lazy is only for Atmosphere Package.
I’m curious how we can make NPM package lazy? :thinking:

For your own code use dynamic import().

What should be the idle size of the bundle?

In the matter of import dynamics and lazy packages, there are really few documents and I am confused

From what I’ve read so far it seems that people are getting relaxed with around 1.4-1.5MB and less, but there is no established hard limit.

Also mind that there is a related problem in Meteor, the bundled css. All css/scss that is not explicitly imported gets compiled into the css bundle, meaning that if you don’t import any, all of your css/scss get compiled into this bundle. It is then referenced in your main.html's <head> section via a <link> tag.

The problem with that is that such a css reference represents a blocking resource, which will ultimately delay the client app’s startup, thus adding to the time until FCP, First Contentful Paint, and, maybe even more importantly the LCP, Largest Contentful Paint. This is bad for SEO and should be taken very seriously since the introduction of Google’s Web Vitals.

Also see e.g.

LCP (largest contentful paint): The amount of time to render the largest content element visible in the viewport, from when the user requests the URL. The largest element is typically an image or video, or perhaps a large block-level text element. This is important because it tells the reader that the URL is actually loading.

LCP thresholds

  • <=2.5s: Good
  • <=4s Needs Improvement
  • >4s Poor
1 Like

Thanks for the information shared,

Attaching two Screenshots,

Can you please suggest the following things

  1. How can we improve Speed Index
  2. Currently the website is on Heroku, which comes with a machine having 512 MB ram, but not sure which parameter will get improvement in case I would change the RAM of the machine, please let me know if this would many any difference
  3. Regarding Reduce initial load time, here I observed that all the package we have in packages (inside the .meteor folder) combines and give us one single file. Is there any approach we can decide whether we want a particular package on a page?
1 Like

@mixmatric
The strategy of increasing RAM and CPU does not work.
I tried many times, it does not have much effect.
Of course I use prerender and nginx proxies it.
I’m not exactly sure if prerender might not work well.