Repository: firebase/firebase-tools (ts)

Issue:
Importing data via emulator UI causes massive memory usage
<!-- DO NOT DELETE
validate_template=true
template_path=.github/ISSUE_TEMPLATE/bug_report.md
-->

### [REQUIRED] Environment info

<!-- What version of the Firebase CLI (`firebase-tools`) are you using? Note that your issue may already be fixed in the latest versions. The latest version can be found at https://github.com/firebase/firebase-tools/releases -->

<!-- Output of `firebase --version` -->

**firebase-tools:** 8.4.0

<!-- e.g. macOS, Windows, Ubuntu -->

**Platform:** Arch Linux

### [REQUIRED] Test case

<!-- Provide a minimal, complete, and verifiable example (http://stackoverflow.com/help/mcve) -->
Export a modest dataset from an existing project. Im my case RTDB json export is 7.5 MB, and my functions count is about 95, with approximately 75% of those are triggers of some sort. 
### [REQUIRED] Steps to reproduce

<!-- Provide the steps needed to reproduce the issue with the above test case. -->
Initialize the emulator for RTDB and functions, then start emulator. Import a 7.5mb json file into RTDB. Memory starts increasing at a rate of about 1gb per second, until my OOM manager starts killing processes.  My project has about 95 functions, and 75% of those are database triggers. My assumption is that a lot of triggers are firing simultaneously.
### [REQUIRED] Expected behavior

<!-- What is the expected behavior? -->
This may be a bug, but a feature would be a bootstrap mode that lets me import data without any database triggers firing.

### [REQUIRED] Actual behavior

<!-- Run the command with --debug flag, and include the logs below. -->
The emulator itself continues to run, by my OOM manager starts killing processes to try to accomodate the memory usage, until pretty much all processes have been killed. The app itself never throws any errors. 

Pull request description:
Limit functions work queue size
<!--

Thank you for contributing to the Firebase community! Please fill out the form below.

Run the linter and test suite
==============================
Run `npm test` to make sure your changes compile properly and the tests all pass on your local machine. We've hooked up this repo with continuous integration to double check those things for you.

-->

### Description

Fixes #2277 by limiting how much parallel work the functions queue will do at one time.  Introduces a (non-documented) env override in case someone has a good reason to want to do a ton of work.

### Scenarios Tested

For all scenarios I am using these functions:
```js
const functions = require('firebase-functions');
const { PubSub } = require('@google-cloud/pubsub');

const pubsub = new PubSub();

exports.launchWorkers = functions.https.onRequest(async (request, response) => {
  for (let i = 0; i < 100; i++) {
    console.log(`Publishing ${i}`);
    await pubsub.topic('dowork').publishJSON({
      count: i
    });
  }
  response.send("Hello from Firebase!");
});

exports.worker = functions.pubsub.topic("dowork").onPublish(async (msg, ctx) => {
  const count = msg.json.count;
  console.log(`[${count}] Doing some work...`);
  await new Promise(res => setTimeout(res, 2500));
  console.log(`[${count}] Done!`);
});
```

**Basic use**
```
$ firebase emulators:start --debug | grep work-queue
[2020-06-02T15:31:46.706Z] [work-queue] {"queueLength":1,"workRunningCount":0}
[2020-06-02T15:31:46.707Z] [work-queue] {"queueLength":0,"workRunningCount":1}
[2020-06-02T15:31:47.362Z] [work-queue] {"queueLength":1,"workRunningCount":1}
[2020-06-02T15:31:47.362Z] [work-queue] {"queueLength":0,"workRunningCount":2}
[2020-06-02T15:31:47.376Z] [work-queue] {"queueLength":1,"workRunningCount":2}
[2020-06-02T15:31:47.376Z] [work-queue] {"queueLength":0,"workRunningCount":3}
[2020-06-02T15:31:47.387Z] [work-queue] {"queueLength":1,"workRunningCount":3}
[2020-06-02T15:31:47.387Z] [work-queue] {"queueLength":0,"workRunningCount":4}
[2020-06-02T15:31:47.411Z] [work-queue] {"queueLength":1,"workRunningCount":4}
[2020-06-02T15:31:47.411Z] [work-queue] {"queueLength":0,"workRunningCount":5}
[2020-06-02T15:31:47.418Z] [work-queue] {"queueLength":1,"workRunningCount":5}
[2020-06-02T15:31:47.419Z] [work-queue] {"queueLength":0,"workRunningCount":6}
[2020-06-02T15:31:47.434Z] [work-queue] {"queueLength":1,"workRunningCount":6}
[2020-06-02T15:31:47.434Z] [work-queue] {"queueLength":0,"workRunningCount":7}
[2020-06-02T15:31:47.468Z] [work-queue] {"queueLength":1,"workRunningCount":7}
[2020-06-02T15:31:47.469Z] [work-queue] {"queueLength":0,"workRunningCount":8}
[2020-06-02T15:31:47.491Z] [work-queue] {"queueLength":1,"workRunningCount":8}
[2020-06-02T15:31:47.492Z] [work-queue] {"queueLength":0,"workRunningCount":9}
[2020-06-02T15:31:47.513Z] [work-queue] {"queueLength":1,"workRunningCount":9}
[2020-06-02T15:31:47.513Z] [work-queue] {"queueLength":0,"workRunningCount":10}
[2020-06-02T15:31:47.526Z] [work-queue] {"queueLength":1,"workRunningCount":10}
[2020-06-02T15:31:47.526Z] [work-queue] {"queueLength":0,"workRunningCount":11}
[2020-06-02T15:31:47.540Z] [work-queue] {"queueLength":1,"workRunningCount":11}
[2020-06-02T15:31:47.541Z] [work-queue] {"queueLength":0,"workRunningCount":12}
[2020-06-02T15:31:47.570Z] [work-queue] {"queueLength":1,"workRunningCount":12}
[2020-06-02T15:31:47.572Z] [work-queue] {"queueLength":0,"workRunningCount":13}
[2020-06-02T15:31:47.658Z] [work-queue] {"queueLength":1,"workRunningCount":13}
[2020-06-02T15:31:47.658Z] [work-queue] {"queueLength":0,"workRunningCount":14}
[2020-06-02T15:31:47.684Z] [work-queue] {"queueLength":1,"workRunningCount":14}
[2020-06-02T15:31:47.685Z] [work-queue] {"queueLength":0,"workRunningCount":15}
[2020-06-02T15:31:47.694Z] [work-queue] {"queueLength":1,"workRunningCount":15}
[2020-06-02T15:31:47.694Z] [work-queue] {"queueLength":0,"workRunningCount":16}
[2020-06-02T15:31:47.735Z] [work-queue] {"queueLength":1,"workRunningCount":16}
[2020-06-02T15:31:47.739Z] [work-queue] {"queueLength":0,"workRunningCount":17}
[2020-06-02T15:31:47.762Z] [work-queue] {"queueLength":1,"workRunningCount":17}
[2020-06-02T15:31:47.762Z] [work-queue] {"queueLength":0,"workRunningCount":18}
[2020-06-02T15:31:47.809Z] [work-queue] {"queueLength":1,"workRunningCount":18}
[2020-06-02T15:31:47.809Z] [work-queue] {"queueLength":0,"workRunningCount":19}
[2020-06-02T15:31:47.818Z] [work-queue] {"queueLength":1,"workRunningCount":19}
[2020-06-02T15:31:47.818Z] [work-queue] {"queueLength":0,"workRunningCount":20}
[2020-06-02T15:31:47.973Z] [work-queue] {"queueLength":1,"workRunningCount":20}
[2020-06-02T15:31:47.974Z] [work-queue] {"queueLength":0,"workRunningCount":21}
[2020-06-02T15:31:48.106Z] [work-queue] {"queueLength":1,"workRunningCount":21}
[2020-06-02T15:31:48.107Z] [work-queue] {"queueLength":0,"workRunningCount":22}
[2020-06-02T15:31:48.119Z] [work-queue] {"queueLength":1,"workRunningCount":22}
[2020-06-02T15:31:48.119Z] [work-queue] {"queueLength":0,"workRunningCount":23}
[2020-06-02T15:31:48.130Z] [work-queue] {"queueLength":1,"workRunningCount":23}
[2020-06-02T15:31:48.130Z] [work-queue] {"queueLength":0,"workRunningCount":24}
[2020-06-02T15:31:48.173Z] [work-queue] {"queueLength":1,"workRunningCount":24}
[2020-06-02T15:31:48.173Z] [work-queue] {"queueLength":0,"workRunningCount":25}
[2020-06-02T15:31:48.195Z] [work-queue] {"queueLength":1,"workRunningCount":25}
[2020-06-02T15:31:48.195Z] [work-queue] {"queueLength":0,"workRunningCount":26}
[2020-06-02T15:31:48.205Z] [work-queue] {"queueLength":1,"workRunningCount":26}
[2020-06-02T15:31:48.205Z] [work-queue] {"queueLength":0,"workRunningCount":27}
[2020-06-02T15:31:48.451Z] [work-queue] {"queueLength":1,"workRunningCount":27}
[2020-06-02T15:31:48.451Z] [work-queue] {"queueLength":0,"workRunningCount":28}
[2020-06-02T15:31:48.518Z] [work-queue] {"queueLength":1,"workRunningCount":28}
[2020-06-02T15:31:48.518Z] [work-queue] {"queueLength":0,"workRunningCount":29}
[2020-06-02T15:31:48.542Z] [work-queue] {"queueLength":1,"workRunningCount":29}
[2020-06-02T15:31:48.542Z] [work-queue] {"queueLength":0,"workRunningCount":30}
[2020-06-02T15:31:48.832Z] [work-queue] {"queueLength":1,"workRunningCount":30}
[2020-06-02T15:31:48.832Z] [work-queue] {"queueLength":0,"workRunningCount":31}
[2020-06-02T15:31:48.842Z] [work-queue] {"queueLength":1,"workRunningCount":31}
[2020-06-02T15:31:48.843Z] [work-queue] {"queueLength":0,"workRunningCount":32}
[2020-06-02T15:31:48.852Z] [work-queue] {"queueLength":1,"workRunningCount":32}
[2020-06-02T15:31:48.852Z] [work-queue] {"queueLength":0,"workRunningCount":33}
[2020-06-02T15:31:48.861Z] [work-queue] {"queueLength":1,"workRunningCount":33}
[2020-06-02T15:31:48.861Z] [work-queue] {"queueLength":0,"workRunningCount":34}
[2020-06-02T15:31:48.870Z] [work-queue] {"queueLength":1,"workRunningCount":34}
[2020-06-02T15:31:48.870Z] [work-queue] {"queueLength":0,"workRunningCount":35}
[2020-06-02T15:31:48.880Z] [work-queue] {"queueLength":1,"workRunningCount":35}
[2020-06-02T15:31:48.880Z] [work-queue] {"queueLength":0,"workRunningCount":36}
[2020-06-02T15:31:48.888Z] [work-queue] {"queueLength":1,"workRunningCount":36}
[2020-06-02T15:31:48.888Z] [work-queue] {"queueLength":0,"workRunningCount":37}
[2020-06-02T15:31:48.894Z] [work-queue] {"queueLength":1,"workRunningCount":37}
[2020-06-02T15:31:48.894Z] [work-queue] {"queueLength":0,"workRunningCount":38}
[2020-06-02T15:31:48.901Z] [work-queue] {"queueLength":1,"workRunningCount":38}
[2020-06-02T15:31:48.901Z] [work-queue] {"queueLength":0,"workRunningCount":39}
[2020-06-02T15:31:48.909Z] [work-queue] {"queueLength":1,"workRunningCount":39}
[2020-06-02T15:31:48.909Z] [work-queue] {"queueLength":0,"workRunningCount":40}
[2020-06-02T15:31:48.916Z] [work-queue] {"queueLength":1,"workRunningCount":40}
[2020-06-02T15:31:48.916Z] [work-queue] {"queueLength":0,"workRunningCount":41}
[2020-06-02T15:31:48.927Z] [work-queue] {"queueLength":1,"workRunningCount":41}
[2020-06-02T15:31:48.927Z] [work-queue] {"queueLength":0,"workRunningCount":42}
[2020-06-02T15:31:48.937Z] [work-queue] {"queueLength":1,"workRunningCount":42}
[2020-06-02T15:31:48.937Z] [work-queue] {"queueLength":0,"workRunningCount":43}
[2020-06-02T15:31:48.946Z] [work-queue] {"queueLength":1,"workRunningCount":43}
[2020-06-02T15:31:48.946Z] [work-queue] {"queueLength":0,"workRunningCount":44}
[2020-06-02T15:31:49.416Z] [work-queue] {"queueLength":1,"workRunningCount":44}
[2020-06-02T15:31:49.416Z] [work-queue] {"queueLength":0,"workRunningCount":45}
[2020-06-02T15:31:49.425Z] [work-queue] {"queueLength":1,"workRunningCount":45}
[2020-06-02T15:31:49.425Z] [work-queue] {"queueLength":0,"workRunningCount":46}
[2020-06-02T15:31:49.436Z] [work-queue] {"queueLength":1,"workRunningCount":46}
[2020-06-02T15:31:49.436Z] [work-queue] {"queueLength":0,"workRunningCount":47}
[2020-06-02T15:31:49.447Z] [work-queue] {"queueLength":1,"workRunningCount":47}
[2020-06-02T15:31:49.447Z] [work-queue] {"queueLength":0,"workRunningCount":48}
[2020-06-02T15:31:49.628Z] [work-queue] {"queueLength":0,"workRunningCount":47}
[2020-06-02T15:31:49.640Z] [work-queue] {"queueLength":1,"workRunningCount":47}
[2020-06-02T15:31:49.641Z] [work-queue] {"queueLength":0,"workRunningCount":48}
[2020-06-02T15:31:49.650Z] [work-queue] {"queueLength":1,"workRunningCount":48}
[2020-06-02T15:31:49.650Z] [work-queue] {"queueLength":0,"workRunningCount":49}
[2020-06-02T15:31:49.657Z] [work-queue] {"queueLength":1,"workRunningCount":49}
[2020-06-02T15:31:49.657Z] [work-queue] {"queueLength":0,"workRunningCount":50}
[2020-06-02T15:31:49.664Z] [work-queue] {"queueLength":1,"workRunningCount":50}
[2020-06-02T15:31:49.664Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:49.664Z] [work-queue] {"queueLength":2,"workRunningCount":50}
[2020-06-02T15:31:49.665Z] [work-queue] {"queueLength":3,"workRunningCount":50}
[2020-06-02T15:31:49.665Z] [work-queue] {"queueLength":4,"workRunningCount":50}
[2020-06-02T15:31:49.665Z] [work-queue] {"queueLength":5,"workRunningCount":50}
[2020-06-02T15:31:49.678Z] [work-queue] {"queueLength":6,"workRunningCount":50}
[2020-06-02T15:31:49.678Z] [work-queue] {"queueLength":7,"workRunningCount":50}
[2020-06-02T15:31:49.679Z] [work-queue] {"queueLength":8,"workRunningCount":50}
[2020-06-02T15:31:49.679Z] [work-queue] {"queueLength":9,"workRunningCount":50}
[2020-06-02T15:31:49.679Z] [work-queue] {"queueLength":10,"workRunningCount":50}
[2020-06-02T15:31:49.679Z] [work-queue] {"queueLength":11,"workRunningCount":50}
[2020-06-02T15:31:49.679Z] [work-queue] {"queueLength":12,"workRunningCount":50}
[2020-06-02T15:31:49.679Z] [work-queue] {"queueLength":13,"workRunningCount":50}
[2020-06-02T15:31:49.680Z] [work-queue] {"queueLength":14,"workRunningCount":50}
[2020-06-02T15:31:49.680Z] [work-queue] {"queueLength":15,"workRunningCount":50}
[2020-06-02T15:31:49.680Z] [work-queue] {"queueLength":16,"workRunningCount":50}
[2020-06-02T15:31:49.680Z] [work-queue] {"queueLength":17,"workRunningCount":50}
[2020-06-02T15:31:49.680Z] [work-queue] {"queueLength":18,"workRunningCount":50}
[2020-06-02T15:31:49.680Z] [work-queue] {"queueLength":19,"workRunningCount":50}
[2020-06-02T15:31:49.681Z] [work-queue] {"queueLength":20,"workRunningCount":50}
[2020-06-02T15:31:49.681Z] [work-queue] {"queueLength":21,"workRunningCount":50}
[2020-06-02T15:31:49.681Z] [work-queue] {"queueLength":22,"workRunningCount":50}
[2020-06-02T15:31:49.681Z] [work-queue] {"queueLength":23,"workRunningCount":50}
[2020-06-02T15:31:49.681Z] [work-queue] {"queueLength":24,"workRunningCount":50}
[2020-06-02T15:31:49.681Z] [work-queue] {"queueLength":25,"workRunningCount":50}
[2020-06-02T15:31:49.681Z] [work-queue] {"queueLength":26,"workRunningCount":50}
[2020-06-02T15:31:49.682Z] [work-queue] {"queueLength":27,"workRunningCount":50}
[2020-06-02T15:31:49.682Z] [work-queue] {"queueLength":28,"workRunningCount":50}
[2020-06-02T15:31:49.682Z] [work-queue] {"queueLength":29,"workRunningCount":50}
[2020-06-02T15:31:49.682Z] [work-queue] {"queueLength":30,"workRunningCount":50}
[2020-06-02T15:31:49.682Z] [work-queue] {"queueLength":31,"workRunningCount":50}
[2020-06-02T15:31:49.684Z] [work-queue] {"queueLength":32,"workRunningCount":50}
[2020-06-02T15:31:49.684Z] [work-queue] {"queueLength":33,"workRunningCount":50}
[2020-06-02T15:31:49.684Z] [work-queue] {"queueLength":34,"workRunningCount":50}
[2020-06-02T15:31:49.684Z] [work-queue] {"queueLength":35,"workRunningCount":50}
[2020-06-02T15:31:49.684Z] [work-queue] {"queueLength":36,"workRunningCount":50}
[2020-06-02T15:31:49.684Z] [work-queue] {"queueLength":37,"workRunningCount":50}
[2020-06-02T15:31:49.685Z] [work-queue] {"queueLength":38,"workRunningCount":50}
[2020-06-02T15:31:49.685Z] [work-queue] {"queueLength":39,"workRunningCount":50}
[2020-06-02T15:31:49.850Z] [work-queue] {"queueLength":40,"workRunningCount":50}
[2020-06-02T15:31:49.850Z] [work-queue] {"queueLength":41,"workRunningCount":50}
[2020-06-02T15:31:49.851Z] [work-queue] {"queueLength":42,"workRunningCount":50}
[2020-06-02T15:31:49.851Z] [work-queue] {"queueLength":43,"workRunningCount":50}
[2020-06-02T15:31:49.851Z] [work-queue] {"queueLength":44,"workRunningCount":50}
[2020-06-02T15:31:49.968Z] [work-queue] {"queueLength":45,"workRunningCount":50}
[2020-06-02T15:31:49.968Z] [work-queue] {"queueLength":46,"workRunningCount":50}
[2020-06-02T15:31:49.968Z] [work-queue] {"queueLength":47,"workRunningCount":50}
[2020-06-02T15:31:49.969Z] [work-queue] {"queueLength":48,"workRunningCount":50}
[2020-06-02T15:31:49.969Z] [work-queue] {"queueLength":49,"workRunningCount":50}
[2020-06-02T15:31:49.969Z] [work-queue] {"queueLength":50,"workRunningCount":50}
[2020-06-02T15:31:54.979Z] [work-queue] {"queueLength":50,"workRunningCount":49}
[2020-06-02T15:31:54.979Z] [work-queue] {"queueLength":49,"workRunningCount":50}
[2020-06-02T15:31:54.980Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:55.231Z] [work-queue] {"queueLength":49,"workRunningCount":49}
[2020-06-02T15:31:55.232Z] [work-queue] {"queueLength":48,"workRunningCount":50}
[2020-06-02T15:31:55.234Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:55.268Z] [work-queue] {"queueLength":48,"workRunningCount":49}
[2020-06-02T15:31:55.268Z] [work-queue] {"queueLength":47,"workRunningCount":50}
[2020-06-02T15:31:55.270Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:55.648Z] [work-queue] {"queueLength":47,"workRunningCount":49}
[2020-06-02T15:31:55.648Z] [work-queue] {"queueLength":46,"workRunningCount":50}
[2020-06-02T15:31:55.650Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:55.821Z] [work-queue] {"queueLength":46,"workRunningCount":49}
[2020-06-02T15:31:55.821Z] [work-queue] {"queueLength":45,"workRunningCount":50}
[2020-06-02T15:31:55.822Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:55.967Z] [work-queue] {"queueLength":45,"workRunningCount":49}
[2020-06-02T15:31:55.967Z] [work-queue] {"queueLength":44,"workRunningCount":50}
[2020-06-02T15:31:55.973Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.036Z] [work-queue] {"queueLength":44,"workRunningCount":49}
[2020-06-02T15:31:56.036Z] [work-queue] {"queueLength":43,"workRunningCount":50}
[2020-06-02T15:31:56.037Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.485Z] [work-queue] {"queueLength":43,"workRunningCount":49}
[2020-06-02T15:31:56.485Z] [work-queue] {"queueLength":42,"workRunningCount":50}
[2020-06-02T15:31:56.487Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.536Z] [work-queue] {"queueLength":42,"workRunningCount":49}
[2020-06-02T15:31:56.536Z] [work-queue] {"queueLength":41,"workRunningCount":50}
[2020-06-02T15:31:56.538Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.583Z] [work-queue] {"queueLength":41,"workRunningCount":49}
[2020-06-02T15:31:56.584Z] [work-queue] {"queueLength":40,"workRunningCount":50}
[2020-06-02T15:31:56.585Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.606Z] [work-queue] {"queueLength":40,"workRunningCount":49}
[2020-06-02T15:31:56.606Z] [work-queue] {"queueLength":39,"workRunningCount":50}
[2020-06-02T15:31:56.607Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.614Z] [work-queue] {"queueLength":39,"workRunningCount":49}
[2020-06-02T15:31:56.614Z] [work-queue] {"queueLength":38,"workRunningCount":50}
[2020-06-02T15:31:56.615Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.620Z] [work-queue] {"queueLength":38,"workRunningCount":49}
[2020-06-02T15:31:56.620Z] [work-queue] {"queueLength":37,"workRunningCount":50}
[2020-06-02T15:31:56.620Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.747Z] [work-queue] {"queueLength":37,"workRunningCount":49}
[2020-06-02T15:31:56.747Z] [work-queue] {"queueLength":36,"workRunningCount":50}
[2020-06-02T15:31:56.748Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.788Z] [work-queue] {"queueLength":36,"workRunningCount":49}
[2020-06-02T15:31:56.788Z] [work-queue] {"queueLength":35,"workRunningCount":50}
[2020-06-02T15:31:56.789Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.837Z] [work-queue] {"queueLength":35,"workRunningCount":49}
[2020-06-02T15:31:56.837Z] [work-queue] {"queueLength":34,"workRunningCount":50}
[2020-06-02T15:31:56.838Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.839Z] [work-queue] {"queueLength":34,"workRunningCount":49}
[2020-06-02T15:31:56.839Z] [work-queue] {"queueLength":33,"workRunningCount":50}
[2020-06-02T15:31:56.840Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.881Z] [work-queue] {"queueLength":33,"workRunningCount":49}
[2020-06-02T15:31:56.881Z] [work-queue] {"queueLength":32,"workRunningCount":50}
[2020-06-02T15:31:56.882Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:56.926Z] [work-queue] {"queueLength":32,"workRunningCount":49}
[2020-06-02T15:31:56.926Z] [work-queue] {"queueLength":31,"workRunningCount":50}
[2020-06-02T15:31:56.927Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.104Z] [work-queue] {"queueLength":31,"workRunningCount":49}
[2020-06-02T15:31:57.104Z] [work-queue] {"queueLength":30,"workRunningCount":50}
[2020-06-02T15:31:57.105Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.281Z] [work-queue] {"queueLength":30,"workRunningCount":49}
[2020-06-02T15:31:57.281Z] [work-queue] {"queueLength":29,"workRunningCount":50}
[2020-06-02T15:31:57.282Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.311Z] [work-queue] {"queueLength":29,"workRunningCount":49}
[2020-06-02T15:31:57.311Z] [work-queue] {"queueLength":28,"workRunningCount":50}
[2020-06-02T15:31:57.312Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.366Z] [work-queue] {"queueLength":28,"workRunningCount":49}
[2020-06-02T15:31:57.366Z] [work-queue] {"queueLength":27,"workRunningCount":50}
[2020-06-02T15:31:57.367Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.491Z] [work-queue] {"queueLength":27,"workRunningCount":49}
[2020-06-02T15:31:57.491Z] [work-queue] {"queueLength":26,"workRunningCount":50}
[2020-06-02T15:31:57.492Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.493Z] [work-queue] {"queueLength":26,"workRunningCount":49}
[2020-06-02T15:31:57.493Z] [work-queue] {"queueLength":25,"workRunningCount":50}
[2020-06-02T15:31:57.494Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.538Z] [work-queue] {"queueLength":25,"workRunningCount":49}
[2020-06-02T15:31:57.538Z] [work-queue] {"queueLength":24,"workRunningCount":50}
[2020-06-02T15:31:57.538Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.627Z] [work-queue] {"queueLength":24,"workRunningCount":49}
[2020-06-02T15:31:57.627Z] [work-queue] {"queueLength":23,"workRunningCount":50}
[2020-06-02T15:31:57.628Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.704Z] [work-queue] {"queueLength":23,"workRunningCount":49}
[2020-06-02T15:31:57.704Z] [work-queue] {"queueLength":22,"workRunningCount":50}
[2020-06-02T15:31:57.705Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.741Z] [work-queue] {"queueLength":22,"workRunningCount":49}
[2020-06-02T15:31:57.741Z] [work-queue] {"queueLength":21,"workRunningCount":50}
[2020-06-02T15:31:57.741Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.747Z] [work-queue] {"queueLength":21,"workRunningCount":49}
[2020-06-02T15:31:57.747Z] [work-queue] {"queueLength":20,"workRunningCount":50}
[2020-06-02T15:31:57.748Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.780Z] [work-queue] {"queueLength":20,"workRunningCount":49}
[2020-06-02T15:31:57.780Z] [work-queue] {"queueLength":19,"workRunningCount":50}
[2020-06-02T15:31:57.782Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.878Z] [work-queue] {"queueLength":19,"workRunningCount":49}
[2020-06-02T15:31:57.878Z] [work-queue] {"queueLength":18,"workRunningCount":50}
[2020-06-02T15:31:57.879Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.923Z] [work-queue] {"queueLength":18,"workRunningCount":49}
[2020-06-02T15:31:57.923Z] [work-queue] {"queueLength":17,"workRunningCount":50}
[2020-06-02T15:31:57.924Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.927Z] [work-queue] {"queueLength":17,"workRunningCount":49}
[2020-06-02T15:31:57.927Z] [work-queue] {"queueLength":16,"workRunningCount":50}
[2020-06-02T15:31:57.928Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:57.982Z] [work-queue] {"queueLength":16,"workRunningCount":49}
[2020-06-02T15:31:57.983Z] [work-queue] {"queueLength":15,"workRunningCount":50}
[2020-06-02T15:31:57.985Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.042Z] [work-queue] {"queueLength":15,"workRunningCount":49}
[2020-06-02T15:31:58.042Z] [work-queue] {"queueLength":14,"workRunningCount":50}
[2020-06-02T15:31:58.043Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.055Z] [work-queue] {"queueLength":14,"workRunningCount":49}
[2020-06-02T15:31:58.055Z] [work-queue] {"queueLength":13,"workRunningCount":50}
[2020-06-02T15:31:58.056Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.057Z] [work-queue] {"queueLength":13,"workRunningCount":49}
[2020-06-02T15:31:58.057Z] [work-queue] {"queueLength":12,"workRunningCount":50}
[2020-06-02T15:31:58.058Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.064Z] [work-queue] {"queueLength":12,"workRunningCount":49}
[2020-06-02T15:31:58.064Z] [work-queue] {"queueLength":11,"workRunningCount":50}
[2020-06-02T15:31:58.065Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.079Z] [work-queue] {"queueLength":11,"workRunningCount":49}
[2020-06-02T15:31:58.079Z] [work-queue] {"queueLength":10,"workRunningCount":50}
[2020-06-02T15:31:58.080Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.080Z] [work-queue] {"queueLength":10,"workRunningCount":49}
[2020-06-02T15:31:58.081Z] [work-queue] {"queueLength":9,"workRunningCount":50}
[2020-06-02T15:31:58.081Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.082Z] [work-queue] {"queueLength":9,"workRunningCount":49}
[2020-06-02T15:31:58.082Z] [work-queue] {"queueLength":8,"workRunningCount":50}
[2020-06-02T15:31:58.083Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.109Z] [work-queue] {"queueLength":8,"workRunningCount":49}
[2020-06-02T15:31:58.109Z] [work-queue] {"queueLength":7,"workRunningCount":50}
[2020-06-02T15:31:58.110Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.163Z] [work-queue] {"queueLength":7,"workRunningCount":49}
[2020-06-02T15:31:58.163Z] [work-queue] {"queueLength":6,"workRunningCount":50}
[2020-06-02T15:31:58.164Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.184Z] [work-queue] {"queueLength":6,"workRunningCount":49}
[2020-06-02T15:31:58.184Z] [work-queue] {"queueLength":5,"workRunningCount":50}
[2020-06-02T15:31:58.184Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.190Z] [work-queue] {"queueLength":5,"workRunningCount":49}
[2020-06-02T15:31:58.190Z] [work-queue] {"queueLength":4,"workRunningCount":50}
[2020-06-02T15:31:58.191Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.243Z] [work-queue] {"queueLength":4,"workRunningCount":49}
[2020-06-02T15:31:58.243Z] [work-queue] {"queueLength":3,"workRunningCount":50}
[2020-06-02T15:31:58.244Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.245Z] [work-queue] {"queueLength":3,"workRunningCount":49}
[2020-06-02T15:31:58.245Z] [work-queue] {"queueLength":2,"workRunningCount":50}
[2020-06-02T15:31:58.246Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.280Z] [work-queue] {"queueLength":2,"workRunningCount":49}
[2020-06-02T15:31:58.280Z] [work-queue] {"queueLength":1,"workRunningCount":50}
[2020-06-02T15:31:58.281Z] [work-queue] waiting for work to finish (running=50)
[2020-06-02T15:31:58.284Z] [work-queue] {"queueLength":1,"workRunningCount":49}
[2020-06-02T15:31:58.284Z] [work-queue] {"queueLength":0,"workRunningCount":50}
[2020-06-02T15:31:58.286Z] [work-queue] {"queueLength":0,"workRunningCount":49}
[2020-06-02T15:31:58.294Z] [work-queue] {"queueLength":0,"workRunningCount":48}
[2020-06-02T15:31:58.315Z] [work-queue] {"queueLength":0,"workRunningCount":47}
[2020-06-02T15:31:58.328Z] [work-queue] {"queueLength":0,"workRunningCount":46}
[2020-06-02T15:31:58.336Z] [work-queue] {"queueLength":0,"workRunningCount":45}
[2020-06-02T15:31:58.477Z] [work-queue] {"queueLength":0,"workRunningCount":44}
[2020-06-02T15:31:58.552Z] [work-queue] {"queueLength":0,"workRunningCount":43}
[2020-06-02T15:31:59.002Z] [work-queue] {"queueLength":0,"workRunningCount":42}
[2020-06-02T15:31:59.052Z] [work-queue] {"queueLength":0,"workRunningCount":41}
[2020-06-02T15:31:59.096Z] [work-queue] {"queueLength":0,"workRunningCount":40}
[2020-06-02T15:31:59.122Z] [work-queue] {"queueLength":0,"workRunningCount":39}
[2020-06-02T15:31:59.128Z] [work-queue] {"queueLength":0,"workRunningCount":38}
[2020-06-02T15:31:59.135Z] [work-queue] {"queueLength":0,"workRunningCount":37}
[2020-06-02T15:31:59.263Z] [work-queue] {"queueLength":0,"workRunningCount":36}
[2020-06-02T15:31:59.304Z] [work-queue] {"queueLength":0,"workRunningCount":35}
[2020-06-02T15:31:59.351Z] [work-queue] {"queueLength":0,"workRunningCount":34}
[2020-06-02T15:31:59.355Z] [work-queue] {"queueLength":0,"workRunningCount":33}
[2020-06-02T15:31:59.397Z] [work-queue] {"queueLength":0,"workRunningCount":32}
[2020-06-02T15:31:59.442Z] [work-queue] {"queueLength":0,"workRunningCount":31}
[2020-06-02T15:31:59.623Z] [work-queue] {"queueLength":0,"workRunningCount":30}
[2020-06-02T15:31:59.798Z] [work-queue] {"queueLength":0,"workRunningCount":29}
[2020-06-02T15:31:59.827Z] [work-queue] {"queueLength":0,"workRunningCount":28}
[2020-06-02T15:31:59.879Z] [work-queue] {"queueLength":0,"workRunningCount":27}
[2020-06-02T15:32:00.007Z] [work-queue] {"queueLength":0,"workRunningCount":26}
[2020-06-02T15:32:00.008Z] [work-queue] {"queueLength":0,"workRunningCount":25}
[2020-06-02T15:32:00.056Z] [work-queue] {"queueLength":0,"workRunningCount":24}
[2020-06-02T15:32:00.143Z] [work-queue] {"queueLength":0,"workRunningCount":23}
[2020-06-02T15:32:00.220Z] [work-queue] {"queueLength":0,"workRunningCount":22}
[2020-06-02T15:32:00.258Z] [work-queue] {"queueLength":0,"workRunningCount":21}
[2020-06-02T15:32:00.261Z] [work-queue] {"queueLength":0,"workRunningCount":20}
[2020-06-02T15:32:00.295Z] [work-queue] {"queueLength":0,"workRunningCount":19}
[2020-06-02T15:32:00.393Z] [work-queue] {"queueLength":0,"workRunningCount":18}
[2020-06-02T15:32:00.441Z] [work-queue] {"queueLength":0,"workRunningCount":17}
[2020-06-02T15:32:00.442Z] [work-queue] {"queueLength":0,"workRunningCount":16}
[2020-06-02T15:32:00.501Z] [work-queue] {"queueLength":0,"workRunningCount":15}
[2020-06-02T15:32:00.557Z] [work-queue] {"queueLength":0,"workRunningCount":14}
[2020-06-02T15:32:00.570Z] [work-queue] {"queueLength":0,"workRunningCount":13}
[2020-06-02T15:32:00.571Z] [work-queue] {"queueLength":0,"workRunningCount":12}
[2020-06-02T15:32:00.579Z] [work-queue] {"queueLength":0,"workRunningCount":11}
[2020-06-02T15:32:00.592Z] [work-queue] {"queueLength":0,"workRunningCount":10}
[2020-06-02T15:32:00.596Z] [work-queue] {"queueLength":0,"workRunningCount":9}
[2020-06-02T15:32:00.596Z] [work-queue] {"queueLength":0,"workRunningCount":8}
[2020-06-02T15:32:00.626Z] [work-queue] {"queueLength":0,"workRunningCount":7}
[2020-06-02T15:32:00.678Z] [work-queue] {"queueLength":0,"workRunningCount":6}
[2020-06-02T15:32:00.697Z] [work-queue] {"queueLength":0,"workRunningCount":5}
[2020-06-02T15:32:00.704Z] [work-queue] {"queueLength":0,"workRunningCount":4}
[2020-06-02T15:32:00.756Z] [work-queue] {"queueLength":0,"workRunningCount":3}
[2020-06-02T15:32:00.761Z] [work-queue] {"queueLength":0,"workRunningCount":2}
[2020-06-02T15:32:00.793Z] [work-queue] {"queueLength":0,"workRunningCount":1}
[2020-06-02T15:32:00.800Z] [work-queue] {"queueLength":0,"workRunningCount":0}
```

**Override (bad value)**
```
$ FUNCTIONS_EMULATOR_PARALLEL=-1 firebase emulators:starti  emulators: Starting emulators: functions, pubsub
i  emulators: Shutting down emulators.
i  hub: Stopping emulator hub

Error: Cannot run Functions emulator with less than 1 parallel worker (FUNCTIONS_EMULATOR_PARALLEL=-1)
```


**Override (good value)**
```
$ FUNCTIONS_EMULATOR_PARALLEL=10 firebase emulators:start --debug | grep work-queue
[2020-06-02T15:33:01.304Z] [work-queue] {"queueLength":1,"workRunningCount":0}
[2020-06-02T15:33:01.305Z] [work-queue] {"queueLength":0,"workRunningCount":1}
[2020-06-02T15:33:01.963Z] [work-queue] {"queueLength":1,"workRunningCount":1}
[2020-06-02T15:33:01.963Z] [work-queue] {"queueLength":0,"workRunningCount":2}
[2020-06-02T15:33:01.968Z] [work-queue] {"queueLength":1,"workRunningCount":2}
[2020-06-02T15:33:01.968Z] [work-queue] {"queueLength":0,"workRunningCount":3}
[2020-06-02T15:33:01.974Z] [work-queue] {"queueLength":1,"workRunningCount":3}
[2020-06-02T15:33:01.975Z] [work-queue] {"queueLength":0,"workRunningCount":4}
[2020-06-02T15:33:02.007Z] [work-queue] {"queueLength":1,"workRunningCount":4}
[2020-06-02T15:33:02.008Z] [work-queue] {"queueLength":0,"workRunningCount":5}
[2020-06-02T15:33:02.013Z] [work-queue] {"queueLength":1,"workRunningCount":5}
[2020-06-02T15:33:02.013Z] [work-queue] {"queueLength":0,"workRunningCount":6}
[2020-06-02T15:33:02.020Z] [work-queue] {"queueLength":1,"workRunningCount":6}
[2020-06-02T15:33:02.020Z] [work-queue] {"queueLength":0,"workRunningCount":7}
[2020-06-02T15:33:02.066Z] [work-queue] {"queueLength":1,"workRunningCount":7}
[2020-06-02T15:33:02.066Z] [work-queue] {"queueLength":0,"workRunningCount":8}
[2020-06-02T15:33:02.089Z] [work-queue] {"queueLength":1,"workRunningCount":8}
[2020-06-02T15:33:02.089Z] [work-queue] {"queueLength":0,"workRunningCount":9}
[2020-06-02T15:33:02.105Z] [work-queue] {"queueLength":1,"workRunningCount":9}
[2020-06-02T15:33:02.105Z] [work-queue] {"queueLength":0,"workRunningCount":10}
[2020-06-02T15:33:02.120Z] [work-queue] {"queueLength":1,"workRunningCount":10}
[2020-06-02T15:33:02.120Z] [work-queue] waiting for work to finish (running=10)
[2020-06-02T15:33:02.130Z] [work-queue] {"queueLength":2,"workRunningCount":10}
[2020-06-02T15:33:02.132Z] [work-queue] {"queueLength":3,"workRunningCount":10}
[2020-06-02T15:33:02.168Z] [work-queue] {"queueLength":4,"workRunningCount":10}
[2020-06-02T15:33:02.169Z] [work-queue] {"queueLength":5,"workRunningCount":10}
[2020-06-02T15:33:02.169Z] [work-queue] {"queueLength":6,"workRunningCount":10}
[2020-06-02T15:33:02.225Z] [work-queue] {"queueLength":7,"workRunningCount":10}
[2020-06-02T15:33:02.225Z] [work-queue] {"queueLength":8,"workRunningCount":10}
[2020-06-02T15:33:02.226Z] [work-queue] {"queueLength":9,"workRunningCount":10}
[2020-06-02T15:33:02.226Z] [work-queue] {"queueLength":10,"workRunningCount":10}
# .... much more ...
```

### Sample Commands

<!-- Proposing a change to commands or flags? Provide examples of how they will be used. -->


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e31990bc..88ffe1df1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,3 +3,4 @@
 - Fixes issue where `firebase init storage` incorrectly reports Cloud Storage has not been used.
 - Adds warning message when an emulator starts on a port restricted by Chrome (#2276).
 - Allows deleting Firestore indexes with the `--force` flag (#2002).
+- Fixes issue where massively parallel functions could lock the host machine (#2277).
diff --git a/src/emulator/workQueue.ts b/src/emulator/workQueue.ts
index 90882eac0..7e6ca6bb0 100644
--- a/src/emulator/workQueue.ts
+++ b/src/emulator/workQueue.ts
@@ -1,3 +1,6 @@
+import * as utils from "../utils";
+import { FirebaseError } from "../error";
+
 import { EmulatorLogger } from "./emulatorLogger";
 import { Emulators, FunctionsExecutionMode } from "./types";
 
@@ -12,12 +15,31 @@ type Work = () => Promise<any>;
  * callback inside the Work function.
  */
 export class WorkQueue {
+  private static MAX_PARALLEL_ENV = "FUNCTIONS_EMULATOR_PARALLEL";
+  private static DEFAULT_MAX_PARALLEL = Number.parseInt(
+    utils.envOverride(WorkQueue.MAX_PARALLEL_ENV, "50")
+  );
+
+  private logger = EmulatorLogger.forEmulator(Emulators.FUNCTIONS);
+
   private queue: Array<Work> = [];
   private workRunningCount: number = 0;
   private notifyQueue: () => void = () => {};
+  private notifyWorkFinish: () => void = () => {};
   private stopped: boolean = true;
 
-  constructor(private mode: FunctionsExecutionMode = FunctionsExecutionMode.AUTO) {}
+  constructor(
+    private mode: FunctionsExecutionMode = FunctionsExecutionMode.AUTO,
+    private maxParallelWork: number = WorkQueue.DEFAULT_MAX_PARALLEL
+  ) {
+    if (maxParallelWork < 1) {
+      throw new FirebaseError(
+        `Cannot run Functions emulator with less than 1 parallel worker (${
+          WorkQueue.MAX_PARALLEL_ENV
+        }=${process.env[WorkQueue.MAX_PARALLEL_ENV]})`
+      );
+    }
+  }
 
   /**
    * Submit an entry to the queue and run it according to the WorkMode.
@@ -40,12 +62,25 @@ export class WorkQueue {
 
     this.stopped = false;
     while (!this.stopped) {
+      // If the queue is empty, wait until something is added.
       if (!this.queue.length) {
         await new Promise((res) => {
           this.notifyQueue = res;
         });
       }
 
+      // If we have too many jobs out, wait until something finishes.
+      if (this.workRunningCount >= this.maxParallelWork) {
+        this.logger.logLabeled(
+          "DEBUG",
+          "work-queue",
+          `waiting for work to finish (running=${this.workRunningCount})`
+        );
+        await new Promise((res) => {
+          this.notifyWorkFinish = res;
+        });
+      }
+
       const workPromise = this.runNext();
       if (this.mode === FunctionsExecutionMode.SEQUENTIAL) {
         await workPromise;
@@ -60,6 +95,13 @@ export class WorkQueue {
     this.stopped = true;
   }
 
+  getState() {
+    return {
+      queueLength: this.queue.length,
+      workRunningCount: this.workRunningCount,
+    };
+  }
+
   private async runNext() {
     const next = this.queue.shift();
     if (next) {
@@ -72,19 +114,13 @@ export class WorkQueue {
         EmulatorLogger.forEmulator(Emulators.FUNCTIONS).log("DEBUG", e);
       } finally {
         this.workRunningCount--;
+        this.notifyWorkFinish();
         this.logState();
       }
     }
   }
 
   private logState() {
-    EmulatorLogger.forEmulator(Emulators.FUNCTIONS).logLabeled(
-      "DEBUG",
-      "work-queue",
-      JSON.stringify({
-        queueLength: this.queue.length,
-        workRunningCount: this.workRunningCount,
-      })
-    );
+    this.logger.logLabeled("DEBUG", "work-queue", JSON.stringify(this.getState()));
   }
 }


Test patch:
diff --git a/src/test/emulators/workQueue.spec.ts b/src/test/emulators/workQueue.spec.ts
index 2de3c6422..8b042e9d4 100644
--- a/src/test/emulators/workQueue.spec.ts
+++ b/src/test/emulators/workQueue.spec.ts
@@ -15,9 +15,11 @@ function resolveIn(ms: number) {
 
 describe("WorkQueue", () => {
   describe("mode=AUTO", () => {
+    const MAX_PARALLEL = 10;
     let queue: WorkQueue;
+
     beforeEach(() => {
-      queue = new WorkQueue(FunctionsExecutionMode.AUTO);
+      queue = new WorkQueue(FunctionsExecutionMode.AUTO, MAX_PARALLEL);
       queue.start();
     });
 
@@ -62,6 +64,26 @@ describe("WorkQueue", () => {
 
       expect(hasRun1 && hasRun2, "hasRun1 && hasRun2");
     });
+
+    it("never runs more than the maximum allowed parallel work", async () => {
+      let numRun = 0;
+      let timePerJob = 5;
+
+      const numJobs = MAX_PARALLEL * 2;
+      for (let i = 0; i < numJobs; i++) {
+        const work = () => {
+          numRun++;
+          return resolveIn(timePerJob);
+        };
+        queue.submit(work);
+      }
+
+      await resolveIn(timePerJob - 1);
+      expect(queue.getState().workRunningCount).to.eq(MAX_PARALLEL);
+
+      await resolveIn(numJobs * timePerJob + 10);
+      expect(numRun).to.eq(numJobs);
+    });
   });
 
   describe("mode=SEQUENTIAL", () => {