Rails5 で `#increment!` と `#decrement!` が同時実行を意識した処理になった！

同時実行になった時におかしくなっちゃう、というその点でずっと使えなかった `#increment!` が同時実行を意識した処理になったよ！
嬉しい！ 素敵！

## rails4

```irb:rails4
irb(main):001:0> counter = Counter.create
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "counters" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", "2016-02-22 11:39:38.966295"], ["updated_at", "2016-02-22 11:39:38.966295"]]
   (1.9ms)  commit transaction
=> #<Counter id: 4, value: nil, created_at: "2016-02-22 11:39:38", updated_at: "2016-02-22 11:39:38">
```

default 設定し忘れて nil になっているけれども `value` は `integer` です。
この `value` を `#increment!` したいと思います。

```irb:rails4
irb(main):004:0> counter.increment!(:value)
   (0.1ms)  begin transaction
  SQL (0.3ms)  UPDATE "counters" SET "value" = ?, "updated_at" = ? WHERE "counters"."id" = ?  [["value", 1], ["updated_at", "2016-02-22 11:40:48.595257"], ["id", 4]]
   (2.7ms)  commit transaction
=> true
```

実行された SQL を見ると value に 1 が指定されてるのがわかります。
この `1` は、どこからきたかっていうと、インスタンスがキャッシュしている value の値を元に計算されてるんですね。

つまりこれはどういうことかっていうと、

```irb:rails4
irb(main):005:0> counter1 = Counter.find(4)
  Counter Load (0.2ms)  SELECT  "counters".* FROM "counters" WHERE "counters"."id" = ? LIMIT 1  [["id", 4]]
=> #<Counter id: 4, value: 1, created_at: "2016-02-22 11:39:38", updated_at: "2016-02-22 11:40:48">
irb(main):006:0> counter2 = Counter.find(4)
  Counter Load (0.1ms)  SELECT  "counters".* FROM "counters" WHERE "counters"."id" = ? LIMIT 1  [["id", 4]]
=> #<Counter id: 4, value: 1, created_at: "2016-02-22 11:39:38", updated_at: "2016-02-22 11:40:48">
```

こんな感じで、同一レコードから生成された別のインスタンスがいる時に、それぞれで同時に `#increment!` したらどうなるかってことです。

```irb:rails4
irb(main):007:0> counter1.increment!(:value)
   (0.1ms)  begin transaction
  SQL (0.3ms)  UPDATE "counters" SET "value" = ?, "updated_at" = ? WHERE "counters"."id" = ?  [["value", 2], ["updated_at", "2016-02-22 11:43:39.842789"], ["id", 4]]
   (1.8ms)  commit transaction
=> true
irb(main):008:0> counter2.increment!(:value)
   (0.1ms)  begin transaction
  SQL (0.3ms)  UPDATE "counters" SET "value" = ?, "updated_at" = ? WHERE "counters"."id" = ?  [["value", 2], ["updated_at", "2016-02-22 11:43:45.706247"], ["id", 4]]
   (1.7ms)  commit transaction
=> true
```

`value` が 1 の状態から `#increment!` は 2回呼ばれました。
`value` が 3 になっていて欲しいのですが、実際には 2 です :worried: 

```
irb(main):011:0> Counter.find(4).value
  Counter Load (0.1ms)  SELECT  "counters".* FROM "counters" WHERE "counters"."id" = ? LIMIT 1  [["id", 4]]
=> 2
```

この同時実行に `#decrement!` が混ざってくると、もう何が何やらです。

```irb:rails4
irb(main):024:0> counter1.increment!(:value, 10)
   (0.1ms)  begin transaction
  SQL (0.3ms)  UPDATE "counters" SET "value" = ?, "updated_at" = ? WHERE "counters"."id" = ?  [["value", 12], ["updated_at", "2016-02-22 11:50:37.442805"], ["id", 4]]
   (1.8ms)  commit transaction
=> true
irb(main):025:0> counter2.increment!(:value, 20)
   (0.1ms)  begin transaction
  SQL (0.3ms)  UPDATE "counters" SET "value" = ?, "updated_at" = ? WHERE "counters"."id" = ?  [["value", 22], ["updated_at", "2016-02-22 11:50:52.249161"], ["id", 4]]
   (1.7ms)  commit transaction
=> true
irb(main):026:0> counter1.decrement!(:value, 10)
   (0.1ms)  begin transaction
  SQL (0.3ms)  UPDATE "counters" SET "value" = ?, "updated_at" = ? WHERE "counters"."id" = ?  [["value", 2], ["updated_at", "2016-02-22 11:51:04.072007"], ["id", 4]]
   (2.7ms)  commit transaction
=> true
irb(main):027:0> Counter.find(4).value
  Counter Load (0.1ms)  SELECT  "counters".* FROM "counters" WHERE "counters"."id" = ? LIMIT 1  [["id", 4]]
=> 2
```

