Skip to content

Commit

Permalink
Enable Fastboot and run its Node server from Procfile
Browse files Browse the repository at this point in the history
USE_FASTBOOT environment variable is used to enable Fastboot.
The number of workers configurable through WEB_CONCURRENCY.

https://devcenter.heroku.com/articles/node-memory-use#running-multiple-processes
  • Loading branch information
kzys committed Nov 15, 2019
1 parent 21e04f3 commit 3c12897
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Procfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
release: bin/diesel migration run
web: bin/start-nginx ./target/release/server
web: ./script/start-web.sh
background_worker: ./target/release/background-worker
4 changes: 4 additions & 0 deletions config/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ module.exports = function(environment) {
// Here you can pass flags/options to your application instance
// when it is created
},

fastboot: {
hostWhitelist: ['crates.io', /^localhost:\d+$/, /\.herokuapp\.com$/],
},
};

if (environment === 'development') {
Expand Down
7 changes: 7 additions & 0 deletions config/nginx.conf.erb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ http {
proxy_pass http://app_server;
}

<% if ENV['USE_FASTBOOT'] %>
# Just in case, only forward "/policies" to Ember for a moment
location = /policies {
proxy_pass http://localhost:9000;
}
<% end %>

location ~ ^/api/v./crates/new$ {
proxy_pass http://app_server;

Expand Down
62 changes: 62 additions & 0 deletions fastboot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* eslint-disable no-console */

'use strict';

const fs = require('fs');
const os = require('os');
const FastBootAppServer = require('fastboot-app-server');

// because fastboot-app-server uses cluster, but it might change in future
const cluster = require('cluster');

class LoggerWithoutTimestamp {
constructor() {
this.prefix = cluster.isMaster ? 'master' : 'worker';
}
writeLine() {
this._write('info', Array.prototype.slice.apply(arguments));
}

writeError() {
this._write('error', Array.prototype.slice.apply(arguments));
}

_write(level, args) {
args[0] = `[${level}][${this.prefix}] ${args[0]}`;
console.log.apply(console, args);
}
}

function writeAppInitializedWhenReady(logger) {
let timeout;

timeout = setInterval(function() {
logger.writeLine('waiting backend');
if (fs.existsSync('/tmp/backend-initialized')) {
logger.writeLine('backend is up. let heroku know the app is ready');
fs.writeFileSync('/tmp/app-initialized', 'hello');
clearInterval(timeout);
} else {
logger.writeLine('backend is still not up');
}
}, 1000);
}

var logger = new LoggerWithoutTimestamp();

logger.writeLine(`${os.cpus().length} cores available`);

let workerCount = process.env.WEB_CONCURRENCY || 1;

let server = new FastBootAppServer({
distPath: 'dist',
port: 9000,
ui: logger,
workerCount: workerCount,
});

if (!cluster.isWorker) {
writeAppInitializedWhenReady(logger);
}

server.start();
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
"lint:js": "eslint . --cache",
"lint:deps": "ember dependency-lint",
"prettier": "prettier --write '{app,tests,mirage}/**/*.js'",
"start": "FASTBOOT_DISABLED=true ember serve",
"start:live": "FASTBOOT_DISABLED=true ember serve --proxy https://crates.io",
"start:local": "FASTBOOT_DISABLED=true ember serve --proxy http://127.0.0.1:8888",
"start:staging": "FASTBOOT_DISABLED=true ember serve --proxy https://staging-crates-io.herokuapp.com",
"start": "./script/ember.sh serve",
"start:live": "./script/ember.sh serve --proxy https://crates.io",
"start:local": "./script/ember.sh serve --proxy http://127.0.0.1:8888",
"start:staging": "./script/ember.sh serve --proxy https://staging-crates-io.herokuapp.com",
"test": "ember exam --split=2 --parallel",
"test-coverage": "COVERAGE=true npm run test && ember coverage-merge && rm -rf coverage_* coverage/coverage-summary.json && nyc report"
},
Expand Down
12 changes: 12 additions & 0 deletions script/ember.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#! /bin/sh
set -ue

export FASTBOOT_DISABLED

if [ "${USE_FASTBOOT:-0}" = '1' ]; then
unset FASTBOOT_DISABLED
else
FASTBOOT_DISABLED=1
fi

ember "$@"
12 changes: 12 additions & 0 deletions script/start-web.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#! /bin/sh
set -ue

if [ "${USE_FASTBOOT:-0}" = 1 ]; then
export USE_FASTBOOT=1
node --optimize_for_size --max_old_space_size=200 fastboot.js &
bin/start-nginx ./target/release/server &
wait -n
else
unset USE_FASTBOOT
bin/start-nginx ./target/release/server
fi
11 changes: 9 additions & 2 deletions src/bin/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ fn main() {
boot::categories::sync(categories_toml).unwrap();

let heroku = dotenv::var("HEROKU").is_ok();
let fastboot = dotenv::var("USE_FASTBOOT").is_ok();

let port = if heroku {
8888
} else {
Expand Down Expand Up @@ -102,8 +104,13 @@ fn main() {
// Creating this file tells heroku to tell nginx that the application is ready
// to receive traffic.
if heroku {
println!("Writing to /tmp/app-initialized");
File::create("/tmp/app-initialized").unwrap();
let path = if fastboot {
"/tmp/backend-initialized"
} else {
"/tmp/app-initialized"
};
println!("Writing to {}", path);
File::create(path).unwrap();
}

// Block the main thread until the server has shutdown
Expand Down

0 comments on commit 3c12897

Please sign in to comment.