TigerBeetle Python 客户端入门:用 basic 示例跑通"建账户—转账—校验余额"全流程
【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle
导读
本文以 TigerBeetle 仓库中 basic 示例 为骨架,完整讲解如何使用 Python 客户端(tigerbeetlepip 包)连接 TigerBeetle 数据库:安装客户端、启动服务端、创建两个账户、发起一笔转账,最后回查账户余额并用断言逐项校验借方(debits)与贷方(credits)的变化。读完本文,你将掌握 Python 客户端的最小可用闭环,并理解账户/转账的数据结构、状态码枚举与批处理(batching)等底层约定,可以直接照抄代码跑通你的第一个 TigerBeetle 事务。
1. 示例概览:这个 basic 项目做了什么
示例代码位于 src/clients/python/samples/basic/main.py,整个流程只有三步,却覆盖了 TigerBeetle 最核心的三种操作:
- 创建账户:用
create_accounts批量创建两个账户,id 分别为1和2; - 创建转账:用
create_transfers将金额10从账户1(debit,借方)转到账户2(credit,贷方); - 回查并校验余额:用
lookup_accounts取回两个账户,断言账户1为debits_posted = 10、credits_posted = 0,账户2为debits_posted = 0、credits_posted = 10。
这本质上就是一个"最小双账户转账"用例,是理解 TigerBeetle 借贷记账模型(double-entry bookkeeping)最快的入口。仓库里还有两个进阶示例与之对照:two-phase(两阶段转账:先 PENDING 再 POST)和 two-phase-many(多条待定转账交替 post/void),可在跑通 basic 后继续阅读。
2. 环境与前置条件
根据 basic 示例 README 与 tigerbeetle-python 客户端文档,运行本示例需要满足:
- 操作系统:Linux >= 5.6 是唯一的生产环境支持平台;为便于开发,macOS 与 Windows 也受支持。
- Python:>=
3.7(PyPy 等实现亦可)。
Python 包内部通过 ctypes 加载随包分发的原生共享库(tb_client)。从 src/clients/python/src/tigerbeetle/lib.py 的加载逻辑可以看到,它按平台自动选择动态库文件:
- 架构仅支持
x86_64/amd64与aarch64/arm64; - Linux 下区分 glibc(
-gnu.2.27后缀)与 musl(-musl后缀),其他 libc 会抛出NativeError: Unsupported libc; - 系统仅支持 Linux、Darwin、Windows,其他平台会抛出
NativeError: Unsupported system。
也就是说,只要你的机器满足上述架构与系统组合,pip install tigerbeetle后无需任何额外编译即可使用。
3. 安装 Python 客户端
进入示例目录后直接安装:
$ cd tigerbeetle/src/clients/python/samples/basic $ pip install tigerbeetle安装完成后,在 Python 中做一次冒烟测试即可确认环境正常:
import os import tigerbeetle as tb print("Import OK!") # 如需开启调试日志,可通过 Python 内置 logging 模块: # logging.basicConfig(level=logging.DEBUG) # tb.configure_logging(debug=True)tb.configure_logging(debug=True)来自 src/clients/python/src/tigerbeetle/client.py,它把 TigerBeetle 原生层的日志回调桥接到 Python 的logging体系,排查问题时非常有用。
4. 启动 TigerBeetle 服务端
示例 README 要求先按仓库主 README 的步骤启动 TigerBeetle。单副本集群的最小启动方式(见 README.md):
$ curl -Lo tigerbeetle.zip https://linux.tigerbeetle.com && unzip tigerbeetle.zip $ ./tigerbeetle version $ ./tigerbeetle format --cluster=0 --replica=0 --replica-count=1 --development 0_0.tigerbeetle $ ./tigerbeetle start --addresses=3000 --development 0_0.tigerbeetle关键点:
- cluster:示例使用
cluster_id=0,与format --cluster=0保持一致; - 地址:默认监听
localhost:3000。如果服务端不在该地址,设置环境变量TB_ADDRESS指向完整地址即可:$ export TB_ADDRESS="127.0.0.1:3000" - 地址的合法写法(来自客户端文档):
3000→ 解释为127.0.0.1:3000127.0.0.1:3000→ 保持不变127.0.0.1→ 解释为127.0.0.1:3001(3001是默认端口)
5. 运行示例
服务端就绪后,在示例目录下执行:
$ python3 main.py程序用assert做自我校验,全部通过后打印ok。整个 main.py 的运行逻辑如下节所示。
6. 逐行拆解 main.py
6.1 创建客户端
import os import tigerbeetle as tb with tb.ClientSync(cluster_id=0, replica_addresses=os.getenv("TB_ADDRESS", "3000")) as client: ...cluster_id必须与启动集群时指定的 cluster 一致;replica_addresses是所有副本的地址列表(本例单副本,取环境变量TB_ADDRESS,缺省"3000");ClientSync是同步客户端,用with语句进入上下文,退出时自动close()(见 src/clients/python/src/tigerbeetle/client.py 中__exit__→close()的实现)。
6.2 创建账户
account_results = client.create_accounts([ tb.Account( id=1, ledger=1, code=1, ), tb.Account( id=2, ledger=1, code=1, ), ])tb.Account是 Python 客户端定义的 dataclass(src/clients/python/src/tigerbeetle/bindings.py),字段与 wire 格式一一对应:
| 字段 | 位宽 | 说明 |
|---|---|---|
id | u128 | 账户唯一标识,示例用1/2;生产环境建议用tb.id()生成基于 ULID 的 128 位可排序 ID |
debits_pending/debits_posted | u128 | 借方待定/已过账金额,新建账户必须为 0(服务端会校验) |
credits_pending/credits_posted | u128 | 贷方待定/已过账金额,同上 |
user_data_128/user_data_64/user_data_32 | u128/u64/u32 | 应用自定义数据,可用于关联业务 ID、做过滤查询 |
ledger | u32 | 账本号,转账双方必须在同一 ledger,且与账户 ledger 一致 |
code | u16 | 业务编码(如账户类型、币种),用于查询过滤 |
flags | u16 | 位标志,见下文"Flags"小节 |
timestamp | u64 | 必须为 0,由服务端分配(见TIMESTAMP_MUST_BE_ZERO错误码) |
示例中所有金额/用户数据字段都取默认值 0,仅显式设置id、ledger、code,这是最简单合法的账户。
6.3 校验建账结果
print(account_results) assert len(account_results) == 2 assert account_results[0].status == tb.CreateAccountStatus.CREATED assert account_results[1].status == tb.CreateAccountStatus.CREATEDcreate_accounts的返回结果与请求一一对应,每个结果包含:
status:CreateAccountStatus枚举。成功为CREATED(数值0xFFFFFFFF,见 bindings.py);timestamp:本次操作由服务端分配的时间戳。
值得注意的是,重复提交相同id的账户不会报"失败",而是返回EXISTS(携带原账户的时间戳),这正是 TigerBeetle 幂等语义的基础——重试是安全的。
6.4 创建转账
transfers_results = client.create_transfers([ tb.Transfer( id=1, debit_account_id=1, credit_account_id=2, amount=10, ledger=1, code=1, ), ])tb.Transfer同样是与 wire 格式对应的 dataclass:
| 字段 | 位宽 | 说明 |
|---|---|---|
id | u128 | 转账唯一标识;TigerBeetle 用它做幂等去重,同 id 的转账只能提交一次 |
debit_account_id | u128 | 借方账户(资金流出方) |
credit_account_id | u128 | 贷方账户(资金流入方),必须与借方不同 |
amount | u128 | 转账金额,0 < amount(普通转账金额不能为 0) |
pending_id | u128 | 两阶段转账时指向 PENDING 转账的 id,普通转账为 0 |
timeout | u32 | 仅对 PENDING 转账有意义(到期自动过期),单位秒(纳秒见源码语义),普通转账为 0 |
ledger/code | u32/u16 | 同账户;转账 ledger 必须等于双方账户 ledger |
flags | u16 | 位标志,普通转账为 0 |
timestamp | u64 | 必须为 0,由服务端分配 |
6.5 校验转账结果
print(transfers_results) assert len(transfers_results) == 1 assert transfers_results[0].status == tb.CreateTransferStatus.CREATEDCreateTransferStatus.CREATED同样是0xFFFFFFFF。与账户类似,若同 id 转账已存在,会返回EXISTS系列状态码。完整错误码枚举(如DEBIT_ACCOUNT_NOT_FOUND、CREDIT_ACCOUNT_NOT_FOUND、ACCOUNTS_MUST_HAVE_THE_SAME_LEDGER、EXCEEDS_CREDITS等)都在 bindings.py 中定义,可用于精细化错误处理。
6.6 回查账户并校验余额
accounts = client.lookup_accounts([1, 2]) assert len(accounts) == 2 for account in accounts: if account.id == 1: assert account.debits_posted == 10 assert account.credits_posted == 0 elif account.id == 2: assert account.debits_posted == 0 assert account.credits_posted == 10 else: raise Exception("Unexpected account: " + account) print("ok")这里体现了 TigerBeetle 记账模型的核心语义:
- debit 是"钱从哪来":账户 1 是借方,
debits_posted从 0 变为 10; - credit 是"钱到哪去":账户 2 是贷方,
credits_posted从 0 变为 10; - 借贷平衡:
debits_posted(账户 1)=credits_posted(账户 2)= 10,系统内部保证总借方恒等于总贷方。
lookup_accounts也是批处理接口,返回的账户顺序不一定与请求 id 顺序一致,因此代码用account.id做区分而不是依赖索引——这一点在客户端 README 的 Account Lookup 一节中有明确说明。
7. 关键 API 与常量速查
示例之外,同一个 Python 客户端还提供以下操作(均来自 bindings.py 的StateMachineMixin/AsyncStateMachineMixin,与 client.py 的同步/异步封装):
| 方法 | 说明 |
|---|---|
create_accounts/create_transfers | 批量创建账户/转账 |
lookup_accounts/lookup_transfers | 按 id 批量查询(无匹配则不返回,顺序不保证) |
get_account_transfers/get_account_balances | 按账户过滤查询流水/时点余额(预览 API,要求账户带HISTORY标志) |
query_accounts/query_transfers | 按字段交集 + 时间范围查询(预览 API) |
常用常量:
tb.id():生成基于 ULID 的 128 位全局唯一、按时间可排序 ID(实现见 client.py 的_IDGenerator);tb.AMOUNT_MAX:2**128 - 1,两阶段转账 post 时表示"post 掉整个 pending 金额";tb.AccountFlags/tb.TransferFlags:enum.IntFlag位标志,可用|组合,例如:AccountFlags.LINKED/TransferFlags.LINKED:链接事件(链上全部成功或全部回滚);AccountFlags.DEBITS_MUST_NOT_EXCEED_CREDITS/CREDITS_MUST_NOT_EXCEED_DEBITS:余额约束;AccountFlags.HISTORY:开启历史余额保留;TransferFlags.PENDING/POST_PENDING_TRANSFER/VOID_PENDING_TRANSFER:两阶段转账三要素。
8. 从 basic 到生产:必须了解的批处理与幂等
basic 示例只提交了 2 个账户和 1 笔转账,但 TigerBeetle 的性能模型高度依赖批处理(客户端文档 "Batching" 一节):
- 客户端实例是线程安全的,多个并发请求可被客户端自动合并批处理;
- 应用侧仍应尽量在单次调用中提交尽可能多的事件。例如插入 100 万笔转账,如果逐笔串行提交,插入速率将只是潜在能力的零头;应始终"尽可能多地批量提交";
- 单批最大条数由服务端配置决定,默认值为
8189(与测试文件 src/clients/python/tests/test_basic.py 中BATCH_MAX = 8189一致)。超过上限会抛出TooMuchDataError:
batch = [] # 待创建的转账列表 BATCH_SIZE = 8189 for i in range(0, len(batch), BATCH_SIZE): transfers_results = client.create_transfers( batch[i:min(len(batch), i + BATCH_SIZE)], ) # 结果处理略另一个生产级要点是幂等:TigerBeetle 以id作为去重键,重复提交同 id 的账户/转账会返回EXISTS(或EXISTS_WITH_DIFFERENT_*系列),而不会重复入账。因此应用可以用业务幂等键作为 id,配合 可靠事务提交 的指引实现"重试安全"的提交。此外,客户端"无限重试、不设单请求超时",关闭客户端时所有在途请求被取消并向调用方返回错误——即使收到错误,请求仍可能已被服务端处理,这正是需要用 id 幂等兜底的原因。
9. 进阶:两阶段转账(Pending → Post/Void)
basic 示例是"即时过账"的单阶段转账。如果业务需要"预授权—确认/取消"(如押金、扣款审批),TigerBeetle 原生支持两阶段转账,可参考 two-phase 示例:
- 发起待定转账(
flags=tb.TransferFlags.PENDING):金额进入双方的debits_pending/credits_pending,不计入已过账余额; - 确认(post)(
flags=tb.TransferFlags.POST_PENDING_TRANSFER,pending_id指向待定转账):系统原子地把pending回滚、把金额应用到debits_posted/credits_posted; - 取消(void)(
flags=tb.TransferFlags.VOID_PENDING_TRANSFER):只回滚pending,不应用到posted。
post 时amount可设为tb.AMOUNT_MAX表示"全部确认"。待定转账还可设置timeout,到期后自动过期(测试 test_basic.py 的test_cannot_void_an_expired_transfer展示了过期语义)。更复杂的交替 post/void 场景可参考 two-phase-many 示例。
10. 常见错误与排查
InitError:ClientSync初始化失败。多为 cluster id 不匹配、地址不可达或原生库未加载(检查平台/架构是否受支持,见 lib.py)。IntegerOverflowError:字段超出位宽。ctypes 绑定在提交前会对每个整数字段做范围校验(如 u128、u64、u32、u16),负数或过大值都会抛出该异常(对应测试 test_basic.py 的test_range_check_*系列)。TooMuchDataError:单批请求超过服务端client_request_batch_max(默认 8189)。ClientClosedError/ClientEvictedError/ClientReleaseTooLowError/ClientReleaseTooHighError:客户端关闭后被使用、被服务端淘汰、或客户端库版本与服务端不兼容(版本过旧/过新)。这些异常类型定义在 client.py。TIMESTAMP_MUST_BE_ZERO:手动给Account.timestamp/Transfer.timestamp传了非 0 值,时间戳一律由服务端分配。
11. 小结
通过 basic 示例 与 main.py,你已经跑通了 TigerBeetle Python 客户端的最小闭环:ClientSync连接 →create_accounts建账 →create_transfers转账 →lookup_accounts校验借贷余额。在此基础上,结合 tigerbeetle-python 客户端文档 中关于 Flags、两阶段转账、批处理、链式事件与导入事件的章节,即可将这套最小示例扩展为具备余额约束、预授权、历史查询的生产级记账系统。
相关资源
- basic 示例 README(本文主体来源)
- basic 示例代码 main.py
- tigerbeetle-python 客户端完整文档
- Python 客户端核心实现 client.py
- Python 客户端数据类型与状态码 bindings.py
- 原生库加载与整型校验 lib.py
- Python 客户端集成测试 test_basic.py
- 两阶段转账示例
- 仓库主 README 的服务端启动步骤
【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考