[Rails6]webpacker-dev-serverがheap out of memory(OOM)エラーで落ちる時の対処方法

RailsでWebpackerを用いフロント開発していたところ、
webpacker-dev-serverがやたらとメモリを食い「heap out of memory」エラーが頻出していました。

Docker環境だからか、VM上でDockerを立てていたからか、仕様かわかりませんが、
最大メモリサイズを指定することでエラーがほぼ出なくなったので、同様の現象が起きている方は参考にしてみてください。

```
rails 6.0.3.1
ruby 2.6.6
webpacker 5.1.1

フロント：React（bin/webpack-dev-server利用）
サーバー：Rails
```

## 結論

`bin/webpack-dev-server`に最大のメモリサイズを渡すと、最大メモリに近づいた時にメモリ解放を行ってくれました。

`NODE_OPTIONS`の環境変数に`--max-old-space-size=3072`を加えると最大メモリサイズを設定することができます。

```ruby:bin/webpack-dev-server
#...
ENV["NODE_OPTIONS"]  ||= "--max-old-space-size=3072"
#...
```

参考: [Rails5.1のbin/webpacker-dev-serverでJavaScript heap out of memoryが起きた時の対処方](https://qiita.com/nmatsumoto4/items/cc73b796c78786430835)

## 解説

開発はDockerを用いています。
Rails ServerとWebpackerを分けて、composeで管理をしています。

```yaml:docker-compose.yml
version: '3'
services:
  db:
    image: postgres:11.6-alpine
    volumes:
      - db:/var/lib/postgresql/data:cached
    networks:
      - web
    environment:
      TZ: Asia/Tokyo
  webpacker:
    build: .
    command: ./bin/webpack-dev-server
    volumes:
      - .:/app:cached
      - bundle:/usr/local/bundle:cached
      - yarn:/node_modules:cached
    ports:
      - '3035:3035'
    networks:
      - web
    environment:
      NODE_ENV: development
      RAILS_ENV: development
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
  web:
    build: .
    command: sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app:cached
      - bundle:/usr/local/bundle:cached
      - yarn:/node_modules:cached
    ports:
      - '3000:3000'
    depends_on:
      - db
      - webpacker
    networks:
      - web
    tty: true
    stdin_open: true
    environment:
      WEBPACKER_DEV_SERVER_HOST: webpacker
volumes:
  db:
  bundle:
  yarn:
networks:
  web:
```

webpack-dev-serverを立てている状態でフロント部分を開発していると、JSを更新すると自動コンパイルされます。
コンパイル内容をメモリに格納していくようで、開発を進めていると更新の度にどんどんメモリが圧迫されます。

LIMITを超えたタイミングで「heap out of memory」エラーが発生しDockerを再起動する必要があります。（メモリを開放する）

Dockerのメモリ使用量は`docker stats`で確認することができます。

```sh
docker stats
=>
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
f697683f3b6e        app_web_1           0.01%               116.4MiB / 3.859GiB   2.95%               84.5kB / 39.1kB     0B / 0B             19
c66e3f0371b1        app_db_1            0.00%               2.633MiB / 3.859GiB   0.07%               11.1kB / 60kB       0B / 0B             7
8bcd63949c32        app_webpacker_1     0.00%               859.9MiB / 3.859GiB   21.76%              92.6kB / 63kB       0B / 0B             11
```

`MEM USAGE`が`LIMITPIDS`を超えるとOOMエラーとなります。

```
MEM USAGE / LIMITPIDS
859.9MiB / 3.859GiB 
```

WebpackerのDockerでは`./bin/webpack-dev-server`コマンドを実行してます。

`./bin/webpack-dev-server`では最終的に`node_modules/.bin/web-packer-dev`を実行しているようです。

参考: [Webpacker使うなら最低限これだけは知っておいてほしいこと](https://qiita.com/chimame/items/8d3d6f4afea675cffa7d)

`./bin/webpack-dev-server`上ではnodeに環境変数を渡すことができます。
NODE_OPTIONSに`--max-old-space-size=3072`を渡すと最大メモリサイズを指定して`web-packer-dev`を実行するようになります。

ご自身の環境に合わせて、任意のメモリサイズを指定してください。

```ruby:bin/webpack-dev-server
#!/usr/bin/env ruby

ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
ENV["NODE_ENV"]  ||= "development"
ENV["NODE_OPTIONS"]  ||= "--max-old-space-size=3072"

require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
  Pathname.new(__FILE__).realpath)

require "bundler/setup"

require "webpacker"
require "webpacker/dev_server_runner"

APP_ROOT = File.expand_path("..", __dir__)
Dir.chdir(APP_ROOT) do
  Webpacker::DevServerRunner.run(ARGV)
end
```

これでwebpack-dev-serverが最大メモリサイズに達したらメモリ解放されるようになりました。

メモリサイズが小さいため、頻繁にコンパイルするとメモリ解放が間に合わずOOMが出る時もありますが、かなり頻度が下がりました。

ご自身のスペックにあったメモリサイズを指定し、快適な開発に用いてください！