"Lerna & Distributed Task Execution" Example

Overview

Lerna Distributed Task Execution (DTE) Example/Benchmark

On how to make your CI 23 times faster with a small config change

New versions of Lerna can use Nx (powerful build system) to execute tasks. This means that Lerna now supports configuration-free distributed task execution (via Nx and Nx Cloud)--you can execute any lerna run command across as many machines while preserving the dev ergonomics of running it on a single machine.

This repo illustrates how this works, and what performance benefits you get from it.

Since Lerna uses Nx to run tasks, in the context of running tasks, the words "Lerna" and "Nx" are interchangeable. If I say Lerna can do something, it means Nx can do it, and vise versa.

Repo Contains

  • 5 shared buildable packages/libraries with 250 components each
  • 5 Next.js applications built out of 20 app-specific libraries. Each app-specific lib has 250 components each. Each library uses the shared components.

Results

results

Running all the builds and tests in this repo on 1 machine takes about ~6h 30m, which is obviously unusable, so to make such a repo work, you have to distribute.

This repo enables Distributed Task Execution using Nx Cloud, which means that lerna run commands would run on multiple agents. It doesn't require us to change how our CI configuration is written or how our commands are executed. Enabling DTE and setting the number of agents is a small config change.

Nx Cloud is free for all OSS projects and for most closed-source projects

The repo shows what happens when we use a different number of agents per CI run. It's 8.7 times faster with 10 agents, and 23 times faster with 30 agents. You can allocate even more agents and reduce the CI time to about 7 minutes.

Note the dev ergonomics remained the same:

  • All build artifacts end up on the main agent
  • All logs are in one place
  • The dev ergonomics of using 30 agents is exactly the same as using a single agent.
  • Changing the number of agents is a 1-line PR.

Intelligent Distribution

Nx Cloud (used by Lerna here) knows what commands your CI is running. It also knows how many agents you typically use, and how long each task in your workspace typically takes. Nx Cloud uses this information to create an execution plan. For instance, it knows that tests do not depend on each other, whereas we need to build the shared libraries first. Nx knows that the theoretical limit of how fast your CI can get is slowest build of shared lib + slowest build of app, so it will prioritize building shared libs to unblock the apps.

This results in a somewhat even utilisation:
utilisation

After you run your CI a couple of times, Nx Cloud (used by Lerna) will learn stats about your workspace, and your CI will be more or less as fast as it can be given the provided agents. If you change the number of agents, it will rebalance the work. As you keep changing your repo, Nx Cloud will keep its understanding of it up to date and will keep your CI fast.

This all happens without you having to do anything.

What About Remote Caching?

Lerna supports remote computation caching, but it doesn't help this particular case. Remote caching ONLY helps with the average case, where some tasks are cached and some are not. In the worst case scenario nothing is cached. The only way to make the worst case scenario fast is to distribute. And you have to distribute. If you average CI time is 10 mins, but your worst case CI time (which say happens every couple of days) is 6 hours, it is still unusable.

Understanding Two Approaches to Distribution

Non-trivial repos always use more than one agent in CI to verify PRs. The bigger the repo is, the more agents you use (our clients’ biggest repos use 100+ agents for every CI run).

There are two ways to distribute work across agents: Sharding/Binning and Distributed Task Execution

Approach 1: Sharding/Binning

Binning is an approach to distribution where the planning job divides the work into equally-weighted bins, one for each worker job. Then every worker executes the work prepared for it. After that the post-verification job runs. This requires you to completely change your CI setup.

Approach 2: Distributed Task Execution

Distributed Task Execution is an approach where any command can be distributed and run on N machines instead of a single machine without changing the dev ergonomics or other assumptions about that command. This doesn't require you to change your CI setup.


diagram showing dte


Sharding/binning works for smaller repos (up to 10 agents), but even at that scale it has a lot of problems:

  • Agent utilization is uneven, so the CI is slower. And it requires manual rebalancing of tasks between agents.
  • Bad dev ergonomics (e.g., 10 different logs with potential failures).
  • It doesn't work for builds.

Sharding Doesn't Work for Builds?

The CI has to build shared libraries first because Next.js applications consume those libraries from "dist".

build deps

You cannot shard non-flat graphs, so you would have to rebuild shared libraries on all agents (which would make your CI much slower). If most of your libraries are buildable, it won’t work at all.

Read more about binning/sharding and DTE.

Could We Use Binning/Sharding?

If we were to change the repo to use binning/sharding, we would end up with:

  • 500-line complex CI config
  • Uneven utilization (so the CI time would be slower given the same number of agents)
  • Bad DX (we would see 30 different commands instead of 4 we logically have). Even with 30 agents, the sharding setup is not really usable.

Why No Other Build System Uses DTE?

Given everything mentioned, using distributed task execution is clearly much better. So why only Lerna and Nx support it, whereas so many tools support remote caching?

Remote caching is relatively easy to build (took me about 3 days to get the first working version up and running), and distributed task execution is hard (took us about a year), but the payoff is huge.

And there are several build systems supporting DTE. For instance, Bazel and Buck support DTE. They are used at Google and Facebook and some form of DTE is the only way to scale large repo. I have a lot of respect for the teams working on them but, if I’m honest, they don’t work well for the JS ecosystem.

To be honest, a lot of Nx (and Lerna) work is taking cool ideas developed by those talented teams and making them super easy to use.

Enabling DTE

In the current setup any lerna run command is distributable using Nx Cloud. It doesn't matter whether you build, test or lint, whether you use GitHub Actions or Jenkins. It works everywhere.

The easiest way to enable it is by using the GitHub Actions workflow (this is what this repo does).