20 はどこに消えた :worried: 

## rails5

さあ、同じように Counter インスタンスを作ります。

```irb:rails5
irb(main):001:0> counter = Counter.create
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "counters" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", 2016-02-22 11:47:23 UTC], ["updated_at", 2016-02-22 11:47:23 UTC]]
   (2.9ms)  commit transaction
=> #<Counter id: 5, value: nil, created_at: "2016-02-22 11:47:23", updated_at: "2016-02-22 11:47:23">
```

おもむろに `#increment!` しましょう。

```irb:rails5
irb(main):002:0> counter.increment!(:value)
  SQL (3.2ms)  UPDATE "counters" SET "value" = COALESCE("value", 0) + 1 WHERE "counters"."id" = ?  [["id", 5]]
=> #<Counter id: 5, value: 1, created_at: "2016-02-22 11:47:23", updated_at: "2016-02-22 11:47:23">
```

発行された SQL を見ると UPDATE 文が変わったのがわかるかと思います。
`"value" = COALESCE("value", 0) + 1` となっていますね(嬉

先ほどと同じように、同一のレコードから生成されたインスタンスを複数作って操作してみます。

```irb:rails5
irb(main):003:0> counter1 = Counter.find(5)
  Counter Load (0.2ms)  SELECT  "counters".* FROM "counters" WHERE "counters"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
=> #<Counter id: 5, value: 1, created_at: "2016-02-22 11:47:23", updated_at: "2016-02-22 11:47:23">
irb(main):004:0> counter2 = Counter.find(5)
  Counter Load (0.2ms)  SELECT  "counters".* FROM "counters" WHERE "counters"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
=> #<Counter id: 5, value: 1, created_at: "2016-02-22 11:47:23", updated_at: "2016-02-22 11:47:23">

irb(main):005:0> counter1.increment!(:value)
  SQL (2.1ms)  UPDATE "counters" SET "value" = COALESCE("value", 0) + 1 WHERE "counters"."id" = ?  [["id", 5]]
=> #<Counter id: 5, value: 2, created_at: "2016-02-22 11:47:23", updated_at: "2016-02-22 11:47:23">
irb(main):006:0> counter2.increment!(:value)
  SQL (2.1ms)  UPDATE "counters" SET "value" = COALESCE("value", 0) + 1 WHERE "counters"."id" = ?  [["id", 5]]
=> #<Counter id: 5, value: 2, created_at: "2016-02-22 11:47:23", updated_at: "2016-02-22 11:47:23">
irb(main):007:0> Counter.find(5).value
  Counter Load (0.2ms)  SELECT  "counters".* FROM "counters" WHERE "counters"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
=> 3
```

今度はちゃんと value が 3 になっています :smile: 

`#decrement!` だって大丈夫！

```irb:rails5
irb(main):008:0> counter1.increment!(:value, 10)
  SQL (2.2ms)  UPDATE "counters" SET "value" = COALESCE("value", 0) + 10 WHERE "counters"."id" = ?  [["id", 5]]
=> #<Counter id: 5, value: 12, created_at: "2016-02-22 11:47:23", updated_at: "2016-02-22 11:47:23">
irb(main):009:0> counter2.increment!(:value, 20)
  SQL (2.2ms)  UPDATE "counters" SET "value" = COALESCE("value", 0) + 20 WHERE "counters"."id" = ?  [["id", 5]]
=> #<Counter id: 5, value: 22, created_at: "2016-02-22 11:47:23", updated_at: "2016-02-22 11:47:23">
irb(main):010:0> counter1.decrement!(:value, 10)
  SQL (2.2ms)  UPDATE "counters" SET "value" = COALESCE("value", 0) - 10 WHERE "counters"."id" = ?  [["id", 5]]
=> #<Counter id: 5, value: 2, created_at: "2016-02-22 11:47:23", updated_at: "2016-02-22 11:47:23">
irb(main):011:0> Counter.find(5).value
  Counter Load (0.2ms)  SELECT  "counters".* FROM "counters" WHERE "counters"."id" = ? LIMIT ?  [["id", 5], ["LIMIT", 1]]
=> 23
```

ただし `#increment!` や `#decrement!` した後は、「実際に DB に保存されている値」と「インスタンスのキャッシュ」にズレが出ている可能性があるというのは、意識しておいた方が良さそうです。

```irb:rails5
irb(main):012:0> counter1.value
=> 2
irb(main):013:0> counter2.value
=> 22
```