Large SaaS migrated to 3.0

…and working well, so far, in tests.

We have a geospatial SaaS platform whose public facing app is built with Meteor.

This Meteor app is fairly big, with just over 100 packages built in-house, containing over 9,500 files, over half of which are JS files. Just migrating to async / await had meant prefixing 671 functions with async, and await-ing 2,816 function calls (the latter representing our own methods, mongo methods, accounts-password, etc.). This is besides the WebApp API changes, mongo collection hooks, and having to fork locally and migrate packages while waiting for official versions (some of which we are contributing to).

Sounds like a lot of work? Depends on what you think it’s a lot. All this took us roughly 140 developer hours to do. And — this is slightly embarrassing, but we are control freaks — most of the work was done without automated scripts. But we do use an excellent IDE (WebStorm) that highlights most un-awaited methods, and we have good tests coverage.

Personally, I would say that most of the hard work was done by the people at Meteor Software, and members of the community like @zodern, @radekmie, @jkuester, @storyteller, @filipenevola and so many others. For our part, we just had to change the code according to instructions and for the most part it actually worked.

So, the strategy was simple:

  • Early on (sometimes in 2022), we migrated all methods and functions containing HTTP calls to our other services (PostgreSQL, map tiling service, data importer, catalogues, etc) to async/await, just as the Meteor package http got deprecated. It forced us to convert a large part of our internal API without having to even touch the collection methods. We left the latter for another time, simply because there were too many unknowns at the time regarding some community packages.

  • Recently, as soon as the alpha 3.0 releases have become available, which meant we had access to new accounts-password and webapp APIs, we decided it’s time to move on with the second stage of migration. With the app on version 2.14, we migrated all the collection methods to their *Async counterparts. This was fairly easy, since the large majority of usage was inside methods and functions we already refactored above.

  • The dirty work stage. Upgraded to Meteor 3.0-beta.0 and left a minimal set of packages in the packages file, with the goal to at least get the app to boot.

    • Getting all sorts of helpful Fibers-related errors, we went and removed all instances of Fibers usage, sprinkling async / await in its place.
    • Myriad packages constraint errors were a bit of a hair raiser. The simple way to deal with those: 1) add packages incrementally, 2) update your own api.versionsFrom, 3) fork locally and quickly migrate whatever you need and it is not yet migrated by the community.
    • Set work in batches: the webapp batch, the accounts-* batch, the browser-policy-content batch, which means we went through the 3.0 changelog here and modified whatever we needed, one by one.

Challenges:

  • We maintain a pretty complex accounts management system, which enables Role-based access control (RBAC), user groups, and access control lists for a variety of activities, from logging in, SSO & LDAP, to inviting users for one-off map edits and field data collection. So we had to wait until v3.0 was available so we could get on with updating everything depending on Accounts hooks, custom login handlers, and such.

  • Some essential packages we had to fork locally and migrate:

    aumel:security-authorization
    storyteller:cdn
    reywood:publish-composite
    meteorhacks:meteorx
    meteorhacks:sikka
    meteorhacks:inject-initial
    simple:json-routes
    meteorhacks:ssr
    

    Some of them, particularly the meteorhacks ones, are quite important for a range of security and server-side black magic, so hopefully we can either 1) publish and maintain our versions, or 2) contribute the modifications to where they are being maintained. The best part is, they all have tests and they all pass after the latest updates :-). The not so good part is that in some cases, the test coverage simply does not account for new behaviour, so I’m sure we have some blind spots in there.

There are still some issues, but nothing insurmountable. All in all, this migration was scary when contemplating it early on, but quite a constructive and refreshing experience once we got our hands busy with it.

12 Likes

Thanks for an excellent write-up! Will be a lot of help once I get around to start updating to 3.0.

1 Like

Congrats @illustreets and thanks for sharing.

This phrase initially gave me a heart attack before reading the next line

Glad to see others pulling off this great feat. Honestly, it doesn’t seem too much of an undertaking.

Some of them, particularly the meteorhacks ones, are quite important for a range of security and server-side black magic, so hopefully we can either 1) publish and maintain our versions, or 2) contribute the modifications to where they are being maintained. The best part is, they all have tests and they all pass after the latest updates :-). The not so good part is that in some cases, the test coverage simply does not account for new behaviour, so I’m sure we have some blind spots in there.

Please don’t feel shy about sharing your work. It’d help out immensely in our migrations efforts and would encourage others to do the same once they know the community packages are still being maintained.

1 Like

Right, good idea @harry97

Feel free to do a PR for my CDN package, I will look at it shortly and release a new version.

Update: Did a quick release with some changes for better Meteor 3 migration:

2 Likes

That was quick! :slight_smile:

Quick question. This block has been in there for a while: https://github.com/StorytellerCZ/meteor-cdn/blob/master/lib/server.js#L95 — I’m wondering, why not use the CDN for CSS files?

It looks like this is a fork of zodern:cdn, so I can answer the question.

The package was modified to be used along with mup-cloud-front, which was sponsored by Hive. When using cloud front, during a deploy the requests for static files could go to either a server running the new version, or a server running the old version. If it goes to the wrong version, the file will be missing and the client will be broken.

To work around this, mup-cloud-front uploads the static files to s3, using a different folder for each version, and has cloud front load the files from there. Since the files for both versions are available, this fixes the problem. The version is added to the path for the static assets by zodern:cdn. This works fine for most files, but changing the path can break some css files.

Thank you.

This makes sense. However, organisations like ours are forced to maintain a local fork just to be able to load the CSS over the CDN. I would like to send a pull request with an optional configuration option to switch on CSS support. Would that work?

I don’t see any reason that wouldn’t work.

It might be possible to only disable css when there is a cdnVersion, though that might not work for people that use a similar setup without mup-cloud-front.

Great! I’ll work on it. With your permission, I would like to reword your above explanation a little and add it to the README. This is to explain why CSS is not loaded in the first place and to introduce the configuration option.

Agree. An explicit configuration option is the safest approach and works for everyone.

1 Like

That would be fine. The mup-cloud-front readme might have a better explanation for parts of it.

We use styled-components, so we don’t have css files. But if you have a PR that adds the capability then I’m happy to merge it and publish a new version.

Here we go: Configurable exclusions by manueltimita · Pull Request #1 · StorytellerCZ/meteor-cdn · GitHub

3 Likes

Hi, what is your new package name for meteorhacks:ssr? I´d really like to use the package to send the emails in HTML, and avoid refactoring my current code.

nvm, I’ve found this package compatible with Meteor 3:

Thanks @harry97 !

2 Likes

Hi, long holiday here, hence the silence. My version of this package was not published, and not on Git either. Just modified locally. So, @harry97’s package worked, I take it?