トランザクションの挙動を確かめるために、1台のPC上に2つのセッションを用意したい

#はじめに
OSS-DB Silver取得に向けて勉強中です。
トランザクションの挙動を実際に試してみたいと思い
1台のPC上で2つのセッションを立ち上げる方法を調べたのですが、
ドンピシャな記事を見つけることができませんでした。

試しにVSCodeでターミナルを2つ立ち上げて動かしてみたところ、解決しました。

以下、その記録です。

◆やりたいこと◆
2つのセッションでトランザクションを開始して、
トランザクションの分離レベルの挙動の違いを確かめたい。
（Read uncommitted、Read committed、Repeatable read、Serializableの挙動の違いを確かめたい）
（ダーティーリード、ファジーリード、ファントムリード、直列化異常が起こる様子を再現したい）

◆疑問◆
1台のPCで2つのトランザクションを動かすには、どうしたら良いの？

◆結論◆
VSCodeでターミナルを2つ立ち上げて、それぞれでトランザクションを開始すればOK！


# 環境
|||バージョン|
|:--|:--|:--|
|Host OS|Windows 10 Home|21H1(OSビルド:19043.1165)|
|Virtual Machine|WSL2|-|
|Remote OS|Ubuntu| 20.04.2 LTS (GNU/Linux 5.4.72-microsoft-standard-WSL2 x86_64)|
|Database|PostgreSQL|12.7 (Ubuntu 12.7-0ubuntu0.20.04.1)|
|Docker|無し|-|

#1.最初の疑問
さあ、トランザクションを試してみよう！と思った矢先。
トランザクションの挙動を試すには、2つのPC？から同テーブルにアクセスしてSQLを実行する必要があるんじゃないの！？
という疑問が浮かびました。

私、PC1つしか持っていない…。でも仮想環境でUbuntuを使っているのだから、この環境を2つにしてUbuntuマシンを増やせば良いのか？
…というか増やせるのか？？

はたまた1つのUbuntuに複数ユーザを作れば良いのか？できるのか？？

↓こちらの記事を参考にさせていただきました。

https://qiita.com/song_ss/items/38e514b05e9dabae3bdb#mysql%E3%81%A7%E5%AE%9F%E9%9A%9B%E3%81%AB%E8%A9%A6%E3%81%99

>2つのターミナルからMySQLに接続します。クライアントAとクライアントBとします

と書かれていました。

2つのターミナル…。VSCodeで複数のターミナルを開いてそこから操作すればOKということでしょうか？
とにかく、[こちらの記事](https://www.javadrive.jp/vscode/terminal/index1.html#section2)で複数ターミナルを開く方法を確認し、実行してみることにしましました。

#2.VSCodeで2つのターミナルを立ち上げる
①まず、Ubuntuで`code`→Enter。VSCodeを立ち上げる。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/e059366d-90db-8b1f-a3d1-fa4e6390d995.png)
↓
立ち上がった。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/92ff4254-84b9-044a-3964-7fbe6d202c3d.png)

②上図赤丸印の「+」をクリック。
赤四角の部分（>bash）が表示されました。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/28840ec6-760a-92f1-4e89-10825a9c5414.png)
参考記事とは違う表示です。２つともbashとなっていて区別が付きにくいですね。
名前変更できるのでしょうか・・・。
↓
右クリックしたらRenameという選択肢が表示されました。名前変更します。
↓
緑本ではセッション1とセッション2からテーブルを見に行く設定になっていたので、S1、S2と名付けました。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/eb9f00c7-36dc-a9c0-4013-4264a521653e.png)

③S1ターミナル（以下S1）でPostgreSQLに接続
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/d64972cc-414f-7dec-6444-d35f9727e5a3.png)

④S2ターミナル（以下S2）でPostgreSQLに接続
（同上）

…これでいいのか？
全く同じ条件で同じデータベース(ossdb)にアクセスしている、という状態だがこれでOKなのか？
いまいち自信を持てませんが、とにかく進めてみます。

#3.ダーティーリードを試す

コードは緑本『OSS教科書 OSS-DB Silver Ver2.0対応』第10章(トランザクション)の解説を参考にしました。

**⓪テーブルを用意**
S1で↓赤枠のテーブルを作成した。
（メモ：SQL文の途中で改行＝`Ctrl`+`Enter`）
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/5123688c-1fee-60cb-476b-02cad230a193.png)

**①S1でトランザクション開始**
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/0c3f98dc-c4d1-45fb-a77a-e380fa8590aa.png)

**②S2でトランザクション開始**
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/e1832a0d-bc81-ca32-cb70-0d32bb83d3dd.png)

**③S1で　id1のcount列　の値を　5→10　に更新**
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/2b2273b5-8e09-d01f-764d-dfe448416266.png)
↑
レコードの上下が逆になってしまいました。ORDER BYで指定しないとこうなるのですね。

**④S2でtblを見る**
さあ、ここでS2からtblテーブルを見たら、S1でコミットしていない「id1--10」が見えるはずです。
つまりダーティーリードが起きるはずです。
見てみます。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/ad04f600-531d-cc55-48af-04783c339a52.png)

？？？
ダーティーリードが起きていません。
更新前の「id1--5」が見えました。

ここでふと、デフォルトの分離レベル「Read committed」では、
ダーティリードは起こらない。だからこのような挙動になったのでは？？
と思いました。

現在の分離レベルを確認してみます。

#4.現在の分離レベルの確認
![1000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/31e2cf5f-2219-c1ce-32bc-570aa773e1be.png)

やはりRead Committedでした。
だからダーティーリードされなかったのですね。