name: CI

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  main:
    name: Main Job
    uses: nrwl/ci/.github/workflows/[email protected]
    with:
      parallel-commands-on-agents: |
        npx lerna run test
        npx lerna run build

  agents:
    name: Cloud - Agents
    uses: nrwl/ci/.github/workflows/[email protected]
    with:
      number-of-agents: 30

But the workflow is just syntactic sugar which can be extended into something like this:

name: LargeRepo

on:
  push:
    branches:
      - main
  pull_request:

env:
  NX_CLOUD_DISTRIBUTED_EXECUTION: 'true'

jobs:
  agents:
    name: Nx Cloud Agents
    runs-on: ubuntu-latest
    timeout-minutes: 60
    strategy:
      matrix:
        agent: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
      - run: npm install
      - name: Start Nx Agent ${{ matrix.agent }}
        run: npx nx-cloud start-agent

  main:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
      - run: npm install
      - name: Run verification
        uses: JamesHenry/[email protected]
        with:
          cmd1: npx lerna run test
          cmd2: npx lerna run build
      - run: npx nx-cloud stop-all-agents

You can find all the examples of setting it up here. There are guides for all major CI providers. Simply replace any "nx affected" command with "lerna run"--the rest will work the same way.

You might also like...

This is a nodejs implementation of the python example

DOC This is a nodejs implementation of the python example provided in https://github.com/EnAccess/OpenPAYGO-Token. In case of anything please feel fre

Jan 25, 2022

An simple blockchain example on node + typescript.

node-ts-blockchain An simple blockchain example on node + typescript. Next steps: Create a usefull README. Add a architecture to store the chain/block

May 30, 2022

Stream Chat Messaging Example App

Stream Chat Messaging Example App Is your core product something that would benefit from the increased engagement that comes from in-app chat? With St

Nov 20, 2022

Example blog for article about Remix.js

Welcome to Remix! Remix Docs Development From your terminal: npm run dev This starts your app in development mode, rebuilding assets on file changes.

Oct 16, 2022

Example CRUD API for API Fest'22. See Pull Requests for chapter 2 and 3

Example CRUD API for API Fest'22. See Pull Requests for chapter 2 and 3

Mar 2, 2022

Chrome Extensions are something which we use very frequently in our day-to-day life for example Grammarly

Chrome-Extension Chrome Extensions are something which we use very frequently in our day-to-day life for example Grammarly, In this project similarly,

Dec 14, 2022

Example TodoMVC app(s) to demo and learn Muban

Muban Todo MVC This repo contains multiple projects that showcase different ways of creating a Todo MVC app with Muban. It serves as an example, but a

Jul 9, 2022

React.js Login, Logout, Registration example with JWT and HttpOnly Cookie

React Login and Registration example with JWT and HttpOnly cookie For more detail, please visit: React Login and Registration example with JWT and Htt

Dec 24, 2022

NextJS with GraphQL using type-graphl and graphql-codegen adoptable dogs example code

This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn dev Open http://

Mar 31, 2022
Owner
Victor Savkin
Nrwl co-founder, ex-Googler. Work on dev tools for TS/JS. Nx and Nx Cloud creator. Calligraphy and philosophy enthusiast.
Victor Savkin
Example auto-generated OpenAPI client library and an accompanying example Angular app.

To utilize this demo Head into petstore_frontend\petes_pets Run npm install Go to frontend_client_lib\out Run npm install Head back into petstore_fron

Alan Gross 1 Jan 21, 2022
An example project to how to integrate Chat Engine into a marketplace

Add Chat to Your Marketplace This is a example online marketplace with Chat Engine fully integrated. It's meant to help people building online marketp

Adam La Morre 13 Dec 22, 2022
Example project implementing authentication, authorization, and routing with Next.js and Supabase

Magic Link Authentication and Route Controls with Supabase and Next.js To run this project, To get started with this project, first create a new proje

Nader Dabit 134 Dec 11, 2022
Example questions for a manager interview

Manager Interview Questions A list of interview questions for manager roles. Follow Ryan Burgess on Twitter Management How would you describe your man

Ryan Burgess 136 Dec 12, 2022
Example of a ceramic app to showcase dynamic NFT capability

This is an example application that uses Ceramic, based on Next.js. Fork it freely. Getting Started Install dependencies. Create local ENV file with y

Ceramic Studio 12 Sep 6, 2022
This is an VanillaJS SPA example with function based rendering.

Function-Based-Rendering This is an VanillaJS SPA example with function based rendering. Here's how to create Views by function composition and how to

강호형 3 Oct 16, 2021
This repository contains a basic example on how to set up and run test automation jobs with CircleCI and report results to Testmo.

CircleCI test automation example This repository contains a basic example on how to set up and run test automation jobs with CircleCI and report resul

Testmo 2 Dec 23, 2021
GatsbyContactFormBasedOnFormikAnalysis - This is example contact form for gatsby framework.

Gatsby minimal starter ?? Quick start Create a Gatsby site. Use the Gatsby CLI to create a new site, specifying the minimal starter. # create a new Ga

Bart 1 Jan 2, 2022
Example api, in Portuguese, to show very basic fundamentals of an express server.

Servidor de texto Aqui é um exemplo simples de como usar o express para servir um valor da memória do servidor para o front como resposta a cada requi

João Saraceni 2 Jan 7, 2022
A minimal norui portfolio example built with NextJS and powered by Notion 🚀.

Minimal Norui A minimal norui portfolio example built with NextJS and powered by Notion ?? . Links Demo Site Documentation Minimal Norui Notion Databa

Joeylene Rivera 4 Nov 13, 2022