# Distributed Transactions - Cont'd

![`smart`](PercolatorTiKV03_aa_002_20220511.png)

------------------------------------------------------------
Here goes a transaction:
```
Begin
< 1,Tom > -> < 1,Jack >
< 2,Andy > -> < 2,Candy > 
Commit;
```

Assuming that key = 1, key = 2 are in different Regions on different TiKV nodes.

First, at the beginning of the transaction, we obtained a start_ts from PD, assuming it is 100. Then we entered the first stage of Percolator, PreWrite. At this time, since the transaction has not been committed, we only need to write to Lock CF and Default CF. As you can see from the figure, when writing to Default CF, we added a timestamp (start_ts) to the key. The two values in the Lock CF are different, mainly including lock_type, user_key, and start_ts.

As shown below (W indicates write, D indicates delete):
```
+****************************************************************************+
|                                              Key = 1                       |
+***********************+*********************************+******************+
|   Default CF      |            Lock CF             ｜      Write CF         |
+***********************+*******+**************************+*******+*********+
|   key  |  value   |  key |       value             |   key   |   value     |
+***********************+*******+********************************************+
| 1_100  |   Jack   |   1  |  W, primary key,100...  |         |             |
+***********************+*******+**************************+*****************+

+****************************************************************************+
|                                              Key = 2                       |
+***********************+*********************************+******************+
|        Default CF          |               Lock CF        ｜   Write CF     |
+***********************+*******+**************************+*******+*********+
|   key        |  value      |    key  |         value      |   key |  value |
+***********************+*******+********************************************+
|   2_100      |   Candy     |     2   |  W, @key = 1,00... |       |        |
+***********************+*******+**************************+*****************+
```

One important point to note is that key = 1 and key = 2 are modified in a transaction. In Lock CF, only the first modified key, key = 1, is used as the primary key, and the lock for key = 2 is directed to key = 1 (i.e., Lock CF in value). This means that the lock for key = 2 is attached to the lock for key = 1.

When modifying or deleting data in Default CF, a new key-value pair is inserted instead of modifying or deleting existing data. This is because Default CF uses MVCC (Multi-Version Concurrency Control) technology, which allows multiple transactions to access the same data concurrently without interfering with each other. When a transaction modifies or deletes data in Default CF, it creates a new version of the data with a newer timestamp, and the old version is not modified or deleted. This ensures that other transactions that have already accessed the old version of the data can continue to use it without being affected by the modifications or deletions made by the current transaction. The details of this will be explained further when we introduce MVCC technology.

Next, we will enter the second stage of Percolator, Commit. At this time, we will obtain a commit_ts from PD, let's assume it is 110. We will then write this commit_ts to Write CF, and add a timestamp (commit_ts) to the value of the transaction's start_ts. We will also mark the primary key record in Lock CF with D to indicate that the transaction has committed and the locks are no longer needed. This completes the Commit stage of the transaction.

As shown below (W indicates write, D indicates delete):
```
+************************************************************************************+
|                                              Key = 1                               |
+***********************+*********************************+**************************+
|        Default CF         |               Lock CF              ｜     Write CF     |
+***********************+*******+**************************+*******+*****************+
|   key       |  value      |    key  |         value            |   key   |   value |
+***********************+*******+****************************************************+
| 1_100       |   Jack      |     1   |  W, primary key,100...   |         |         |
+***********************+*******+**************************+*************************+
|             |             |     1   |  D,  primary key,100...  |         |         |
+***********************+*******+**************************+*************************+

+**************************************************************************************+
|                                              Key = 2                                 |
+***********************+*********************************+****************************+
|        Default CF         |               Lock CF          ｜           Write CF      |
+***********************+*******+**************************+*******+*******************+
|   key        |  value     |    key  |        value         |   key   |   value       |
+***********************+*******+******************************************************+
| 2_100        |   Candy    |     2   |  W, @key = 1,00...   |         |               |
+***********************+*******+**************************+***************************+
|              |            |     2   |  D,  @key = 1,00...  |  2_110  | start_ts=100  |
+***********************+*******+**************************+***************************+
```

After the Commit stage is completed, when we want to read the data, we will first check if there are any locks on the data in Lock CF. If there are no locks, or if the locks have been removed (i.e., the flag is D), the data can be read. Otherwise, the read will fail because the data is still locked by a transaction that has not yet completed. This ensures that concurrent transactions are isolated from each other and that data is not read by one transaction until it has been committed by another transaction.

We discovered that during the commit phase of Percolator, we only need to delete the key = 1 primary key. This is because the key = 2 Lock CF contains a pointer to the key = 1 primary key. In the event of an error that causes only key = 1 to be deleted, we can still determine whether the transaction was successfully submitted by checking the key = 2 Lock CF. This solution resolves the distributed transaction problem mentioned earlier.

We've only provided a brief introduction to the Percolator model. The actual implementation will be more complex.
