## モジュール7: Branch 機能でイノベーションを加速する

![demo_203-tidb-x-branching-ja.jpg 85%](https://lab-static.pingcap.com/images/2025/12/2/4d3c74e5091a47138635e82630eb26a67dcf9290.jpg)

> [!NOTE]
> このモジュールでは、現在プレビュー段階にある機能を紹介します。

このモジュールでは、Branch機能を活用することで開発効率を大幅に向上できることを示します。

Branchとは、元のクラスタから分岐した独立したクラスターインスタンスで、それぞれが個別のデータコピーを持っています。この分離された環境により、本番クラスターへ影響を与えることなく、安全に実験や変更のテストを行うことが可能です。本モジュールでは、現在のデータベースから3つの独立したBranchを作成し、それぞれのBranch上で異なる在庫管理ソリューションの並列テストを実行します。

### 手順 1. Branch を作成する

- TiDB Cloudコンソールの[Clustersページ](https://tidbcloud.com/project/clusters)にアクセスし、ラボクラスター名をクリックします。`Create New Cluster`からラボを開始した場合、クラスター名は `${LAB:SERVERLESS_CLUSTER_NAME}` です。

![Demo-RU-001.png 85%](https://lab-static.pingcap.com/images/2025/11/24/9c016ddc77e89cb9d9ae0669890d36cb316f6d20.png)

- クラスター名 `${LAB:SERVERLESS_CLUSTER_NAME}` をクリックし、左上の「Overview」を選択します。

![Demo-RU-002.png 85%](https://lab-static.pingcap.com/images/2025/11/24/8d064cdda27759df1640171cbf92f7d340e4364d.png)

- 左側の「Branches」タブをクリックして、Branch パネルを開きます。

![demo_branch_UI_01.png 85%](https://lab-static.pingcap.com/images/2025/11/27/c094e9effcea9e16423f52268a3f03be1d9f27fe.png)

- Branchesパネルの右上にある「Create Branch」ボタンをクリックして、新しいBranchを作成します。

![demo_branch_UI_02.png 85%](https://lab-static.pingcap.com/images/2025/11/27/544986b0dc326f95d55e8674dd3b403630801592.png)

- ポップアップのCreate Branchウィンドウで、Nameフィールドに `test-branch-1` を入力し、Parent branchフィールドで `main` を選択、「Include data up to」フィールドで `Current point in time` を選択します。これで、現時点のデータスナップショットを持つ `test-branch-1` という名前のBranchが作成されます。その後「Create」ボタンをクリックします。

![demo_branch_UI_03.png 50%](https://lab-static.pingcap.com/images/2025/11/27/1a617fd5c838b7b4559866a7196edf6fb4b71aaf.png)

- 同様の手順で、`test-branch-2` という名前のBranchも現時点のデータスナップショット付きで作成します。

![demo_branch_UI_04.png 50%](https://lab-static.pingcap.com/images/2025/11/27/f14658cf6af60b5bebde97b0c9baeb72196855a0.png)

- 同じく `test-branch-3` という名前のBranchも作成します。

![demo_branch_UI_05.png 50%](https://lab-static.pingcap.com/images/2025/11/27/067cc091c2c27bb173c4b1d47ecaefee42b79e5a.png)

- Branchパネルに戻り、作成した3つの新しいBranchと Status 列を確認しましょう。

![demo_branch_UI_06.png 85%](https://lab-static.pingcap.com/images/2025/11/27/5719c270a2536174780784e9ec2c970d0baa51ef.png)

- 3つ全てのBranchがStatus列で「Active」と表示されていれば、使用可能です。

![demo_branch_UI_07.png 85%](https://lab-static.pingcap.com/images/2025/11/27/7316875a385eb9453c8d5985677bdb5739f1c1e0.png)

### 手順 2. すべての Branch に接続する

- 「Branches」パネルで `test-branch-1` を選択し、「Action」列の `...` をクリックして「Connect（接続）」を選びます。

![demo_branch_UI_08.png 85%](https://lab-static.pingcap.com/images/2025/11/27/fcc16837cc8a9500b11dfce73d167fa16664ba49.png)

- ポップアップウィンドウで「Generate Password（パスワード生成）」をクリックします。

![demo_branch_UI_09.png 85%](https://lab-static.pingcap.com/images/2025/11/27/acda36b4b8b64f0e63910a71ee3a51bca663a969.png)

- パスワードが生成されたら、「Connect with .env」を選択し、「copy（コピー）」アイコンをクリックします。

![env-file-branch-1.jpg 50%](https://lab-static.pingcap.com/images/2025/12/2/d16d8e8bca04b77d597b2df188502031c49dd771.jpg)

- [Terminal 1](ssh://monitor1-ssh#1) で、コピーした `test-branch-1` の接続情報を貼り付けて環境変数に設定します。下記のような出力になります：

  ```output
  ubuntu@ip-10-90-1-65:~$ DB_HOST=gateway01.us-west-2.prod.aws.tidbcloud.com
  DB_PORT=4000
  DB_USERNAME='3NViMyHWFBkJvQq.root'
  DB_PASSWORD='1Pbcc4CFXTtnZA4C'
  DB_DATABASE='lab_demo_203_6930011'
  ```

- [Terminal 1](ssh://monitor1-ssh#1) で、下記コマンドを実行し、`test-branch-1` の接続環境変数を `.env-1` ファイルに保存します。

  ```bash
  cat > .env-1 <<EOF
  BRANCH_HOST=$DB_HOST
  BRANCH_PORT=$DB_PORT
  BRANCH_USERNAME=$DB_USERNAME
  BRANCH_PASSWORD=$DB_PASSWORD
  BRANCH_DATABASE=${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}
  EOF
  ```

- `test-branch-2` も同様に、「Generate Password」でパスワード生成後、「Connect with .env」を選択し、「copy」アイコンをクリックしてください。

![env-file-branch-2.jpg 50%](https://lab-static.pingcap.com/images/2025/12/2/3eac0b2ee150121143fe91b4b514bbecc617e5fc.jpg)

- [Terminal 2](ssh://monitor1-ssh#2) をクリックし、コピーした `test-branch-2` の接続情報を貼り付けて環境変数に設定します。下記のような出力になります：

  ```output
  ubuntu@ip-10-90-1-65:~$ DB_HOST=gateway01.us-west-2.prod.aws.tidbcloud.com
  DB_PORT=4000
  DB_USERNAME='zxBRD32X3wj8KRY.root'
  DB_PASSWORD='PvNnqFGh63rAX4RK'
  DB_DATABASE='lab_demo_203_6930011'
  ```

- [Terminal 2](ssh://monitor1-ssh#2) で、下記コマンドを実行し、`test-branch-2` の接続環境変数を `.env-2` ファイルに保存します。

  ```bash
  cat > .env-2 <<EOF
  BRANCH_HOST=$DB_HOST
  BRANCH_PORT=$DB_PORT
  BRANCH_USERNAME=$DB_USERNAME
  BRANCH_PASSWORD=$DB_PASSWORD
  BRANCH_DATABASE=${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}
  EOF
  ```

- `test-branch-3` も同様に、パスワードを生成し「Connect with .env」を選んで「copy」アイコンをクリックしてください。

![env-file-branch-3.jpg 50%](https://lab-static.pingcap.com/images/2025/12/2/2c0584b8494b0cfaf347cc86203f0fb991e1d032.jpg)

- [Terminal 3](ssh://monitor1-ssh#3) をクリックし、コピーした `test-branch-3` の接続情報を貼り付けて環境変数に設定します。下記のような出力になります：

  ```output
  ubuntu@ip-10-90-1-65:~$ DB_HOST=gateway01.us-west-2.prod.aws.tidbcloud.com
  DB_PORT=4000
  DB_USERNAME='2SE7X5kxT7oxJU5.root'
  DB_PASSWORD='AlrsLuPTxy76zFdI'
  DB_DATABASE='lab_demo_203_6930011'
  ```

- [Terminal 3](ssh://monitor1-ssh#3) で、下記コマンドを実行し、`test-branch-3` の接続環境変数を `.env-3` ファイルに保存します。

  ```bash
  cat > .env-3 <<EOF
  BRANCH_HOST=$DB_HOST
  BRANCH_PORT=$DB_PORT
  BRANCH_USERNAME=$DB_USERNAME
  BRANCH_PASSWORD=$DB_PASSWORD
  BRANCH_DATABASE=${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}
  EOF
  ```

- いずれかのターミナル上で、3つの環境ファイル（.env-1、.env-2、.env-3）に3つの Branch の接続情報が正しく設定されているか確認します。3つの異なるユーザー名が見られるはずです。

  ```bash
  cat .env-1 .env-2 .env-3 | grep BRANCH_USERNAME
  ```

  ```output
  BRANCH_USERNAME=XXXXXXXXXXXXXXX.root
  BRANCH_USERNAME=YYYYYYYYYYYYYYY.root
  BRANCH_USERNAME=ZZZZZZZZZZZZZZZ.root
  ```

- [Terminal 1](ssh://monitor1-ssh#1) 上で `test-branch-1` へ接続します。

  ```bash
  source .env-1
  mysql \
  --comments -u $BRANCH_USERNAME -p$BRANCH_PASSWORD \
   -h $BRANCH_HOST -P $BRANCH_PORT \
   --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt
  ```

  ```output
  mysql>
  ```

- `test-branch-1` 上で、トランザクション分離レベルを「READ-COMMITTED」に設定します。

  ```sql
  SET GLOBAL transaction_isolation = 'READ-COMMITTED';
  SHOW GLOBAL VARIABLES LIKE 'transaction_isolation';
  EXIT
  ```

  ```output
  Query OK, 0 rows affected (0.18 sec)

  +-----------------------+----------------+
  | Variable_name         | Value          |
  +-----------------------+----------------+
  | transaction_isolation | READ-COMMITTED |
  +-----------------------+----------------+
  1 row in set (0.16 sec)

  Bye
  ```

- [Terminal 2](ssh://monitor1-ssh#2) 上で `test-branch-2` へ接続します。

  ```bash
  source .env-2
  mysql \
  --comments -u $BRANCH_USERNAME -p$BRANCH_PASSWORD \
   -h $BRANCH_HOST -P $BRANCH_PORT \
   --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt
  ```

  ```output
  mysql>
  ```

- `test-branch-2` 上で、トランザクション分離レベルを「READ-COMMITTED」に設定します。

  ```sql
  SET GLOBAL transaction_isolation = 'READ-COMMITTED';
  SHOW GLOBAL VARIABLES LIKE 'transaction_isolation';
  EXIT
  ```

  ```output
  Query OK, 0 rows affected (0.18 sec)

  +-----------------------+----------------+
  | Variable_name         | Value          |
  +-----------------------+----------------+
  | transaction_isolation | READ-COMMITTED |
  +-----------------------+----------------+
  1 row in set (0.16 sec)

  Bye
  ```

- [Terminal 3](ssh://monitor1-ssh#3) 上で `test-branch-3` へ接続します。

  ```bash
  source .env-3
  mysql \
  --comments -u $BRANCH_USERNAME -p$BRANCH_PASSWORD \
   -h $BRANCH_HOST -P $BRANCH_PORT \
   --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt
  ```

  ```output
  mysql>
  ```

- `test-branch-3` 上で、トランザクション分離レベルを「READ-COMMITTED」に設定します。

  ```sql
  SET GLOBAL transaction_isolation = 'READ-COMMITTED';
  SHOW GLOBAL VARIABLES LIKE 'transaction_isolation';
  EXIT
  ```

  ```output
  Query OK, 0 rows affected (0.18 sec)

  +-----------------------+----------------+
  | Variable_name         | Value          |
  +-----------------------+----------------+
  | transaction_isolation | READ-COMMITTED |
  +-----------------------+----------------+
  1 row in set (0.16 sec)

  Bye
  ```

### 手順 3. 3つのブランチで並列テストを実施する

- 3つのブランチが用意できたので、3種類の在庫差し引きソリューションを並行してテストできます。

- 3つのターミナル（[ターミナル1](ssh://monitor1-ssh#1)、[ターミナル2](ssh://monitor1-ssh#2)、[ターミナル3](ssh://monitor1-ssh#3)）が開かれていることを確認してください。

- 3つのブランチ用の環境ファイルが準備できていることも確認します（ステップ2でこの作業は済んでいます）。どのターミナルでも以下のコマンドでファイルの存在を確認できます。

  ```bash
  file .env-1 .env-2 .env-3
  ```

  ```output
  .env-1: ASCII text
  .env-2: ASCII text
  .env-3: ASCII text
  ```

- まず最初のPythonコードをデプロイします。このテストは「同じ商品を8つの同時セッションが購入する」シミュレーションです。
  - `book` テーブルの在庫数を `SELECT ... FOR UPDATE` でロックし確認します。
  - `order` テーブルに注文データを挿入します。
  - `book` テーブルの在庫を減らします。
  - これら全てが1つのトランザクション内で完結します。

![demo_branch_pic_02.png 50%](https://lab-static.pingcap.com/images/2025/11/27/5dacb92501bf6389d1e7b7c7a2f857801cd2b999.png)

- [ターミナル1](ssh://monitor1-ssh#1) で、Pythonファイル `demo_branch_1_test.py` を作成してください。

  ```bash
  cat > demo_branch_1_test.py <<'EOF'
  import mysql.connector
  from prettytable import PrettyTable
  import datetime
  import time
  import random
  import threading
  import sys,os
  from dotenv import load_dotenv

  load_dotenv(sys.argv[1])
  branch_host = os.environ.get("BRANCH_HOST")
  branch_port = os.environ.get("BRANCH_PORT")
  branch_username = os.environ.get("BRANCH_USERNAME")
  branch_password = os.environ.get("BRANCH_PASSWORD")
  branch_database = '${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}'

  config = {
    'user': branch_username,
    'password': branch_password,
    'host': branch_host,
    'database': branch_database,
    'raise_on_warnings': False,
    'port': branch_port,
  }

  # Simulate order and inventory processing for a connection, which continuously runs purchase simulations until inventory is depleted.
  def process_order_worker(user_id):
        # Establishing the connection
        conn = mysql.connector.connect(**config, buffered=True)

        try:
            while True:
                cursor = conn.cursor()

                # Enable transaction handling where each purchase activity is completed within a single transaction.
                cursor.execute("BEGIN")

                # Query the inventory of the product to be purchased and apply a lock on the product data.
                cursor.execute("""
                     SELECT stock FROM books
                     WHERE id = 6 FOR UPDATE
                 """)

                result = cursor.fetchone()

                # Check if the product is sold out.
                if not result or result[0] <= 0:
                    print(f"Stock is not enough, Stop!")
                    break

                current_stock = result[0]

                # Generate a random purchase quantity.
                purchase_quantity = random.randint(1, 10)

                if purchase_quantity > current_stock:
                    purchase_quantity = current_stock

                # Create an order record.
                cursor.execute("""
                     INSERT INTO orders(book_id, user_id, quality, ordered_at)
                     VALUES(6, %s, %s, CURRENT_TIMESTAMP)
                               """, (user_id, purchase_quantity,))

                # Reduce inventory.
                cursor.execute("""
                     UPDATE books
                     SET stock = stock - %s
                     WHERE id = 6
                           """,(purchase_quantity,))

                # If the purchase is successful, commit the transaction.
                conn.commit()

                print(f"User {user_id} successfully purchased {purchase_quantity} books, and the current remaining stock is {current_stock - purchase_quantity}")

                time.sleep(random.uniform(0.1, 0.5))

        except Exception as e:
            print(f"User {user_id} got error when process the order.")
            conn.rollback()
        finally:
            conn.close

  def main():

    # Simulate 8 sessions performing purchases concurrently.
    num_threads = 8

    start_time = 0
    end_time = 0

    print(f"Starting {num_threads} database worker threads...")

    # Create and start threads
    threads = []
    for i in range(num_threads):
        thread = threading.Thread(
            target=process_order_worker,
            args=(i + 1,),
            name=f"User-{i+1}",
            daemon=True
        )
        threads.append(thread)
        thread.start()
        print(f"{thread.name} thread started successfully!")

    print(f"All {num_threads} user threads started successfully!")

    start_time = time.time()

    try:
        # Keep the main thread alive - wait for threads to complete (they run indefinitely)
        for thread in threads:
            thread.join()

        end_time = time.time()

        conn = mysql.connector.connect(**config, buffered=True)

        # Print test results and test duration.
        try:
             cursor = conn.cursor()

             cursor.execute("""
                 SELECT stock FROM books
                 WHERE id = 6
             """)
             final_stock = cursor.fetchone()[0]

             cursor.execute("""
                 SELECT COUNT(*) as total_orders FROM orders WHERE book_id = 6
             """)
             total_orders = cursor.fetchone()[0]

             cursor.execute("""
                 SELECT SUM(quality) as total_quantity FROM orders WHERE book_id = 6
             """)
             total_quantity = cursor.fetchone()[0]

             duration_time = end_time - start_time

             print("=======================================")
             print(f"Final stock: {final_stock}")
             print(f"Total orders: {total_orders}")
             print(f"Total purchased books: {total_quantity}")
             print(f"Process duration: {duration_time:.2f} seconds.")
             print("Test finished！")
             print("=======================================")

        except Exception as e:
            print(f"Got error when get summary!")
            conn.rollback()
        finally:
            conn.close

    except KeyboardInterrupt:
        print("\nReceived interrupt signal. Stopping all threads...")
        sys.exit(0)

  if __name__ == "__main__":
    main()
  EOF
  ls -l demo_branch_1_test.py
  ```

  ```output
  -rw-rw-r-- 1 ubuntu ubuntu 4770 Nov 26 10:37 demo_branch_1_test.py
  ```

- 2つ目のテスト用Pythonコードをデプロイします。このテストは、8つの同時セッションが同じ商品を個別のトランザクションで購入するシミュレーションです:
  - 在庫を確認する（トランザクション1）
  - 注文レコードを挿入する（トランザクション2）
  - 在庫を減少させる（トランザクション3）

![demo_branch_pic_03.png 50%](https://lab-static.pingcap.com/images/2025/11/27/5c23d6698ceb6107676abbec140d556fa588e914.png)

- [ターミナル2](ssh://monitor1-ssh#2) で、Pythonコード `demo_branch_2_test.py` を作成してください。

  ```bash
  cat > demo_branch_2_test.py <<'EOF'
  import mysql.connector
  from prettytable import PrettyTable
  import datetime
  import time
  import random
  import threading
  import sys,os
  from dotenv import load_dotenv

  load_dotenv(sys.argv[1])
  branch_host = os.environ.get("BRANCH_HOST")
  branch_port = os.environ.get("BRANCH_PORT")
  branch_username = os.environ.get("BRANCH_USERNAME")
  branch_password = os.environ.get("BRANCH_PASSWORD")
  branch_database = '${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}'

  config = {
    'user': branch_username,
    'password': branch_password,
    'host': branch_host,
    'database': branch_database,
    'raise_on_warnings': False,
    'port': branch_port,
  }

  # Simulate order and inventory processing for a connection, which continuously runs purchase simulations until inventory is depleted.
  def process_order_worker(user_id):

        # Establishing the connection
        conn = mysql.connector.connect(**config, buffered=True)

        try:
            while True:
                cursor = conn.cursor()

                # transaction handling where each purchase activity is completed across multiple transactions.
                cursor.execute("BEGIN")

                # Transaction 1: Query the inventory of the product to be purchased.
                cursor.execute("""
                     SELECT stock FROM books
                     WHERE id = 6
                 """)

                result = cursor.fetchone()

                # Check if the product is sold out.
                if not result or result[0] <= 0:
                    print(f"Stock is not enough, Stop!")
                    break

                current_stock = result[0]

                # Generate a random purchase quantity.
                purchase_quantity = random.randint(1, 10)

                if purchase_quantity > current_stock:
                    purchase_quantity = current_stock

                conn.commit()

                # Transaction 2: Create an order record.
                cursor.execute("BEGIN")

                # Reduce inventory.
                cursor.execute("""
                     INSERT INTO orders(book_id, user_id, quality, ordered_at)
                     VALUES(6, %s, %s, CURRENT_TIMESTAMP)
                               """, (user_id, purchase_quantity,))
                conn.commit()

                # Transaction 3: Reduce inventory.
                cursor.execute("BEGIN")

                cursor.execute("""
                     UPDATE books
                     SET stock = stock - %s
                     WHERE id = 6
                           """,(purchase_quantity,))

                # If the purchase is successful, commit the transaction.
                conn.commit()

                print(f"User {user_id} successfully purchased {purchase_quantity} books, and the current remaining stock is {current_stock - purchase_quantity}")

                time.sleep(random.uniform(0.1, 0.5))

        except Exception as e:
            print(f"User {user_id} got error when process the order.")
            conn.rollback()
        finally:
            conn.close

  def main():

    # Simulate 8 sessions performing purchases concurrently.
    num_threads = 8

    start_time = 0
    end_time = 0

    print(f"Starting {num_threads} database worker threads...")

    # Create and start threads
    threads = []
    for i in range(num_threads):
        thread = threading.Thread(
            target=process_order_worker,
            args=(i + 1,),
            name=f"User-{i+1}",
            daemon=True
        )
        threads.append(thread)
        thread.start()
        print(f"{thread.name} thread started successfully!")

    print(f"All {num_threads} user threads started successfully!")

    start_time = time.time()

    # Print test results and test duration.
    try:
        # Keep the main thread alive - wait for threads to complete (they run indefinitely)
        for thread in threads:
            thread.join()

        end_time = time.time()

        conn = mysql.connector.connect(**config, buffered=True)

        try:
             cursor = conn.cursor()

             cursor.execute("""
                 SELECT stock FROM books
                 WHERE id = 6
             """)
             final_stock = cursor.fetchone()[0]

             cursor.execute("""
                 SELECT COUNT(*) as total_orders FROM orders WHERE book_id = 6
             """)
             total_orders = cursor.fetchone()[0]

             cursor.execute("""
                 SELECT SUM(quality) as total_quantity FROM orders WHERE book_id = 6
             """)
             total_quantity = cursor.fetchone()[0]

             duration_time = end_time - start_time

             print("=======================================")
             print(f"Final stock: {final_stock}")
             print(f"Total orders: {total_orders}")
             print(f"Total purchased books: {total_quantity}")
             print(f"Process duration: {duration_time:.2f} seconds.")
             print("Test finished！")
             print("=======================================")

        except Exception as e:
            print(f"Got error when get summary!")
            conn.rollback()
        finally:
            conn.close

    except KeyboardInterrupt:
        print("\nReceived interrupt signal. Stopping all threads...")
        sys.exit(0)

  if __name__ == "__main__":
    main()
  EOF
  ls -l demo_branch_2_test.py
  ```

  ```output
  -rw-rw-r-- 1 ubuntu ubuntu 4922 Nov 26 10:54 demo_branch_2_test.py
  ```

- 3つ目のテスト用Pythonコードをデプロイします。このテストは「同じ商品を8つの同時セッションが購入する」シミュレーションです:
  - ロックせずに在庫を確認します。
  - 注文テーブルに注文レコードを挿入します。
  - `book`テーブルの在庫を減少させます。
  - 全ての処理は1つのトランザクション内で完結します。

![demo_branch_pic_04.png 50%](https://lab-static.pingcap.com/images/2025/11/27/f5b72da437ba5da5fbbc597a5e1fe8cba271e8f9.png)

- [ターミナル3](ssh://monitor1-ssh#3) で、Pythonコード `demo_branch_3_test.py` を作成してください。

  ```bash
  cat > demo_branch_3_test.py <<'EOF'
  import mysql.connector
  from prettytable import PrettyTable
  import datetime
  import time
  import random
  import threading
  import sys,os
  from dotenv import load_dotenv

  load_dotenv(sys.argv[1])
  branch_host = os.environ.get("BRANCH_HOST")
  branch_port = os.environ.get("BRANCH_PORT")
  branch_username = os.environ.get("BRANCH_USERNAME")
  branch_password = os.environ.get("BRANCH_PASSWORD")
  branch_database = '${LAB:SERVERLESS_CLUSTER_DATABASE_NAME}'

  config = {
    'user': branch_username,
    'password': branch_password,
    'host': branch_host,
    'database': branch_database,
    'raise_on_warnings': False,
    'port': branch_port,
  }

  # Simulate order and inventory processing for a connection, which continuously runs purchase simulations until inventory is depleted.
  def process_order_worker(user_id):

        # Establishing the connection
        conn = mysql.connector.connect(**config, buffered=True)

        try:
            while True:
                cursor = conn.cursor()

                # Enable transaction handling where each purchase activity is completed within a single transaction.
                cursor.execute("BEGIN")

                # Query the inventory of the product to be purchased without applying a lock on the product data.
                cursor.execute("""
                     SELECT stock FROM books
                     WHERE id = 6
                 """)

                result = cursor.fetchone()

                # Check if the product is sold out.
                if not result or result[0] <= 0:
                    print(f"Stock is not enough, Stop!")
                    break

                current_stock = result[0]

                # Generate a random purchase quantity.
                purchase_quantity = random.randint(1, 10)

                if purchase_quantity > current_stock:
                    purchase_quantity = current_stock

                # Create an order record.
                cursor.execute("""
                     INSERT INTO orders(book_id, user_id, quality, ordered_at)
                     VALUES(6, %s, %s, CURRENT_TIMESTAMP)
                               """, (user_id, purchase_quantity,))

                # Reduce inventory.
                cursor.execute("""
                     UPDATE books
                     SET stock = stock - %s
                     WHERE id = 6
                           """,(purchase_quantity,))

                conn.commit()

                print(f"User {user_id} successfully purchased {purchase_quantity} books, and the current remaining stock is {current_stock - purchase_quantity}")

                time.sleep(random.uniform(0.1, 0.5))

        except Exception as e:
            print(f"User {user_id} got error when process the order.")
            conn.rollback()
        finally:
            conn.close

  def main():

    # Simulate 8 sessions performing purchases concurrently.
    num_threads = 8

    start_time = 0
    end_time = 0

    print(f"Starting {num_threads} database worker threads...")

    # Create and start threads
    threads = []
    for i in range(num_threads):
        thread = threading.Thread(
            target=process_order_worker,
            args=(i + 1,),
            name=f"User-{i+1}",
            daemon=True
        )
        threads.append(thread)
        thread.start()
        print(f"{thread.name} thread started successfully!")

    print(f"All {num_threads} user threads started successfully!")

    start_time = time.time()

    try:
        # Keep the main thread alive - wait for threads to complete (they run indefinitely)
        for thread in threads:
            thread.join()

        end_time = time.time()

        conn = mysql.connector.connect(**config, buffered=True)

        # Print test results and test duration.
        try:
             cursor = conn.cursor()

             cursor.execute("""
                 SELECT stock FROM books
                 WHERE id = 6
             """)
             final_stock = cursor.fetchone()[0]

             cursor.execute("""
                 SELECT COUNT(*) as total_orders FROM orders WHERE book_id = 6
             """)
             total_orders = cursor.fetchone()[0]

             cursor.execute("""
                 SELECT SUM(quality) as total_quantity FROM orders WHERE book_id = 6
             """)
             total_quantity = cursor.fetchone()[0]

             duration_time = end_time - start_time

             print("=======================================")
             print(f"Final stock: {final_stock}")
             print(f"Total orders: {total_orders}")
             print(f"Total purchased books: {total_quantity}")
             print(f"Process duration: {duration_time:.2f} seconds.")
             print("Test finished！")
             print("=======================================")

        except Exception as e:
            print(f"Got error when get summary!")
            conn.rollback()
        finally:
            conn.close

    except KeyboardInterrupt:
        print("\nReceived interrupt signal. Stopping all threads...")
        sys.exit(0)

  if __name__ == "__main__":
    main()
  EOF
  ls -l demo_branch_3_test.py
  ```

  ```output
  -rw-rw-r-- 1 ubuntu ubuntu 4682 Nov 26 11:05 demo_branch_3_test.py
  ```

### 手順 4. 並列テストを実行する

- [ターミナル1](ssh://monitor1-ssh#1) で `demo_branch_1_test.py` を実行します（待たずに次へ進んでください）。

  ```bash
  ~/.venv/bin/python demo_branch_1_test.py .env-1
  ```

  ```output
  Starting 8 database worker threads...
  User-1 thread started successfully!
  User-2 thread started successfully!
  User-3 thread started successfully!
  User-4 thread started successfully!
  User-5 thread started successfully!
  User-6 thread started successfully!
  User-7 thread started successfully!
  User-8 thread started successfully!
  All 8 user threads started successfully!
  User 1 successfully purchased 3 books, and
  ...
  ```

- [ターミナル2](ssh://monitor1-ssh#2) で `demo_branch_2_test.py` を実行します（続けて実行してください）。

  ```bash
  ~/.venv/bin/python demo_branch_2_test.py .env-2
  ```

  ```output
  Starting 8 database worker threads...
  User-1 thread started successfully!
  User-2 thread started successfully!
  User-3 thread started successfully!
  User-4 thread started successfully!
  User-5 thread started successfully!
  User-6 thread started successfully!
  User-7 thread started successfully!
  User-8 thread started successfully!
  All 8 user threads started successfully!
  User 7 successfully purchased 1 books, and
  ...
  ```

- [ターミナル3](ssh://monitor1-ssh#3) で `demo_branch_3_test.py` を実行します（続けて実行してください）。

  ```bash
  ~/.venv/bin/python demo_branch_3_test.py .env-3
  ```

  ```output
  Starting 8 database worker threads...
  User-1 thread started successfully!
  User-2 thread started successfully!
  User-3 thread started successfully!
  User-4 thread started successfully!
  User-5 thread started successfully!
  User-6 thread started successfully!
  User-7 thread started successfully!
  User-8 thread started successfully!
  All 8 user threads started successfully!
  ...
  ```

- 3つのテストがすべて終了するまで監視します。[ターミナル3](ssh://monitor1-ssh#3) で `test-branch-3` の結果を見ると、実行時間は最も短いですが、在庫がマイナスとなり（売り越し発生）、この解決策には問題があります。※結果はラボ環境により異なる場合があります。最初に売り越しが発生しない場合は、複数回テストを実施してください。

  ```output
  ...
  User 4 successfully purchased 4 books, and the current remaining stock is 23
  User 7 successfully purchased 7 books, and the current remaining stock is 15
  User 6 successfully purchased 8 books, and the current remaining stock is 5
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  User 2 successfully purchased 8 books, and the current remaining stock is 5
  User 3 successfully purchased 6 books, and the current remaining stock is 7
  Stock is not enough, Stop!
  User 8 successfully purchased 2 books, and the current remaining stock is 0
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  =======================================
  Final stock: -22
  Total orders: 370
  Total purchased books: 2022
  Process duration: 68.15 seconds.
  Test finished！
  =======================================
  ```

- 続いて [ターミナル2](ssh://monitor1-ssh#2) で `test-branch-2` の実行結果を確認します。実行時間は中程度で、こちらも売り越しが発生する場合があります。結果はラボ環境により異なります。最初に売り越しが発生しない場合は、複数回テストを実施してください。

  ```output
  ...
  User 3 successfully purchased 6 books, and the current remaining stock is 18
  User 8 successfully purchased 3 books, and the current remaining stock is 11
  User 5 successfully purchased 9 books, and the current remaining stock is 1
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  User 2 successfully purchased 4 books, and the current remaining stock is 6
  User 7 successfully purchased 1 books, and the current remaining stock is 0
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  =======================================
  Final stock: -36
  Total orders: 377
  Total purchased books: 2036
  Process duration: 97.72 seconds.
  Test finished！
  =======================================
  ```

- 最後に [ターミナル1](ssh://monitor1-ssh#1) で `test-branch-1` の実行結果を確認します。実行時間は最も長いですが、在庫は正しく0となり、このアルゴリズムは期待通りに動作しています。

  ```output
  ...
  User 5 successfully purchased 6 books, and the current remaining stock is 11
  User 3 successfully purchased 5 books, and the current remaining stock is 6
  User 1 successfully purchased 3 books, and the current remaining stock is 3
  User 6 successfully purchased 2 books, and the current remaining stock is 1
  User 2 successfully purchased 1 books, and the current remaining stock is 0
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  Stock is not enough, Stop!
  =======================================
  Final stock: 0
  Total orders: 363
  Total purchased books: 2000
  Process duration: 181.34 seconds.
  Test finished！
  =======================================
  ```

### 手順 5. 次のテストフェーズに進む

- このステップでは、本モジュールの成功したテスト結果をもとに、次のモジュールのテストを続行したいと考えています。そのため、テストが成功／失敗どちらの場合も、データを保持しておく必要があります。

![demo_branch_pic_05.png 50%](https://lab-static.pingcap.com/images/2025/11/27/1e3707c00fbf73158eddf3f321b37edf31a22ba2.png)

- [ターミナル1]のアルゴリズムが期待通り動作したので、そのBranchを使ってさらなるテストを行い、全テストデータを保存しておきます。[Clustersページ](https://tidbcloud.com/project/clusters) にアクセスし、ラボクラスター名をクリックします。`Create New Cluster` からラボを開始した場合、クラスター名は `${LAB:SERVERLESS_CLUSTER_NAME}` です。

![Demo-RU-001.png 85%](https://lab-static.pingcap.com/images/2025/11/24/9c016ddc77e89cb9d9ae0669890d36cb316f6d20.png)

- クラスター名 `${LAB:SERVERLESS_CLUSTER_NAME}` をクリックし、左上の「Overview」を選択します。

![Demo-RU-002.png 85%](https://lab-static.pingcap.com/images/2025/11/24/8d064cdda27759df1640171cbf92f7d340e4364d.png)

- 左側の「Branches」タブをクリックして、Branch パネルを開きます。

![demo_branch_UI_01.png 85%](https://lab-static.pingcap.com/images/2025/11/27/c094e9effcea9e16423f52268a3f03be1d9f27fe.png)

- 右上の「Create Branch」ボタンをクリックし、新しいBranchを作成します。

![demo_branch_UI_13.png 85%](https://lab-static.pingcap.com/images/2025/11/27/5009833f8220f1851e54711e7ce3eec3bb8b0e90.png)

- 表示される「Create Branch」ウィンドウで、Name欄に `test-branch-1-1`、Parent branch欄で `test-branch-1` を選択し、Include data up to欄では「Current point in time」を選択します。これで `test-branch-1` の現在時点のデータスナップショット付きで `test-branch-1-1` が作成されます。Createボタンをクリックしてください。

![demo_branch_UI_14.png 50%](https://lab-static.pingcap.com/images/2025/11/27/ce6719b47458cca94d43ef005bde46298527f39d.png)

- `test-branch-1-1` のステータスが「Active」になるまでお待ちください。このBranchで今後のテストを行いつつ、`test-branch-1`, `test-branch-2`, `test-branch-3` は回帰テスト用に残しておきます。

![demo_branch_UI_15.png 85%](https://lab-static.pingcap.com/images/2025/11/27/4d3232d62da501d83d91d9277479b4ddf8dfa798.png)

### 手順 6. クリーンアップ

- [Clustersページ](https://tidbcloud.com/project/clusters) にアクセスし、ラボクラスター名をクリックします。`Create New Cluster` からラボを開始した場合、クラスター名は `${LAB:SERVERLESS_CLUSTER_NAME}` です。

![Demo-RU-001.png 85%](https://lab-static.pingcap.com/images/2025/11/24/9c016ddc77e89cb9d9ae0669890d36cb316f6d20.png)

- クラスター名 `${LAB:SERVERLESS_CLUSTER_NAME}` をクリックし、左上の「Overview」を選択します。

![Demo-RU-002.png 85%](https://lab-static.pingcap.com/images/2025/11/24/8d064cdda27759df1640171cbf92f7d340e4364d.png)

- 左側の「Branches」タブをクリックして、Branches パネルを開きます。

![demo_branch_UI_01.png 85%](https://lab-static.pingcap.com/images/2025/11/27/c094e9effcea9e16423f52268a3f03be1d9f27fe.png)

- Branches パネルで `test-branch-1-1` を選択し、「Action」列の `...` をクリックして「Delete」を選びます。

![demo_branch_UI_16.png 85%](https://lab-static.pingcap.com/images/2025/11/27/bd50c1b3885fe2492471bc20d2005f327d7a20ad.png)

- 確認ウィンドウで「I understand, delete」をクリックします。

![demo_branch_UI_17.png 50%](https://lab-static.pingcap.com/images/2025/11/27/72bedd61a6ce908307edb8e6950df88c56466ede.png)

- 同様の操作で `test-branch-2`, `test-branch-3`, `test-branch-1` も削除します。

![demo_branch_UI_18.png 85%](https://lab-static.pingcap.com/images/2025/11/27/dc0a91b1c61ac243809ad40a31f73ffca2afa7d8.png)

> [!NOTE]
> このモジュールでは、3つのBranchを手動で管理・操作しました。今後のラボではAPIやAIエージェントを利用し、より多くのBranchを自動管理・並列化する手法もご紹介していきます。ご期待ください！