ダーティリードの挙動を確かめるには、分離性を「Read uncommitted」に変更すれば良いのか？
と最初は思いましたが、
[OSS-DB道場](https://oss-db.jp/dojo/dojo_info_07)に
>'READ UNCOMMITTED'　に設定しても内部的には　'READ COMMITTED'　と同じ動作となり、
ダーティーリードという望ましくない結果が起きないようになっている 

とありました。
つまり、*PostgreSQLではダーティーリードは起こり得ない*、ということですね。
ダーティリードの挙動は確かめずに次に行きます。

（tblテーブルはS1で`DROP TABLE tbl;`を実行し、削除しておきました。
　トランザクション開始されていてコミットされていない状態でも、テーブル削除可能でした。）

#5.分離レベルの変更（Read Committed → Repeatable read）

S1で、tblテーブルの
分離レベルを
Read committed(PostgreSQLのデフォルト)
から
Repeatable readに変更する。

緑本（p318）の説明では以下の書式が紹介されていた。

◆セッション単位の分離レベルの指定方法◆
`SET default_transaction_isolation TO '分離レベル';`

◆トランザクション単位の分離レベルの指定方法◆
`SET transaction_isolation TO '分離レベル';`

`BEGIN ISOLATION LEVEL 分離レベル;`
または
`START TRANSACTION ISOLATION LEVEL 分離レベル`

*「どちらを使うのが適切なのか」という判断はどこをポイントに考えればよいのか、
今はよくわかりません。（未解決の疑問 欄に追記）*

トランザクション単位の指定をした場合、そのトランザクション中だけ指定した分離レベルとなり、COMMITやABORTにより元の分離レベルに戻る、とのことでした。
今回は、自分が変更するまでは指定した分離レベルを維持したいので、
セッション単位で指定します。

**⓪S1で分離レベルをデフォルトの「Read Committed」から「Repeatable read」に変更**
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/242c591d-5f1e-e6de-e89a-a080bfa37ffe.png)

**①S1でトランザクション開始**
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/0c3f98dc-c4d1-45fb-a77a-e380fa8590aa.png)

**②S2でトランザクション開始**
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/e1832a0d-bc81-ca32-cb70-0d32bb83d3dd.png)

**③S1で　id1のcount列　の値を　5→10　に更新**
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/2b2273b5-8e09-d01f-764d-dfe448416266.png)

**④S2でtblを見る**
さあ、ここでS2からtblテーブルを見ます。
ダーティーリードが起きれば、S1でコミットしていない「id1=10」が見えるはずです。
ダーティーリードが起きなければ、S1のトランザクション開始前の「id1 =5」が見えるはずです。
見てみます。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/ad04f600-531d-cc55-48af-04783c339a52.png)

ダーティーリードは起きませんでした。OKです！

**⑤ABORT**
ここではトランザクションはCOMMITさせず、ABORTしてトランザクション開始前の状態に戻しておきます。
＜S1＞
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/a747e577-3edc-aa01-c6d1-374c7214cd9e.png)
ROLLBACKと返ってきました。緑本（p.310）に「ROLLBACKとABORTは同義です」とありました。
ABORTしてもROLLBACKと返ってくるのですね。（**ABORTと返ってくるケースもあるのだろうか？**）

＜S2＞
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/a747e577-3edc-aa01-c6d1-374c7214cd9e.png)


#6.ファントムリードを試す
現在の「Repeatable read」では、ファントムリードと直列化異常は起こる、というのが特徴です。
ファントムリードが起こるかどうか、確かめてみます。
テーブルは、ここまで使っていたtblテーブルを使います。

＜tblテーブル＞

|id|count|
|:--|:--|
|1|5|
|2|8|

>ファントムリード
自身のトランザクションから、別のトランザクションのコミットされた挿入結果が見えてしまうこと
（緑本p316）

**①S1でトランザクション開始**
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/0c3f98dc-c4d1-45fb-a77a-e380fa8590aa.png)

**②S2でトランザクション開始**
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/e1832a0d-bc81-ca32-cb70-0d32bb83d3dd.png)

**③S1で　id1のcount列　の値を　5→10　に更新**
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/2b2273b5-8e09-d01f-764d-dfe448416266.png)

**④S2でtblを見る**
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/ad04f600-531d-cc55-48af-04783c339a52.png)

S1の③の**更新**結果は見えていない、つまり「**ダーティーリードが起きていない**」状態です。OKです。

**⑤S1でtblテーブルにレコードを挿入**
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/3b1a3c95-0867-9bd1-d9af-64184e5a890f.png)

**⑥S1のトランザクションをCOMMIT**
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/4881f52d-1a81-f538-e31a-4ab231c9164c.png)

**⑦S2のトランザクション内でtblテーブルをSELECT**
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/26a2fd49-6e25-e30e-e19c-5ad9bbb89e56.png)

S2でトランザクション開始後、1回目にSELECTしたtblテーブルと2回目にSELECTしたtblテーブルの内容が異なる（＝別のトランザクションで新レコードを挿入&コミットされたテーブルが見えてしまった）現象が起きました。
つまり、ファントムリードが起きました！
見えてはいけないものが見えてしまった、まさにファントム（幽霊）ですね。


ということで、1台のPCで2つのセッションを立ち上げてトランザクションの挙動を確かめるためにはどうしたら良いのか？という疑問の答えは、

**VSCodeで2つのターミナルを立ち上げればOK。**でした！ 

次は、他の分離レベル（ファジーリード、直列化異常）についても試してみます。（→別記事へ）

#未解決の疑問
##同PC内でトランザクションを試すための他の方法は？
今回はVSCodeのターミナルを2つ立ち上げる事で試すことができましたが、
他の方法はどんな方法があるのでしょうか？
今回のターミナル2つ使う方法も、物は試しだとやってみて、成功しましたが
こうした方法を解説する記事を見つけることができませんでした。
他の方法も知りたいです。