From 3c1289712ce2544d38c8501265a6cd0c944b70c8 Mon Sep 17 00:00:00 2001 From: Kazuyoshi Kato Date: Wed, 10 Apr 2019 22:06:22 -0700 Subject: [PATCH 1/4] Enable Fastboot and run its Node server from Procfile 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 --- Procfile | 2 +- config/environment.js | 4 +++ config/nginx.conf.erb | 7 +++++ fastboot.js | 62 +++++++++++++++++++++++++++++++++++++++++++ package.json | 8 +++--- script/ember.sh | 12 +++++++++ script/start-web.sh | 12 +++++++++ src/bin/server.rs | 11 ++++++-- 8 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 fastboot.js create mode 100755 script/ember.sh create mode 100755 script/start-web.sh diff --git a/Procfile b/Procfile index e8bdf97fa9e..320f6aa1ca2 100644 --- a/Procfile +++ b/Procfile @@ -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 diff --git a/config/environment.js b/config/environment.js index 7681acac190..8d03ba05410 100644 --- a/config/environment.js +++ b/config/environment.js @@ -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') { diff --git a/config/nginx.conf.erb b/config/nginx.conf.erb index 8f15875f3c9..c12f5336bcd 100644 --- a/config/nginx.conf.erb +++ b/config/nginx.conf.erb @@ -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; diff --git a/fastboot.js b/fastboot.js new file mode 100644 index 00000000000..0f36c52bb61 --- /dev/null +++ b/fastboot.js @@ -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(); diff --git a/package.json b/package.json index 7bd40819da3..698347610a7 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/script/ember.sh b/script/ember.sh new file mode 100755 index 00000000000..b67e00cabdb --- /dev/null +++ b/script/ember.sh @@ -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 "$@" diff --git a/script/start-web.sh b/script/start-web.sh new file mode 100755 index 00000000000..27350900511 --- /dev/null +++ b/script/start-web.sh @@ -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 diff --git a/src/bin/server.rs b/src/bin/server.rs index 73bfd7d422c..3871f9f6c53 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -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 { @@ -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 From 9ced6efcbcfa530ce40c391c348ac25ff51f5c22 Mon Sep 17 00:00:00 2001 From: Kazuyoshi Kato Date: Wed, 10 Apr 2019 23:00:38 -0700 Subject: [PATCH 2/4] Fix /crates/$crate_id --- fastboot/initializers/ajax.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 fastboot/initializers/ajax.js diff --git a/fastboot/initializers/ajax.js b/fastboot/initializers/ajax.js new file mode 100644 index 00000000000..f0950ec2caf --- /dev/null +++ b/fastboot/initializers/ajax.js @@ -0,0 +1,8 @@ +export default { + name: 'ajax-service', + initialize() { + // This is to override Fastboot's initializer which prevents ember-fetch from working + // https://github.com/ember-fastboot/ember-cli-fastboot/blob/master/fastboot/initializers/ajax.js + // https://github.com/ember-cli/ember-fetch#ajax-service + }, +}; From 2bdf7aedc7b943bb2cff8c164e9742552457d9b7 Mon Sep 17 00:00:00 2001 From: Kazuyoshi Kato Date: Thu, 14 Nov 2019 23:07:13 -0800 Subject: [PATCH 3/4] "wait -n" needs bash --- script/start-web.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/start-web.sh b/script/start-web.sh index 27350900511..47317d0867c 100755 --- a/script/start-web.sh +++ b/script/start-web.sh @@ -1,7 +1,7 @@ -#! /bin/sh +#! /bin/bash set -ue -if [ "${USE_FASTBOOT:-0}" = 1 ]; then +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 & From 6e373b61d386199e7461ed620bcee6b83402e926 Mon Sep 17 00:00:00 2001 From: Kazuyoshi Kato Date: Thu, 14 Nov 2019 23:22:01 -0800 Subject: [PATCH 4/4] Fix eslint --- fastboot.js | 1 + 1 file changed, 1 insertion(+) diff --git a/fastboot.js b/fastboot.js index 0f36c52bb61..5c50b9b7888 100644 --- a/fastboot.js +++ b/fastboot.js @@ -1,4 +1,5 @@ /* eslint-disable no-console */ +/* eslint-env node */ 'use strict';