ロストアップデートと呼ばれるものはDBアーキテクト上には存在しないんじゃない？説

# ロストアップデートと呼ばれるものがあるらしい

15年ほどPostgreSQLを運用していて、最近恥ずかしながら初めて「ロストアップデート」という言葉を目にしました。
へぇーMySQLはREPEATABLE READでこんな事が起こるのかぁー。大変だなあ。と他のサイトで情報を調べながらの帰り道。調べれば調べるほど嫌な予感がしてくる。

早速帰宅後試してみました。手元にあったDocker imageから
## 実験準備

```mysql.
mysql> show variables like 'tx_isolation';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.02 sec)

mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.20    |
+-----------+

mysql> CREATE TABLE Posts (  
    -> id INT UNSIGNED NOT NULL PRIMARY KEY,  
    -> b INT NOT NULL     
    -> ) ENGINE INNODB;

mysql> INSERT INTO Posts VALUES(1, 100);
```
と設定しました。

## 実験開始

```トランザクションA.
mysql> begin; 
Query OK, 0 rows affected (0.00 sec)

mysql> select * from Posts where id = 1;
+----+-----+
| id | b   |
+----+-----+
| 1  | 100 |
+----+-----+
```
```トランザクションB.
mysql> begin; 
Query OK, 0 rows affected (0.00 sec)
```
```トランザクションA.
mysql> update Posts set b = b+1 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
```
```トランザクションB.
mysql> select * from Posts where id = 1;
+----+-----+
| id | b   |
+----+-----+
| 1  | 101 |
+----+-----+
```
あれ？　ああ、なるほど、トランザクションAがコミットする前に一度selectしておく必要があるのか。もう一度。

```トランザクションA.
mysql> update Posts set b = 100 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> begin; 
Query OK, 0 rows affected (0.00 sec)
```
```トランザクションB.
mysql> begin; 
Query OK, 0 rows affected (0.00 sec)
mysql> select * from Posts where id = 1;
+----+-----+
| id | b   |
+----+-----+
| 1  | 100 |
+----+-----+
```
```トランザクションA.
mysql> update Posts set b = b+1 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
```
```トランザクションB.
mysql> select * from Posts where id = 1;
+----+-----+
| id | b   |
+----+-----+
| 1  | 100 |
+----+-----+
```
うんうん。

```トランザクションB.
mysql> update Posts set b = b+1 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from Posts where id = 1;
+----+-----+
| id | b   |
+----+-----+
| 1  | 102 |
+----+-----+
1 row in set (0.01 sec)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from Posts where id = 1;
+----+-----+
| id | b   |
+----+-----+
| 1  | 102 |
+----+-----+
1 row in set (0.01 sec)
```
あれれー？　嫌な予感が当たったー。
やっぱりロストアップデートなんて起こらないんだけどー？？
これってもしかしてさー、、、Active Recordとかのこういったことをロストアップデートって言ってるの？

```rils.console
irb(main):010:0> post = Post.find(1)
  Post Load (3.3ms)  SELECT  "posts".* FROM "posts" WHERE "posts"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]

irb(main):014:0> post.b += 1
=> 101

irb(main):015:0> post.save
   (1.8ms)  BEGIN
  Post Update (1.9ms)  UPDATE "posts" SET "updated_at" = $1, "b" = $2 WHERE "posts"."id" = $3  [["updated_at", "2019-03-26 16:18:35.708693"], ["b", 101], ["id", 1]]
   (3.5ms)  COMMIT
=> true
```
これってDBの設計やトランザクション隔離レベルではなくて、```find```から```save```の間隔があればあるほどリスク高くなるんじゃ...別トランザクションのコミットが失われたってニュアンスはなんだかなあ...もやっと。
それをSELECT FOR UPDATEで防止するってのもなんだかなあ...もやっと。
DBのレイヤでの解決は、```update table set column = column + 1```で良いと思ったのですが、、、

確かにActive Recordで```column = column + 1```ってSQLの書き方は調べても分からなかったですが、、、
が、aplicationやlibraryの都合で必要のないロックを取りましょう！ってのは個人的にはよくないと思います。こうゆうのは優秀なruby界隈の人がシュババっと実装してくれそうですし。

ロックについてはこの増田の真似をしないように。
https://anond.hatelabo.jp/20190324181740