# Implementing Hot and Cold Data Storage Tiering

### Overview
+ In these exercises, you will gain experience with TiDB's Placement Rules in SQL, a feature that allows you to specify where data is stored in a TiDB using SQL statements.

### Notes
+ Some of the steps require that you type commands in two different terminal windows. You might find it convenient to launch two terminals and keep them open for the duration of the exercises.
+ Your environment may be different with these exercises, including but not limited to `IP`, `port`, `username`, `password` and `directory`.
+ The `$` in your terminal window is an OS prompt.
+ The "`tidb>`" or "`mysql>`" in your terminal window is SQL command prompt.
+ These exercises can be run on a single machine, such as your laptop.
+ This exercise uses TiDB Playground Cluster. We need to take full control over the labels on PD and TiKV. Whereas in TiDB Cloud, it is managed by Database as a Service.
+ All the steps you practice in these exercises are similar to a real cluster environment; for further learning on how to manage a production TiDB cluster, you can take the DBA-oriented courses provided by [PingCAP Education](https://pingcap.com/education/). It is crucial to note that the TiDB Playground cluster is not designed for production purposes.

### Assumptions
+ You are using macOS or Linux.
+ You have cloned the [lab repository](https://github.com/pingcap/tidb-course-201-lab.git).
+ You have completed the Exercises - Creating a TiDB Cluster for Testing Purposes.

## Exercise 1: Cold and Hot Data Placement Control

### Overview
+ You practice using the placement rules to separate cold data to `lower-cost` storage (or low performance node) and hot data to `higher-cost` storage (or high performance node) with table partitions to improve cost efficiency.

  <img src="./diagram/PlacementRuleColdHot_en_001_20230422.png" width="50%" align="top"/>

### Duration
+ This exercise should take you approximately 30 minutes to complete.

### Tasks
1. Terminate the `classroom` Playground cluster if it is still running.
2. Deploy a new Playground cluster tagged with `classroom-pr` to simulate TiKV nodes with two storage configurations: `lower-cost` and `higher-cost`.
3. Create placement policies for designating hot data to TiKV nodes with `higher-cost` storage attached, and cold data to TiKV nodes with `lower-cost` storage attached.
4. Create a range-partitioned table, utilizing placement policies in the previous step to maximize storage cost efficiency.
5. Verify that the placement rules had taken effect.
6. Clean up the `classroom-pr` TiDB Playground Cluster.

### Solution
1. Terminate the `classroom` Playground cluster if it is still running.

      Go to the terminal where you previously executed `playground-start.sh`. The screen might look like this:
      ```
      ...
      To view the dashboard: http://127.0.0.1:2379/dashboard
      PD client endpoints: [127.0.0.1:2379 127.0.0.1:2382 127.0.0.1:2384]
      To view the Prometheus: http://127.0.0.1:9090
      To view the Grafana: http://127.0.0.1:3000
      ```

      Press `Ctrl-C` once to abort the entire Playground cluster. All PD, TiKV, TiDB, and TiFlash instances will be terminated:
      ```
      ^CPlayground receive signal:  interrupt
      Got signal interrupt (Component: playground ; PID: 47472)
      ...
      tikv quit
      ```

2. Deploy a new TiDB Playground Cluster with the `classroom-pr` tag that serves as a simulation of TiKV nodes and has two distinct storage configurations. One configuration is designed for hosting `lower-cost` cold data, while the other is better-suited for handling `higher-cost` hot data. Label these storage configurations with the `storage-performance` key. For this exercise, you can ignore the `region` label key, which is not utilized in the exercises. However, it's worth noting that `"region"="tokyo"`, indicating that the TiKV nodes are deployed in Tokyo.

      Under the working directory `tidb-course-201-lab/scripts`, check the content of a TiKV configuration file `misc/label-storage-higher-cost.toml`. It can add two labels to TiKV nodes, `(region, tokyo)` and `(storage-performance, high)`. The label keys are `region` and `storage-performance`, the label values are `tokyo` and `high`, which means the label is suitable for those TiKV nodes that are deployed in Tokyo and with higher performance (cost) storage attached:
      ```
      $ cd tidb-course-201-lab/scripts
      $ cat misc/label-storage-higher-cost.toml
      [server]
      labels = {"region"="tokyo", "storage-performance"="high"}
      ```

      Check another TiKV configuration file `misc/label-storage-lower-cost.toml`. This time the `storage-performance` value is `low` and the `region` is still `Tokyo`, which means the label is suitable for those TiKV nodes that are deployed in Tokyo and with lower performance (cost) storage attached:
      ```
      $ cat misc/label-storage-lower-cost.toml
      [server]
      labels = {"region"="tokyo", "storage-performance"="low"}
      ```

      Review the script `playground-init-classroom-pr-01.sh`, it can deploy (or re-deploy) a new Playground, tagged with `classroom-pr`, on the local machine. Note that it will be initialized with three `higher-cost` storage TiKV nodes. Please note that this is a simulation, since each "node" or "instance" is actually running on the same local machine: 
      ```
      $ cat ./playground-init-classroom-pr-01.sh 
      #!/bin/bash

      ~/.tiup/bin/tiup clean classroom-pr

      # Higher cost storage TiKV node + 3
      sleep 1;
      ~/.tiup/bin/tiup playground v6.5.1 --tag classroom-pr \
      --pd 1 \
      --kv 3 \
      --db 0 \
      --tiflash 0 \
      --without-monitor \
      --kv.config ./misc/label-storage-higher-cost.toml
      ```

      Run script `playground-init-classroom-pr-01.sh`:
      ```
      $ ./playground-init-classroom-pr-01.sh
      ...
      Start pd instance:v6.5.1
      Start tikv instance:v6.5.1
      Start tikv instance:v6.5.1
      Start tikv instance:v6.5.1
      ```

      Switch to another terminal, review the script `playground-init-classroom-pr-02.sh`, it adds one TiDB server and scale-out the TiKV cluster for your Playground with three `lower-cost` storage TiKV nodes:
      ```
      $ cd tidb-course-201-lab/scripts
      $ cat playground-init-classroom-pr-02.sh
      #!/bin/bash

      # Adding one TiDB server 
      ~/.tiup/bin/tiup playground scale-out --db 1

      # Lower cost storage TiKV node + 3
      sleep 1;
      ~/.tiup/bin/tiup playground scale-out --kv 3 --kv.config ./misc/label-storage-lower-cost.toml

      ~/.tiup/bin/tiup playground display
      ``` 

      Run the script `playground-init-classroom-pr-02.sh`. Upon completion, you will have a total of 6 TiKV instances, with 3 labeled as high and 3 labeled as low in terms of storage performance.
      ```
      $ ./playground-init-classroom-pr-02.sh 
      ...
      Pid    Role  Uptime
      ---    ----  ------
      95771  pd    1m16.365078657s
      95772  tikv  1m16.297747426s
      95773  tikv  1m16.234072579s
      95774  tikv  1m16.16758214s
      95819  tikv  905.153455ms
      95820  tikv  839.390523ms
      95821  tikv  775.561918ms
      95807  tidb  22.953849065s
      ```

3. Create placement policies for designating hot data to TiKV nodes with `higher-cost` storage attached (you have 3), and cold data to TiKV nodes with `lower-cost` storage attached (you have 3).

      Connect to TiDB server as root: 
      ```
      $ ./connect-4000.sh
      tidb:4000>
      ```

      Make sure the TiKV region replicas setting is 3 for your Playground, this is the minimal settings for replicas in most production environment:
      ```sql
      SET CONFIG PD replication.max-replicas = 3;
      ```
      ```
      tidb:4000> SET CONFIG PD replication.max-replicas = 3;
      Query OK, 0 rows affected (0.03 sec)
      ```

      Verify the available labels for placement rules. Pay attention to the label with key `storage-performance`, it's safe to ignore `engine` and `region` for now:
      ```sql
      SHOW PLACEMENT LABELS;
      ```
      ```
      tidb:4000> SHOW PLACEMENT LABELS;
      +---------------------+-----------------+
      | Key                 | Values          |
      +---------------------+-----------------+
      | region              | ["tokyo"]       |
      | storage-performance | ["high", "low"] |
      +---------------------+-----------------+
      3 rows in set (0.01 sec)
      ```

      Check the available labels and their corresponding TiKV stores. Note that the `store_id` column represents the TiKV store ID, and the `label` column indicates the labeled key-values for each TiKV store:
      ```sql
      SELECT store_id, address, store_state_name, label
      FROM information_schema.tikv_store_status;
      ```
      ```
      tidb:4000> SELECT store_id, address, store_state_name, label
          -> FROM information_schema.tikv_store_status;
      +----------+-----------------+------------------+----------------------------------------------------------------------------------------+
      | store_id | address         | store_state_name | label                                                                                  |
      +----------+-----------------+------------------+----------------------------------------------------------------------------------------+
      |        1 | 127.0.0.1:20162 | Up               | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "high"}] |
      |        4 | 127.0.0.1:20161 | Up               | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "high"}] |
      |        5 | 127.0.0.1:20160 | Up               | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "high"}] |
      |      136 | 127.0.0.1:49829 | Up               | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "low"}]  |
      |      137 | 127.0.0.1:49831 | Up               | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "low"}]  |
      |      138 | 127.0.0.1:49837 | Up               | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "low"}]  |
      +----------+-----------------+------------------+----------------------------------------------------------------------------------------+
      6 rows in set (0.00 sec)
      ```

      Create a placement policy for hot data, mapping it to the label key-value pair: `(storage-performance, high)`:
      ```sql
      DROP PLACEMENT POLICY IF EXISTS hot;
      CREATE PLACEMENT POLICY hot
        CONSTRAINTS="[+storage-performance=high]";
      ```
      ```
      tidb:4000> DROP PLACEMENT POLICY IF EXISTS hot;
      Query OK, 0 rows affected, 1 warning (0.00 sec)

      tidb:4000> CREATE PLACEMENT POLICY hot
          ->   CONSTRAINTS="[+storage-performance=high]";
      Query OK, 0 rows affected (0.07 sec)
      ```

      Create a placement policy for cold data, mapping it to the label key-value pair: `(storage-performance, low)`:
      ```sql
      DROP PLACEMENT POLICY IF EXISTS cold;
      CREATE PLACEMENT POLICY cold
        CONSTRAINTS="[+storage-performance=low]";
      ```
      ```
      tidb:4000> DROP PLACEMENT POLICY IF EXISTS cold;
      Query OK, 0 rows affected, 1 warning (0.01 sec)

      tidb:4000> CREATE PLACEMENT POLICY cold
          ->   CONSTRAINTS="[+storage-performance=low]";
      Query OK, 0 rows affected (0.07 sec)
      ```

      Verify the constraint definitions of created placement policies, `hot` and `cold`:
      ```sql
      SELECT policy_name, constraints FROM information_schema.placement_policies;
      ```
      ```
      tidb:4000> SELECT policy_name, constraints FROM information_schema.placement_policies;
      +-------------+-----------------------------+
      | policy_name | constraints                 |
      +-------------+-----------------------------+
      | hot         | [+storage-performance=high] |
      | cold        | [+storage-performance=low]  |
      +-------------+-----------------------------+
      2 rows in set (0.00 sec)
      ```

4. Create two range-partitioned tables utilizing placement policies in the previous step, one with hot-cold partitions and the other with cold-only partitions, to maximize the storage cost efficiency.

      Create a partitioned table `test.orders` using `hot` and `cold` placement policies. Note that orders with an `order_date` before 2010 and between 2010 and 2015 (exclusive) are marked as `cold`, while those with an `order_date` after 2015 are marked as `hot` which mapped to the `MAXVALUE` partition:
      ```sql
      DROP TABLE IF EXISTS test.orders;
      CREATE TABLE test.orders (order_id INT, item VARCHAR(50), order_date DATE, PRIMARY KEY (order_date, order_id))
      PARTITION BY RANGE( YEAR(order_date) ) (
        PARTITION p10 VALUES LESS THAN (2010) PLACEMENT POLICY=cold,
        PARTITION p15 VALUES LESS THAN (2015) PLACEMENT POLICY=cold,
        PARTITION pm VALUES LESS THAN MAXVALUE PLACEMENT POLICY=hot
      );
      ```
      ```
      tidb:4000> DROP TABLE IF EXISTS test.orders;
      Query OK, 0 rows affected, 1 warning (0.00 sec)

      tidb:4000> CREATE TABLE test.orders (order_id INT, item VARCHAR(50), order_date DATE, PRIMARY KEY (order_date, order_id))
          -> PARTITION BY RANGE( YEAR(order_date) ) (
          ->   PARTITION p10 VALUES LESS THAN (2010) PLACEMENT POLICY=cold,
          ->   PARTITION p15 VALUES LESS THAN (2015) PLACEMENT POLICY=cold,
          ->   PARTITION pm VALUES LESS THAN MAXVALUE PLACEMENT POLICY=hot
          -> );
      Query OK, 0 rows affected (0.34 sec)
      ```

      Create a partitioned table `test.orders_log` using the `cold` placement policy only for all its partitions:
      ```sql
      DROP TABLE IF EXISTS test.orders_log;
      CREATE TABLE test.orders_log (order_id INT, item VARCHAR(50), order_date DATE, PRIMARY KEY (order_date, order_id))
      PARTITION BY RANGE( YEAR(order_date) ) (
        PARTITION p10 VALUES LESS THAN (2010) PLACEMENT POLICY=cold,
        PARTITION p15 VALUES LESS THAN (2015) PLACEMENT POLICY=cold,
        PARTITION pm VALUES LESS THAN MAXVALUE PLACEMENT POLICY=cold
      );
      ```
      ```
      tidb:4000> DROP TABLE IF EXISTS test.orders_log;
      Query OK, 0 rows affected, 1 warning (0.00 sec)

      tidb:4000> CREATE TABLE test.orders_log (order_id INT, item VARCHAR(50), order_date DATE, PRIMARY KEY (order_date, order_id))
          -> PARTITION BY RANGE( YEAR(order_date) ) (
          ->   PARTITION p10 VALUES LESS THAN (2010) PLACEMENT POLICY=cold,
          ->   PARTITION p15 VALUES LESS THAN (2015) PLACEMENT POLICY=cold,
          ->   PARTITION pm VALUES LESS THAN MAXVALUE PLACEMENT POLICY=cold
          -> );
      Query OK, 0 rows affected (0.38 sec)
      ```
  
5. Verify that the placement rules had taken effect.

      Show TiKV region information about the hot-cold table `test.orders`. Pay attention to the `REGION_ID` with the highest `START_KEY`, it's the `MAXVALUE` partition. Note that you might have different numbering for region id and start keys in your environment:
      ```sql
      SHOW TABLE test.orders REGIONS\G
      ```
      ```
      tidb:4000> SHOW TABLE test.orders REGIONS\G
      --------------
      SHOW TABLE test.orders REGIONS
      --------------

      *************************** 1. row ***************************
                   REGION_ID: 199
                   START_KEY: t_83_
                     END_KEY: t_84_
                   LEADER_ID: 211
             LEADER_STORE_ID: 182
                       PEERS: 211, 216, 218
                  SCATTERING: 0
               WRITTEN_BYTES: 136
                  READ_BYTES: 0
        APPROXIMATE_SIZE(MB): 1
            APPROXIMATE_KEYS: 0
      SCHEDULING_CONSTRAINTS: CONSTRAINTS="[+storage-performance=low]"
            SCHEDULING_STATE: SCHEDULED
      *************************** 2. row ***************************
                   REGION_ID: 203
                   START_KEY: t_84_
                     END_KEY: t_85_
                   LEADER_ID: 212
             LEADER_STORE_ID: 182
                       PEERS: 212, 217, 220
                  SCATTERING: 0
               WRITTEN_BYTES: 136
                  READ_BYTES: 0
        APPROXIMATE_SIZE(MB): 1
            APPROXIMATE_KEYS: 0
      SCHEDULING_CONSTRAINTS: CONSTRAINTS="[+storage-performance=low]"
            SCHEDULING_STATE: SCHEDULED
      *************************** 3. row ***************************
                   REGION_ID: 207
                   START_KEY: t_85_
                     END_KEY: t_86_
                   LEADER_ID: 210
             LEADER_STORE_ID: 3
                       PEERS: 208, 209, 210
                  SCATTERING: 0
               WRITTEN_BYTES: 39
                  READ_BYTES: 0
        APPROXIMATE_SIZE(MB): 1
            APPROXIMATE_KEYS: 0
      SCHEDULING_CONSTRAINTS: CONSTRAINTS="[+storage-performance=high]"
            SCHEDULING_STATE: SCHEDULED
      3 rows in set (0.02 sec)
      ```

      Verify the regions and the TiKV stores mapping for table `test.orders` is aligned with the placement policy. According to the placement rules, `REGION_ID` 207's region should be mapped to TiKV node with `storage-performance:high`, whereas the other two regions, 199 and 203 should be mapped to TiKV nodes with `storage-performance:low`. Note that you might have different numbering for region id and start keys in your environment:
      ```sql
      SELECT kvrs.region_id, kvss.store_id, kvrp.is_leader, kvss.label, kvss.start_ts
        FROM information_schema.tikv_region_status kvrs
        JOIN information_schema.tikv_region_peers kvrp
        ON kvrs.region_id = kvrp.region_id
        JOIN information_schema.tikv_store_status kvss
        ON kvrp.store_id = kvss.store_id
        JOIN information_schema.tables t
        ON t.table_name = kvrs.table_name
        WHERE t.table_schema='test' 
        AND t.table_name='orders'
        AND kvrs.is_index = 0
        AND kvrp.is_leader = 1;
      ```
      ```
      tidb:4000> SELECT kvrs.region_id, kvss.store_id, kvrp.is_leader, kvss.label, kvss.start_ts
      ...
      --------------

      +-----------+----------+-----------+----------------------------------------------------------------------------------------+---------------------+
      | region_id | store_id | is_leader | label                                                                                  | start_ts            |
      +-----------+----------+-----------+----------------------------------------------------------------------------------------+---------------------+
      |       203 |      182 |         1 | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "low"}]  | 2023-06-21 10:42:46 |
      |       199 |      182 |         1 | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "low"}]  | 2023-06-21 10:42:46 |
      |       207 |        3 |         1 | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "high"}] | 2023-06-21 10:40:26 |
      +-----------+----------+-----------+----------------------------------------------------------------------------------------+---------------------+
      3 rows in set (0.04 sec)
      ```

      Show TiKV region information about the cold-data-only table `test.orders_log`. Pay attention to the `REGION_ID` for all regions, in the example output, they are 226, 230 and 234 (note that you might have different numbering for region id and start keys in your environment):
      ```sql
      SHOW TABLE test.orders_log REGIONS\G
      ```
      ```
      tidb:4000> SHOW TABLE test.orders_log REGIONS\G
      --------------
      SHOW TABLE test.orders_log REGIONS
      --------------

      *************************** 1. row ***************************
                   REGION_ID: 226
                   START_KEY: t_88_
                     END_KEY: t_89_
                   LEADER_ID: 239
             LEADER_STORE_ID: 182
                       PEERS: 239, 247, 250
                  SCATTERING: 0
               WRITTEN_BYTES: 136
                  READ_BYTES: 0
        APPROXIMATE_SIZE(MB): 1
            APPROXIMATE_KEYS: 0
      SCHEDULING_CONSTRAINTS: CONSTRAINTS="[+storage-performance=low]"
            SCHEDULING_STATE: SCHEDULED
      *************************** 2. row ***************************
                   REGION_ID: 230
                   START_KEY: t_89_
                     END_KEY: t_90_
                   LEADER_ID: 238
             LEADER_STORE_ID: 182
                       PEERS: 238, 245, 248
                  SCATTERING: 0
               WRITTEN_BYTES: 136
                  READ_BYTES: 0
        APPROXIMATE_SIZE(MB): 1
            APPROXIMATE_KEYS: 0
      SCHEDULING_CONSTRAINTS: CONSTRAINTS="[+storage-performance=low]"
            SCHEDULING_STATE: SCHEDULED
      *************************** 3. row ***************************
                   REGION_ID: 234
                   START_KEY: t_90_
                     END_KEY: t_91_
                   LEADER_ID: 240
             LEADER_STORE_ID: 182
                       PEERS: 240, 252, 253
                  SCATTERING: 0
               WRITTEN_BYTES: 136
                  READ_BYTES: 0
        APPROXIMATE_SIZE(MB): 1
            APPROXIMATE_KEYS: 0
      SCHEDULING_CONSTRAINTS: CONSTRAINTS="[+storage-performance=low]"
            SCHEDULING_STATE: SCHEDULED
      3 rows in set (0.01 sec)
      ```

      Verify the regions and the TiKV stores mapping for table `test.orders_log` is aligned with the placement policy. According to the placement rules, all regions should be mapped to TiKV nodes with `storage-performance:low`:
      ```sql
      SELECT kvrs.region_id, kvss.store_id, kvrp.is_leader, kvss.label, kvss.start_ts
        FROM information_schema.tikv_region_status kvrs
        JOIN information_schema.tikv_region_peers kvrp
        ON kvrs.region_id = kvrp.region_id
        JOIN information_schema.tikv_store_status kvss
        ON kvrp.store_id = kvss.store_id
        JOIN information_schema.tables t
        ON t.table_name = kvrs.table_name
        WHERE t.table_schema='test' 
        AND t.table_name='orders_log'
        AND kvrs.is_index = 0
        AND kvrp.is_leader = 1;
      ```
      ```
      ...
      --------------

      +-----------+----------+-----------+---------------------------------------------------------------------------------------+---------------------+
      | region_id | store_id | is_leader | label                                                                                 | start_ts            |
      +-----------+----------+-----------+---------------------------------------------------------------------------------------+---------------------+
      |       230 |      182 |         1 | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "low"}] | 2023-06-21 10:42:46 |
      |       234 |      182 |         1 | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "low"}] | 2023-06-21 10:42:46 |
      |       226 |      182 |         1 | [{"key": "region", "value": "tokyo"}, {"key": "storage-performance", "value": "low"}] | 2023-06-21 10:42:46 |
      +-----------+----------+-----------+---------------------------------------------------------------------------------------+---------------------+
      3 rows in set (0.02 sec)
      ```

      Run a query trace to verify the partition pruning for table `test.orders`. The query scan on the cold storage is anticipated, as you are querying data in 2013. From the output, observe that the region with region ID 203 is scanned, which is on `storage-performance:low` labeled TiKV node:
      ```sql
      TRACE
      SELECT item, order_date FROM test.orders
      WHERE order_date BETWEEN STR_TO_DATE('01/01/2013', '%m/%d/%Y') AND STR_TO_DATE('12/31/2013', '%m/%d/%Y');
      ```
      ```
      tidb:4000> TRACE
          -> SELECT item, order_date FROM test.orders
          -> WHERE order_date BETWEEN STR_TO_DATE('01/01/2013', '%m/%d/%Y') AND STR_TO_DATE('12/31/2013', '%m/%d/%Y');
      +----------------------------------------------------------------+-----------------+------------+
      | operation                                                      | startTS         | duration   |
      +----------------------------------------------------------------+-----------------+------------+
      | trace                                                          | 00:47:24.951954 | 8.606975ms |
      |   ├─session.ExecuteStmt                                        | 00:47:24.951965 | 5.072329ms |
      |   │ ├─executor.Compile                                         | 00:47:24.951984 | 3.676066ms |
      |   │ └─session.runStmt                                          | 00:47:24.956317 | 687.5µs    |
      |   └─*executor.IndexLookUpExecutor.Next                         | 00:47:24.957047 | 3.483774ms |
      |     └─distsql.Select                                           | 00:47:24.957089 | 26.993µs   |
      |       └─regionRequest.SendReqCtx                               | 00:47:24.957484 | 2.998185ms |
      |         └─rpcClient.SendRequest, region ID: 203, type: Cop     | 00:47:24.958109 | 2.038166ms |
      +----------------------------------------------------------------+-----------------+------------+
      8 rows in set (0.02 sec)
      ```

      Run a query trace to verify the partition pruning for table `test.orders` again. The query scan on the hot storage is anticipated, as you are querying data between 2018 and 2020. From the output, observe that the region with region ID 207 is scanned, which is on `storage-performance:high` labeled TiKV node:
      ```sql
      TRACE
      SELECT item, order_date FROM test.orders
      WHERE order_date BETWEEN STR_TO_DATE('01/01/2018', '%m/%d/%Y') AND STR_TO_DATE('12/31/2020', '%m/%d/%Y');
      ```
      ```
      tidb:4000> TRACE
          -> SELECT item, order_date FROM test.orders
          -> WHERE order_date BETWEEN STR_TO_DATE('01/01/2018', '%m/%d/%Y') AND STR_TO_DATE('12/31/2020', '%m/%d/%Y');
      +----------------------------------------------------------------+-----------------+------------+
      | operation                                                      | startTS         | duration   |
      +----------------------------------------------------------------+-----------------+------------+
      | trace                                                          | 00:48:02.290148 | 2.240812ms |
      |   ├─session.ExecuteStmt                                        | 00:48:02.290152 | 446.907µs  |
      |   │ ├─executor.Compile                                         | 00:48:02.290168 | 308.025µs  |
      |   │ └─session.runStmt                                          | 00:48:02.290495 | 83.827µs   |
      |   └─*executor.IndexLookUpExecutor.Next                         | 00:48:02.290609 | 1.749ms    |
      |     └─distsql.Select                                           | 00:48:02.290653 | 35.851µs   |
      |       └─regionRequest.SendReqCtx                               | 00:48:02.290745 | 1.567311ms |
      |         └─rpcClient.SendRequest, region ID: 207, type: Cop     | 00:48:02.290763 | 1.510832ms |
      +----------------------------------------------------------------+-----------------+------------+
      8 rows in set (0.01 sec)
      ```

      If you have more time, you can try similar queries against the table `test.orders_log`, which can only be landed on cold TiKV nodes.

6. Clean up the `classroom-pr` Playground.
    
    On the terminal where the `classroom-pr` Playground is running, press `Ctrl-C` once to abort the entire Playground cluster. All PD, TiKV, and TiDB instances will be terminated, the screen might look like this:
    ```
    Ctrl-C
    ```
    ```
    ^CPlayground receive signal:  interrupt
    Got signal interrupt (Component: playground ; PID: 96359)
    ```

    Run `tiup clean` to clear the data folder for `classroom-pr` Playground:
    ```
    $ ~/.tiup/bin/tiup clean classroom-pr
    Clean instance of `playground`, directory: /Users/username/.tiup/data/classroom-pr
    ```

## Supplementary
The exercise is intended to be accomplished through the TiDB Playground Cluster environment on a single node machine, which is not suitable for production purposes. In case you need a more sophisticated testing or demonstration for production purposes, [kindly click on the provided link](https://www.pingcap.com/demo/?utm_source=edu&utm_medium=referral&utm_campaign=201.3.E10) to submit your application.

## End of Exercises