diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..31b124f --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +TIDB_HOST='xxxxxxxx.aws.tidbcloud.com' +TIDB_PORT='4000' +TIDB_USER='xxxxxxxxxxx.root' +TIDB_PASSWORD='xxxxxxx' +TIDB_DB_NAME='test' +CA_PATH='/etc/ssl/cert.pem' \ No newline at end of file diff --git a/Makefile b/Makefile index cf22ec1..a5d82eb 100644 --- a/Makefile +++ b/Makefile @@ -26,28 +26,20 @@ test: dep: pip3 install -r requirement.txt -recreate-table: - mycli --host 127.0.0.1 --port 4000 -u root --no-warn < player_init.sql - # ORMs peewee-test: - make recreate-table python3 peewee_example.py sqlalchemy-test: - make recreate-table python3 sqlalchemy_example.py # Drivers mysqlclient-test: - make recreate-table python3 mysqlclient_example.py pymysql-test: - make recreate-table python3 pymysql_example.py mysql-connector-python-test: - make recreate-table python3 mysql_connector_python_example.py diff --git a/README-zh.md b/README-zh.md index 98a3a56..afca84b 100644 --- a/README-zh.md +++ b/README-zh.md @@ -4,24 +4,24 @@ [English](/README.md) | 中文 -**tidb-example-python** 是 PingCAP 为 Python 连接 [TiDB](https://docs.pingcap.com/tidb/stable) 而编写的示例项目。你可以在这里找到以下示例: +**tidb-example-python** 是 PingCAP 为 Python 连接 [TiDB](https://docs.pingcap.com/tidb/stable) 而编写的示例项目。其所属文档为 [TiDB 开发者文档](https://docs.pingcap.com/zh/tidb/stable/dev-guide-overview)。你可以在这里找到以下示例: -**Driver** +## Driver - [PyMySQL](/pymysql_example.py) 示例 - [mysqlclient](/mysqlclient_example.py) 示例 - [mysql-connector-python](/mysql_connector_python_example.py) 示例 -**ORM** +## ORM - [SQLAlchemy](/sqlalchemy_example.py) 示例 - [peewee](/peewee_example.py) 示例 -**Framework** +## Framework - [Django](/django_example) 示例 -**Scenario** +## Scenario - [Serverless Tier 连接](/serverless_tier_example.py)示例 - [批量删除场景](/batch_delete.py)示例 @@ -29,11 +29,38 @@ - [避免写偏斜](/write_skew_example.py)示例 - [乐观/悲观事务](/txn_example.py)示例 -## 依赖 +## 前提要求 -- [TiDB](https://docs.pingcap.com/tidb/stable) -- [mycli](https://www.mycli.net/) -- [Python 3.6+](https://www.python.org/) +- 已安装 [Python](https://www.python.org/),推荐版本 3.10 及以上。 +- 启动你的 TiDB 集群,如果你还没有 TiDB 集群: + + - 推荐参考[创建 TiDB Serverless 集群](https://docs.pingcap.com/zh/tidb/stable/dev-guide-build-cluster-in-cloud)文档创建你自己的 TiDB Cloud 集群。 + - 备选参考[部署本地测试 TiDB 集群](https://docs.pingcap.com/zh/tidb/stable/quick-start-with-tidb)或[部署正式 TiDB 集群](https://docs.pingcap.com/zh/tidb/stable/production-deployment-using-tiup)文档创建本地集群。 + +- 已获取代码并安装依赖 + + ```bash + git clone https://github.com/pingcap-inc/tidb-example-python.git + cd tidb-example-python + pip install requirement.txt + ``` + +- 已正确配置代码目录下的 `.env` 配置文件,你可以用 `cp .env.example .env` 复制初始配置文件后进行更改。如果你不知道这些信息如何获取,请参考 [Obtain the connection parameters](https://docs.pingcap.com/tidbcloud/connect-via-standard-connection-serverless#obtain-the-connection-parameters) 及 [Where is the CA root path on my system?](https://docs.pingcap.com/tidbcloud/secure-connections-to-serverless-tier-clusters#where-is-the-ca-root-path-on-my-system) 文档: + + ```properties + # TiDB 集群地址 + TIDB_HOST='xxxxxxxx.aws.tidbcloud.com' + # TiDB 集群端口 + TIDB_PORT='4000' + # TiDB 集群用户名 + TIDB_USER='xxxxxxxxxxx.root' + # TiDB 集群密码 + TIDB_PASSWORD='xxxxxxx' + # 希望本示例使用的数据库名称 + TIDB_DB_NAME='test' + # 使用的 TiDB 集群如果要求使用安全连接,并使用自定义的 CA 证书时 + CA_PATH='/etc/ssl/cert.pem' + ``` ## 运行 @@ -45,22 +72,8 @@ make test ### 单独运行某项测试 -以 `peewee_example.py` 为例: - -1. 安装依赖: +以 **peewee_example.py** 为例:`python3 peewee_example.py`。 - ```bash - pip install -r requirement.txt - ``` - -2. 初始化表 - - ```bash - mycli --host 127.0.0.1 --port 4000 -u root --no-warn < player_init.sql - ``` +### 预期输出 -3. 运行脚本 - - ```bash - python3 peewee_example.py - ``` \ No newline at end of file +[预期输出](/Expected-Output.md) diff --git a/README.md b/README.md index 7ad6d91..465ff9a 100644 --- a/README.md +++ b/README.md @@ -4,24 +4,24 @@ English | [中文](/README-zh.md) -**tidb-example-python** is a sample project written by PingCAP for Python to connect to [TiDB](https://docs.pingcap.com/tidb/stable). You can find the following examples here. +**tidb-example-python** is a sample project written by PingCAP for Python to connect to [TiDB](https://docs.pingcap.com/tidb/stable). This document belongs to the [TiDB developer documentation](https://docs.pingcap.com/tidb/stable/dev-guide-overview). You can find the following examples here. -**Driver** +## Driver - [PyMySQL](/pymysql_example.py) example - [mysqlclient](/mysqlclient_example.py) example - [mysql-connector-python](/mysql_connector_python_example.py) example -**ORM** +## ORM - [SQLAlchemy](/sqlalchemy_example.py) example - [peewee](/peewee_example.py) example -**Framework** +## Framework - [Django](/django_example) example -**Scenario** +## Scenario - [Serverless Tier connection](/serverless_tier_example.py) example - [Butch delete](/batch_delete.py) example @@ -29,38 +29,51 @@ English | [中文](/README-zh.md) - [Avoid write skew](/write_skew_example.py) example - [Optimistic and pessimistic transaction](/txn_example.py) example -## Dependcies +## Prerequisites -- [TiDB](https://docs.pingcap.com/tidb/stable) -- [mycli](https://www.mycli.net/) -- [Python 3.6+](https://www.python.org/) +- Python installed (recommended version 3.10 and above). +- Start your TiDB cluster. If you don't have a TiDB cluster yet: -## Run + - We recommend referring to the document [Create a TiDB Serverless Cluster](https://docs.pingcap.com/tidb/stable/dev-guide-build-cluster-in-cloud) to create your own TiDB Cloud cluster. + - Alternatively, you can refer to the documents [Deploy a Local Testing TiDB Cluster](https://docs.pingcap.com/tidb/stable/quick-start-with-tidb) or [Deploy a Formal TiDB Cluster](https://docs.pingcap.com/tidb/stable/production-deployment-using-tiup) to create a local cluster. -### Run the Whole ORM and Driver Test +- Get the code and install the dependencies -```bash -make test -``` + ```bash + git clone https://github.com/pingcap-inc/tidb-example-python.git + cd tidb-example-python + pip install -r requirement.txt + ``` -### Run the particular test +- Configure the `.env` file in the code directory correctly. You can use `cp .env.example .env` to copy the initial configuration file and make changes accordingly. If you are unsure how to obtain this information, please refer to the documents [Obtain the connection parameters](https://docs.pingcap.com/tidbcloud/connect-via-standard-connection-serverless#obtain-the-connection-parameters) and [Where is the CA root path on my system?](https://docs.pingcap.com/tidbcloud/secure-connections-to-serverless-tier-clusters#where-is-the-ca-root-path-on-my-system): -Using run the `peewee_example.py` script as an example: + ```properties + # TiDB cluster address + TIDB_HOST='xxxxxxxx.aws.tidbcloud.com' + # TiDB cluster port + TIDB_PORT='4000' + # TiDB cluster username + TIDB_USER='xxxxxxxxxxx.root' + # TiDB cluster password + TIDB_PASSWORD='xxxxxxx' + # The database name to be used for this example + TIDB_DB_NAME='test' + # The location of the CA certificate on the instance where the example is run. Required when the used cluster require secure connection + CA_PATH='/etc/ssl/cert.pem' + ``` -1. Install dependcy. +## Run - ```bash - pip install -r requirement.txt - ``` +### Run the Entire ORM / Driver Test + +```bash +make test +``` -2. Initial the table. +### Run a specific test individually - ```bash - mycli --host 127.0.0.1 --port 4000 -u root --no-warn < player_init.sql - ``` +Taking **peewee_example.py** as an example: `python3 peewee_example.py`. -3. Run the script. +### Expected output - ```bash - python3 peewee_example.py - ``` \ No newline at end of file +[Expected output](/Expected-Output.md) diff --git a/batch_delete.py b/batch_delete.py index 7e6db01..d06266f 100644 --- a/batch_delete.py +++ b/batch_delete.py @@ -12,20 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import MySQLdb import datetime import time +from connect_tidb import get_mysqlclient_connection -connection = MySQLdb.connect( - host="127.0.0.1", - port=4000, - user="root", - password="", - database="bookshop", - autocommit=True -) - -with connection: +with get_mysqlclient_connection() as connection: with connection.cursor() as cursor: start_time = datetime.datetime(2022, 4, 15) end_time = datetime.datetime(2022, 4, 15, 0, 15) diff --git a/batch_update.py b/batch_update.py index adf4e18..65c19e9 100644 --- a/batch_update.py +++ b/batch_update.py @@ -11,18 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import MySQLdb import time - -connection = MySQLdb.connect( - host="127.0.0.1", - port=4000, - user="root", - password="", - database="bookshop", - autocommit=True -) - +from connect_tidb import get_mysqlclient_connection def update_batch(cur, last_book_id: int = None, last_user_id: int = None) -> (int, int): if last_book_id is None or last_user_id is None: @@ -49,7 +39,7 @@ def update_batch(cur, last_book_id: int = None, last_user_id: int = None) -> (in return latest_book_id, latest_user_id -with connection: +with get_mysqlclient_connection() as connection: with connection.cursor() as cursor: # add a column `ten_point` # cursor.execute("ALTER TABLE `bookshop`.`ratings` ADD COLUMN `ten_point` BOOL NOT NULL DEFAULT FALSE") diff --git a/connect_tidb.py b/connect_tidb.py new file mode 100644 index 0000000..1a8194f --- /dev/null +++ b/connect_tidb.py @@ -0,0 +1,127 @@ +# Copyright 2022 PingCAP, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +from dotenv import load_dotenv + +import MySQLdb +import mysql.connector +import playhouse.db_url +import peewee +import pymysql +from pymysql.cursors import DictCursor as PyMySQLDictCursor +import sqlalchemy + +class Config: + def __init__(self): + load_dotenv() + self.tidb_host = os.getenv("TIDB_HOST", "127.0.0.1") + self.tidb_port = int(os.getenv("TIDB_PORT", "4000")) + self.tidb_user = os.getenv("TIDB_USER", "root") + self.tidb_password = os.getenv("TIDB_PASSWORD", "") + self.tidb_db_name = os.getenv("TIDB_DB_NAME", "test") + self.ca_path = os.getenv("CA_PATH", "") + +def get_mysqlclient_connection(autocommit:bool=True) -> MySQLdb.Connection: + config = Config() + db_conf = { + "host": config.tidb_host, + "port": config.tidb_port, + "user": config.tidb_user, + "password": config.tidb_password, + "database": config.tidb_db_name, + "autocommit": autocommit + } + + if config.ca_path: + db_conf["ssl_mode"] = "VERIFY_IDENTITY" + db_conf["ssl"] = {"ca": config.ca_path} + + return MySQLdb.connect(**db_conf) + + +def get_mysql_connector_python_connection(autocommit:bool=True) -> mysql.connector.MySQLConnection: + config = Config() + db_conf = { + "host": config.tidb_host, + "port": config.tidb_port, + "user": config.tidb_user, + "password": config.tidb_password, + "database": config.tidb_db_name, + "autocommit": autocommit + } + + if config.ca_path: + db_conf["ssl_verify_identity"] = True + db_conf["ssl_ca"] = config.ca_path + + return mysql.connector.connect(**db_conf) + + +def get_pymysql_connection(autocommit: bool = False) -> pymysql.Connection: + config = Config() + db_conf = { + "host": config.tidb_host, + "port": config.tidb_port, + "user": config.tidb_user, + "password": config.tidb_password, + "database": config.tidb_db_name, + "autocommit": autocommit, + "cursorclass": PyMySQLDictCursor, + } + + if config.ca_path: + db_conf["ssl_verify_cert"] = True + db_conf["ssl_verify_identity"] = True + db_conf["ssl_ca"] = config.ca_path + + return pymysql.connect(**db_conf) + + +def get_peewee_db() -> peewee.MySQLDatabase: + config = Config() + url = f'mysql://{config.tidb_user}:{config.tidb_password}@{config.tidb_host}:{config.tidb_port}/{config.tidb_db_name}' + if config.ca_path: + return playhouse.db_url.connect(url, ssl_verify_cert=True, ssl_ca=config.ca_path) + else: + return playhouse.db_url.connect(url) + + +def get_sqlalchemy_engine(): + config = Config() + url = f'mysql://{config.tidb_user}:{config.tidb_password}@{config.tidb_host}:{config.tidb_port}/{config.tidb_db_name}' + if config.ca_path: + return sqlalchemy.create_engine(url, connect_args={ + "ssl_mode": "VERIFY_IDENTITY", + "ssl": { "ca": config.ca_path } + }) + else: + return sqlalchemy.create_engine(url) + + +def __recreate_table(connection) -> None: + with connection.cursor() as cur: + cur.execute("DROP TABLE IF EXISTS player;") + cur.execute("CREATE TABLE player (`id` VARCHAR(36), `coins` INTEGER, `goods` INTEGER, PRIMARY KEY (`id`));") + +def mysqlclient_recreate_table() -> None: + __recreate_table(get_mysqlclient_connection(autocommit=True)) + + +def mysql_connector_python_recreate_table() -> None: + __recreate_table(get_mysql_connector_python_connection(autocommit=True)) + + +def pymysql_recreate_table() -> None: + __recreate_table(get_pymysql_connection(autocommit=True)) + diff --git a/mysql_connector_python_example.py b/mysql_connector_python_example.py index a91458c..3f16e46 100644 --- a/mysql_connector_python_example.py +++ b/mysql_connector_python_example.py @@ -13,22 +13,14 @@ # limitations under the License. import uuid +from connect_tidb import get_mysql_connector_python_connection, mysql_connector_python_recreate_table + from typing import List -from mysql.connector import connect, MySQLConnection +from mysql.connector import MySQLConnection from mysql.connector.cursor import MySQLCursor -def get_connection(autocommit: bool = True) -> MySQLConnection: - connection = connect(host='127.0.0.1', - port=4000, - user='root', - password='', - database='test') - connection.autocommit = autocommit - return connection - - def create_player(cursor: MySQLCursor, player: tuple) -> None: cursor.execute("INSERT INTO player (id, coins, goods) VALUES (%s, %s, %s)", player) @@ -104,7 +96,7 @@ def trade(connection: MySQLConnection, sell_id: str, buy_id: str, amount: int, p def simple_example() -> None: - with get_connection(autocommit=True) as connection: + with get_mysql_connector_python_connection(autocommit=True) as connection: with connection.cursor() as cur: # create a player, who has a coin and a goods. create_player(cur, ("test", 1, 1)) @@ -133,7 +125,7 @@ def simple_example() -> None: def trade_example() -> None: - with get_connection(autocommit=False) as conn: + with get_mysql_connector_python_connection(autocommit=False) as conn: with conn.cursor() as cur: # create two players # player 1: id is "1", has only 100 coins. @@ -159,5 +151,6 @@ def trade_example() -> None: print(f'id:2, coins:{player2_coin}, goods:{player2_goods}') +mysql_connector_python_recreate_table() simple_example() trade_example() diff --git a/mysqlclient_example.py b/mysqlclient_example.py index 92905c8..ba41e73 100644 --- a/mysqlclient_example.py +++ b/mysqlclient_example.py @@ -15,26 +15,15 @@ import uuid from typing import List -import MySQLdb from MySQLdb import Connection from MySQLdb.cursors import Cursor +from connect_tidb import get_mysqlclient_connection, mysqlclient_recreate_table # If you don't have a TiDB cluster, just register here: # https://tidbcloud.com/console/clusters/create-cluster # And get a TiDB Cloud Serverless Tier in 1 min (no kidding, and it's free now) -def get_connection(autocommit: bool = True) -> MySQLdb.Connection: - return MySQLdb.connect( - host="127.0.0.1", - port=4000, - user="root", - password="", - database="test", - autocommit=autocommit - ) - - def create_player(cursor: Cursor, player: tuple) -> None: cursor.execute("INSERT INTO player (id, coins, goods) VALUES (%s, %s, %s)", player) @@ -110,7 +99,7 @@ def trade(connection: Connection, sell_id: str, buy_id: str, amount: int, price: def simple_example() -> None: - with get_connection(autocommit=True) as conn: + with get_mysqlclient_connection(autocommit=True) as conn: with conn.cursor() as cur: # create a player, who has a coin and a goods. create_player(cur, ("test", 1, 1)) @@ -137,7 +126,7 @@ def simple_example() -> None: def trade_example() -> None: - with get_connection(autocommit=False) as conn: + with get_mysqlclient_connection(autocommit=False) as conn: with conn.cursor() as cur: # create two players # player 1: id is "1", has only 100 coins. @@ -163,5 +152,6 @@ def trade_example() -> None: print(f'id:2, coins:{player2_coin}, goods:{player2_goods}') +mysqlclient_recreate_table() simple_example() trade_example() diff --git a/peewee_example.py b/peewee_example.py index 9eaf5c0..58098a5 100644 --- a/peewee_example.py +++ b/peewee_example.py @@ -11,16 +11,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import os import uuid from typing import List from peewee import * -from playhouse.db_url import connect - -db = connect('mysql://root:@127.0.0.1:4000/test') +from connect_tidb import get_peewee_db +db = get_peewee_db() class Player(Model): id = CharField(max_length=36, primary_key=True) diff --git a/player_init.sql b/player_init.sql deleted file mode 100644 index 5b8eead..0000000 --- a/player_init.sql +++ /dev/null @@ -1,9 +0,0 @@ -USE test; -DROP TABLE IF EXISTS player; - -CREATE TABLE player ( - `id` VARCHAR(36), - `coins` INTEGER, - `goods` INTEGER, - PRIMARY KEY (`id`) -); \ No newline at end of file diff --git a/pymysql_example.py b/pymysql_example.py index 210d9d0..d574691 100644 --- a/pymysql_example.py +++ b/pymysql_example.py @@ -14,20 +14,9 @@ import uuid from typing import List -import pymysql.cursors from pymysql import Connection from pymysql.cursors import DictCursor - - -def get_connection(autocommit: bool = False) -> Connection: - return pymysql.connect(host='127.0.0.1', - port=4000, - user='root', - password='', - database='test', - cursorclass=DictCursor, - autocommit=autocommit) - +from connect_tidb import get_pymysql_connection, pymysql_recreate_table def create_player(cursor: DictCursor, player: tuple) -> None: cursor.execute("INSERT INTO player (id, coins, goods) VALUES (%s, %s, %s)", player) @@ -104,7 +93,7 @@ def trade(connection: Connection, sell_id: str, buy_id: str, amount: int, price: def simple_example() -> None: - with get_connection(autocommit=True) as connection: + with get_pymysql_connection(autocommit=True) as connection: with connection.cursor() as cur: # create a player, who has a coin and a goods. create_player(cur, ("test", 1, 1)) @@ -131,7 +120,7 @@ def simple_example() -> None: def trade_example() -> None: - with get_connection(autocommit=False) as connection: + with get_pymysql_connection(autocommit=False) as connection: with connection.cursor() as cur: # create two players # player 1: id is "1", has only 100 coins. @@ -155,5 +144,6 @@ def trade_example() -> None: print(get_player(cur, "2")) +pymysql_recreate_table() simple_example() trade_example() \ No newline at end of file diff --git a/requirement.txt b/requirement.txt index b22a635..2219862 100644 --- a/requirement.txt +++ b/requirement.txt @@ -8,6 +8,7 @@ mysqlclient==2.1.1 peewee==3.15.4 protobuf==3.20.1 PyMySQL==1.0.2 +python-dotenv==1.0.0 pytz==2022.6 SQLAlchemy==1.4.44 sqlparse==0.4.3 diff --git a/sqlalchemy_example.py b/sqlalchemy_example.py index f9a9d03..a2e41fe 100644 --- a/sqlalchemy_example.py +++ b/sqlalchemy_example.py @@ -15,12 +15,12 @@ import uuid from typing import List -from sqlalchemy import create_engine, String, Column, Integer, select, func +from sqlalchemy import String, Column, Integer, select, func from sqlalchemy.orm import declarative_base, sessionmaker +from connect_tidb import get_sqlalchemy_engine -engine = create_engine('mysql://root:@127.0.0.1:4000/test') +engine = get_sqlalchemy_engine() Base = declarative_base() -Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) @@ -127,5 +127,7 @@ def trade_example() -> None: session.commit() +Base.metadata.drop_all(engine) +Base.metadata.create_all(engine) simple_example() trade_example() diff --git a/txn_example.py b/txn_example.py index 736ef5c..5681a68 100644 --- a/txn_example.py +++ b/txn_example.py @@ -17,6 +17,7 @@ import os import datetime from threading import Thread +from connect_tidb import get_mysqlclient_connection REPEATABLE_ERROR_CODE_SET = { 9007, # Transactions in TiKV encounter write conflicts. @@ -26,19 +27,8 @@ } -def create_connection(): - return MySQLdb.connect( - host="127.0.0.1", - port=4000, - user="root", - password="", - database="bookshop", - autocommit=False - ) - - def prepare_data() -> None: - connection = create_connection() + connection = get_mysqlclient_connection(autocommit=False) with connection: with connection.cursor() as cursor: cursor.execute("INSERT INTO `books` (`id`, `title`, `type`, `published_at`, `price`, `stock`) " @@ -53,7 +43,7 @@ def prepare_data() -> None: def buy_optimistic(thread_id: int, order_id: int, book_id: int, user_id: int, amount: int, optimistic_retry_times: int = 5) -> None: - connection = create_connection() + connection = get_mysqlclient_connection(autocommit=False) txn_log_header = f"/* txn {thread_id} */" if thread_id != 1: @@ -112,7 +102,7 @@ def buy_optimistic(thread_id: int, order_id: int, book_id: int, user_id: int, am def buy_pessimistic(thread_id: int, order_id: int, book_id: int, user_id: int, amount: int) -> None: - connection = create_connection() + connection = get_mysqlclient_connection(autocommit=False) txn_log_header = f"/* txn {thread_id} */" if thread_id != 1: diff --git a/write_skew_example.py b/write_skew_example.py index 489fc4c..71986ce 100644 --- a/write_skew_example.py +++ b/write_skew_example.py @@ -13,22 +13,11 @@ # limitations under the License. from threading import Thread, Semaphore -import MySQLdb - - -def create_connection(autocommit=True): - return MySQLdb.connect( - host="127.0.0.1", - port=4000, - user="root", - password="", - database="test", - autocommit=autocommit - ) +from connect_tidb import get_mysqlclient_connection def prepare_data() -> None: - connection = create_connection(autocommit=True) + connection = get_mysqlclient_connection(autocommit=True) with connection: with connection.cursor() as cursor: # create table @@ -48,7 +37,7 @@ def prepare_data() -> None: def ask_for_leave(thread_id: int, txn1_run: Semaphore, doctor_id: int) -> None: - connection = create_connection(False) + connection = get_mysqlclient_connection(autocommit=False) txn_log_header = f"/* txn {thread_id} */" if thread_id != 1: txn_log_header = "\t" + txn_log_header