[実験]リクエスト・スレッド間の、変数の種類によるスコープの違いについて

# はじめに

Railsのインスタンス変数・クラス変数・クラスインスタンス変数は、リクエストやスレッドに対してどのようなスコープを持つようになっているのか、調べてみます。

調べ方ですが、以下のような、インスタンス変数・クラス変数・クラスインスタンス変数をインクリメントするcontrollerクラスを作成します。pumaのプロセス数・スレッド数の設定を変えて、postsコントローラ(http://localhost/posts) へ連続してアクセスし、各変数のふるまいの違いを見てみます。

```ruby
# posts_controller.rb
class PostsController < InheritedResources::Base
  @@class_var = 0
  @class_instance_var = 0

  def initialize
    @instance_var = 0
    super
  end

  def index
    @@class_var += 1
    @instance_var += 1
    puts 'class var: %d' % @@class_var
    puts 'instance var: %d' % @instance_var
    self.class.increment_civ
    puts '------------------------'
  end

  def self.increment_civ
    @class_instance_var += 1
    puts 'class instance var: %d' % @class_instance_var
  end
end
```

```ruby
# config/puma.rb
workers 1 # プロセス数
threads_count = 1 # スレッド数
threads threads_count, threads_count
```

今回使用した環境は、以下のとおりです。

- Rubyのバージョン: `ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]`
- Railsのバージョン: `rails (5.0.0.1)`
- rackのバージョン: `rack (2.0.1)`
- pumaのバージョン: `puma (3.6.0)`

# プロセス数・スレッド数を変えて実験

### 1プロセス・1スレッドの場合

```
class var: 1
instance var: 1
class instance var: 1
------------------------
class var: 2
instance var: 1
class instance var: 2
------------------------
class var: 3
instance var: 1
class instance var: 3
------------------------
class var: 4
instance var: 1
class instance var: 4
------------------------
```

クラス変数・クラスインスタンス変数がインクリメントされていきます。クラス変数・クラスインスタンス変数が、リクエストをまたいで使い回されていることがわかります。
一方で、インスタンス変数の値はすべて1のままです。インスタンス変数はリクエストごとに作成されることがわかります。


### 2プロセス・1スレッドの場合

```
class var: 1
instance var: 1
class instance var: 1
------------------------
class var: 1
instance var: 1
class instance var: 1
------------------------
class var: 2
instance var: 1
class instance var: 2
------------------------
class var: 3
instance var: 1
class instance var: 3
------------------------
class var: 2
instance var: 1
class instance var: 2
------------------------
class var: 3
instance var: 1
class instance var: 3
------------------------
```

クラス変数・クラスインスタンス変数が二手に分かれてインクリメントされていきます。プロセスごとに別のクラス変数・クラスインスタンス変数が作成されていることがわかります。
インスタンス変数の値は、相変わらずすべて1のままです。


### 2プロセス・2スレッドの場合

```
class var: 1
instance var: 1
class instance var: 1
------------------------
class var: 1
instance var: 1
class instance var: 1
------------------------
class var: 2
instance var: 1
class instance var: 2
------------------------
class var: 2
instance var: 1
class instance var: 2
------------------------
class var: 3
instance var: 1
class instance var: 3
------------------------
class var: 3
instance var: 1
class instance var: 3
------------------------
```

2プロセス・1スレッドの場合と同様に、クラス変数・クラスインスタンス変数が、二手に分かれてインクリメントされる結果となりました。同じプロセスの異なるスレッドでは、クラス変数・クラスインスタンス変数がスレッドをまたいで使い回されていることがわかります。
インスタンス変数の値は、相変わらずすべて1のままです。

### `config.cache_classes = false`にすると・・・
`config.cache_classes = false`にして任意のクラスを編集すると、クラス変数・クラスインスタンス変数の値がリセットされました。クラス変数・クラスインスタンス変数の使い回しはクラスのキャッシュによるもので、クラスがリロードされるとクラス変数・クラスインスタンス変数もあらためて初期化されることがわかります。

# 結論

- クラス変数・クラスインスタンス変数は、リクエスト・スレッドをまたいで同じキャッシュが使われる。プロセスが異なれば、プロセスごとに別のキャッシュが使われる。
- インスタンス変数は、プロセス・リクエスト・スレッドごとに作成される。
- クラスのキャッシュがリロードされると、クラス変数・クラスインスタンス変数も初期化される。

クラス変数・クラスインスタンス変数を使うときは、気をつけましょう！