ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

TigerBeetle Go 客户端实战指南:账户、转账、两阶段转账与批量操作

TigerBeetle Go 客户端实战指南:账户、转账、两阶段转账与批量操作 TigerBeetle Go 客户端实战指南账户、转账、两阶段转账与批量操作【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle本指南以 Go 客户端官方文档 为主体系统讲解在 Go 项目中接入 TigerBeetle 金融级事务数据库的完整流程从环境准备、客户端初始化到账户/转账的创建与查询、两阶段转账、链式linked事件、历史数据导入imported events以及性能关键的大型批量写入。阅读完本文你将能够基于github.com/tigerbeetle/tigerbeetle-go编写一套可投入生产的记账核心代码。一、环境准备与工程初始化TigerBeetle Go 客户端tigerbeetle-go通过 cgo 绑定到tb_client原生库实现见 tb_client.go因此对运行环境有明确要求生产环境仅支持 Linux 5.6为便于开发同时支持 macOS 与 WindowsGo 1.21注意仓库内的 go.mod 声明go 1.17这是模块最低版本约束官方文档推荐使用 1.21 及以上的工具链Windows 额外要求安装 Zig 0.14.1并将CC环境变量设置为zig.exe cc的完整路径原生库依赖 Zig 提供的交叉编译工具链。初始化一个全新的 Go 工程go mod init tbtest go get github.com/tigerbeetle/tigerbeetle-go注意务必从github.com/tigerbeetle/tigerbeetle-go导入包而不是从仓库子目录本地导入。包名导入方式为点导入例如import . github.com/tigerbeetle/tigerbeetle-go这是官方文档示例和 samples/basic/main.go 中采用的方式。创建main.go并写入验证代码package main import ( fmt . github.com/tigerbeetle/tigerbeetle-go ) func main() { fmt.Println(Import ok!) }构建并运行go run main.go看到Import ok!即表示 cgo 链接与原生库加载全部正常。仓库 CIci.zig中还会执行gofmt -l .、go vet、go test并针对basic、two-phase、two-phase-many、walkthrough四个示例启动临时 TigerBeetle 实例做端到端验证说明这套流程是经过完整测试的。二、官方示例项目一览Go 客户端仓库提供了四个可直接运行的示例均有go.mod与main.go示例路径演示内容Basicsamples/basic创建两个账户并在它们之间转移一笔金额Two-Phase Transfersamples/two-phase创建两个账户发起一笔 pending 转账后再 post 该转账Many Two-Phase Transferssamples/two-phase-many创建两个账户发起多笔 pending 转账交替 post/voidWalkthroughsamples/walkthrough完整走查教程其中 Basic 示例的运行流程是创建账户1、2Ledger1Code1向账户 2 转账10最后用LookupAccounts校验账户 1 的debits_posted 10、账户 2 的credits_posted 10可以作为最直接的上手参照。三、创建 Client集群 ID 与副本地址客户端通过「集群 ID 全部副本地址」与 TigerBeetle 集群建立连接。集群 ID 和副本地址由启动 TigerBeetle 集群的一方决定客户端创建时原样传入即可。tbAddress : os.Getenv(TB_ADDRESS) if len(tbAddress) 0 { tbAddress 3000 } client, err : NewClient(ToUint128(0), []string{tbAddress}) if err ! nil { log.Printf(Error creating client: %s, err) return } defer client.Close()上面示例中集群 ID 为0只有一个副本地址从TB_ADDRESS环境变量读取缺省端口为3000。地址的合法写法对应 tb_client.go 中NewClient用strings.Join以逗号拼接后交给tb_client_init的处理逻辑3000—— 解释为127.0.0.1:3000127.0.0.1:3000—— 解释为127.0.0.1:3000127.0.0.1—— 解释为127.0.0.1:30013001是缺省端口。多个副本地址以逗号分隔传入[]string即可例如NewClient(ToUint128(0), []string{3000, 3001, 3002})。线程安全与实例复用Client 是线程安全的多个并发任务应共享同一个实例。这样做的好处是事件可以在客户端侧被自动合并批处理batching显著提升吞吐。只有当需要连接多个 TigerBeetle 集群时才创建多个客户端实例。从源码看NewClient的初始化失败会映射为 errors.go 中定义的错误包括错误变量含义ErrUnexpected内部意外错误ErrOutOfMemory客户端内部内存不足ErrSystemResources客户端内部系统资源耗尽ErrNetworkSubsystem网络子系统异常ErrAddressLimitExceeded提供的地址过多ErrInvalidAddress集群地址非法ErrClientEvicted客户端被集群驱逐ErrClientReleaseTooLow/ErrClientReleaseTooHigh客户端版本过旧 / 过新而被驱逐ErrClientClosed客户端已关闭ErrInvalidOperation内部操作非法ErrTooMuchData单批次数据量过大四、Uint128128 位无符号整数的转换助手TigerBeetle 的ID、UserData128、Amount、账户余额等字段都是128 位小端无符号整数。Go 客户端将其封装为Uint128类型实现见 uint128.go并提供一组转换函数ToUint128(uint64)—— 由 64 位整数构造高位补零BytesToUint128([16]byte)—— 由原始小端字节构造HexStringToUint128(string)—— 由十六进制字符串解析长度不得超过 32 个 hex 字符自动补齐奇数位BigIntToUint128(*big.Int)—— 由math/big.Int转换负值会 panic反向value.Bytes()、value.String()十六进制去掉前导零、value.BigInt()、value.Uint64()返回低 64 位与高 64 位两个值。另外还有一个全局常量AmountMax定义于 tb_client.go即 128 位全0xff的最大金额常用于「post 整个 pending 金额」的场景本文后面会用到。五、创建账户CreateAccounts账户的核心字段定义见 bindings.go 的Account结构体完整字段及含义如下字段类型说明IDUint128账户唯一 ID推荐使用时间基 IDDebitsPending/DebitsPosted/CreditsPending/CreditsPostedUint128四类余额创建时通常为 0UserData128/UserData64/UserData32Uint128 / uint64 / uint32业务自定义数据可用于查询过滤Reserveduint32保留字段必须为 0Ledgeruint32账本 ID用于隔离不同业务账本Codeuint16业务代码如账户类型Flagsuint16标志位位域Timestampuint64由服务端分配创建时传 0创建两个账户accountResults, err : client.CreateAccounts([]Account{ { ID: ID(), // TigerBeetle time-based ID. UserData128: ToUint128(0), UserData64: 0, UserData32: 0, Ledger: 1, Code: 718, Flags: 0, Timestamp: 0, }, }) // Results handling omitted.其中ID()是客户端内置的TigerBeetle 时间基 ID 生成器uint128.go基于 ULID 规范由毫秒时间戳 80 位随机数构成在按小端解释时保证单调递增且可安全地被多个 goroutine 并发调用内部有互斥锁保证顺序一致性。关于推荐 ID 方案的更完整讨论可参考仓库中的>account0 : Account{ ID: ToUint128(100), Ledger: 1, Code: 718, Flags: AccountFlags{ DebitsMustNotExceedCredits: true, Linked: true, }.ToUint16(), } account1 : Account{ ID: ToUint128(101), Ledger: 1, Code: 718, Flags: AccountFlags{ History: true, }.ToUint16(), } accountResults, err : client.CreateAccounts([]Account{account0, account1}) // Results handling omitted.5.2 响应与错误处理CreateAccounts返回与请求一一对应的结果数组每个元素包含状态码Status与时间戳Timestamp成功创建的账户返回AccountCreated状态及服务端分配的Timestamp已存在的账户返回AccountExists及原对象的时间戳校验失败的账户返回对应错误状态码及校验发生时的时间戳。CreateAccountStatus的完整取值定义在 bindings.go常用的有AccountCreated、AccountExists、AccountLinkedEventFailed、AccountLinkedEventChainOpen、AccountIDMustNotBeZero、AccountLedgerMustNotBeZero、AccountCodeMustNotBeZero、AccountFlagsAreMutuallyExclusive以及AccountExistsWithDifferent*系列表示同 ID 账户字段不一致。推荐的批处理结果处理模式account0 : Account{ ID: ToUint128(102), Ledger: 1, Code: 718, Flags: 0, } account1 : Account{ ID: ToUint128(103), Ledger: 1, Code: 718, Flags: 0, } account2 : Account{ ID: ToUint128(104), Ledger: 1, Code: 718, Flags: 0, } accountResults, err : client.CreateAccounts([]Account{account0, account1, account2}) if err ! nil { log.Printf(Error creating accounts: %s, err) return } for i, result : range accountResults { switch result.Status { case AccountCreated: log.Printf(Batch account at %d successfully created with timestamp %d., i, result.Timestamp) case AccountExists: log.Printf(Batch account at %d already exists with timestamp %d., i, result.Timestamp) default: log.Printf(Batch account at %d failed to create: %s, i, result.Status) } }六、账户查询LookupAccounts账户查询同样支持批处理传入全部要查询的 ID返回匹配到的账户对象。accounts, err : client.LookupAccounts([]Uint128{ToUint128(100), ToUint128(101)})关键语义若某个 ID 没有匹配的账户响应中就不会有对应对象因此返回顺序不一定与请求顺序一致应通过每个账户的ID字段来区分。这与 get_account_balances 等参考文档 描述的行为一致。七、创建转账CreateTransfers转账在两个账户之间创建一笔记账分录journal entry。Transfer结构体完整字段见 bindings.go字段类型说明IDUint128转账唯一 ID幂等键DebitAccountID/CreditAccountIDUint128借记方 / 贷记方账户AmountUint128转账金额PendingIDUint128关联的 pending 转账 IDpost/void 时使用UserData128/UserData64/UserData32各类型业务自定义数据Timeoutuint32pending 转账的超时秒单位见源码注释仅 pending 转账可用Ledgeruint32账本 ID必须与两个账户一致Codeuint16业务代码如转账类型Flagsuint16标志位位域Timestampuint64服务端分配创建时传 0创建一笔普通转账transfers : []Transfer{{ ID: ID(), // TigerBeetle time-based ID. DebitAccountID: ToUint128(101), CreditAccountID: ToUint128(102), Amount: ToUint128(10), Ledger: 1, Code: 1, Flags: 0, Timestamp: 0, }} transferResults, err : client.CreateTransfers(transfers) // Results handling omitted.与创建账户相同CreateTransfers的响应同样包含状态码与时间戳成功返回TransferCreated与服务端时间戳已存在返回TransferExists与原时间戳失败返回对应错误码。CreateTransferStatus取值定义在 bindings.go数量较多可按类别理解基础校验类TransferIDMustNotBeZero、TransferLedgerMustNotBeZero、TransferCodeMustNotBeZero、TransferAccountsMustBeDifferent、TransferDebitAccountIDMustNotBeZero、TransferFlagsAreMutuallyExclusive等账户相关TransferDebitAccountNotFound、TransferCreditAccountNotFound、TransferAccountsMustHaveTheSameLedger、TransferTransferMustHaveTheSameLedgerAsAccounts、TransferDebitAccountAlreadyClosed、TransferCreditAccountAlreadyClosed余额约束类TransferOverflowsDebits、TransferOverflowsCredits、TransferExceedsDebits、TransferExceedsCredits等两阶段转账相关TransferPendingTransferNotFound、TransferPendingTransferAlreadyPosted、TransferPendingTransferAlreadyVoided、TransferPendingTransferExpired、TransferExceedsPendingTransferAmount等幂等冲突类TransferExistsWithDifferent*系列同 ID 但字段不同。结果处理示例transfers : []Transfer{{ ID: ToUint128(1), DebitAccountID: ToUint128(101), CreditAccountID: ToUint128(102), Amount: ToUint128(10), Ledger: 1, Code: 1, Flags: 0, }, { ID: ToUint128(2), DebitAccountID: ToUint128(101), CreditAccountID: ToUint128(102), Amount: ToUint128(10), Ledger: 1, Code: 1, Flags: 0, }, { ID: ToUint128(3), DebitAccountID: ToUint128(101), CreditAccountID: ToUint128(102), Amount: ToUint128(10), Ledger: 1, Code: 1, Flags: 0, }} transferResults, err : client.CreateTransfers(transfers) if err ! nil { log.Printf(Error creating transfers: %s, err) return } for i, result : range transferResults { switch result.Status { case TransferCreated: log.Printf(Batch transfer at %d successfully created with timestamp %d., i, result.Timestamp) case TransferExists: log.Printf(Batch transfer at %d already exists with timestamp %d., i, result.Timestamp) default: log.Printf(Batch transfer at %d failed to create: %s, i, result.Status) } }八、批量写入TigerBeetle 性能的关键TigerBeetle 的吞吐在批量 API 调用时达到最大共享客户端实例 并发多个 goroutine 共享一个客户端时客户端会自动把并发请求合并批处理单次调用尽量多传应用仍应尽量在一次调用中塞入尽可能多的事件。反例警示如果逐条顺序插入 100 万笔转账插入速率只会是理论上限的一个零头——因为客户端必须等每笔的应答才能发下一笔。所以结论是永远尽可能地批量提交。批量上限由 TigerBeetle 服务端配置决定默认最大批量为 8191 个事件README 中明确写 default 8191实际以你部署的 TigerBeetle 服务端配置为准。客户端在CreateTransfers/CreateAccounts等操作内部会校验批量大小超限时报ErrTooMuchData见 tb_client.go 的TB_PACKET_TOO_MUCH_DATA分支。按最大批量切分写入的推荐模式batch : []Transfer{} BATCH_SIZE : 8191 for i : 0; i len(batch); i BATCH_SIZE { size : BATCH_SIZE if iBATCH_SIZE len(batch) { size len(batch) - i } transferResults, err : client.CreateTransfers(batch[i : isize]) // Results handling omitted. _, _ transferResults, err }8.1 队列与 Worker 场景如果请求来自队列中拉任务的 worker可以通过「一次拉取多个任务、合并后批量提交」的方式实现批量例如从队列一次性取出 N 个任务而不是一次一个攒够一批再调用一次CreateTransfers。九、转账标志位TransferFlagsTransferFlags结构体及ToUint16()见 bindings.go可用的位标志位用途Linkedbit 0与批内下一事件链接Pendingbit 1发起两阶段转账先挂起PostPendingTransferbit 2post 一笔 pending 转账VoidPendingTransferbit 3void 一笔 pending 转账BalancingDebit/BalancingCreditbit 4/5差额补平转账ClosingDebit/ClosingCreditbit 6/7关户转账Importedbit 8导入历史转账例如链接transfer0与transfer1同生共死transfer0 : Transfer{ ID: ToUint128(4), DebitAccountID: ToUint128(101), CreditAccountID: ToUint128(102), Amount: ToUint128(10), Ledger: 1, Code: 1, Flags: TransferFlags{Linked: true}.ToUint16(), } transfer1 : Transfer{ ID: ToUint128(5), DebitAccountID: ToUint128(101), CreditAccountID: ToUint128(102), Amount: ToUint128(10), Ledger: 1, Code: 1, Flags: 0, } transferResults, err : client.CreateTransfers([]Transfer{transfer0, transfer1}) // Results handling omitted.十、两阶段转账Two-Phase TransfersTigerBeetle原生支持两阶段转账发起时设置Pending标志TigerBeetle 会把金额记入双方账户的credits_pending/debits_pending随后必须再发送一笔对应的 post 或 void 转账来终结这笔 pending 转账。相关概念与记账语义可参考 two-phase-transfers 文档 与 debit-credit 文档。10.1 Post 一笔 Pending 转账设置post_pending_transfer标志后TigerBeetle 会原子地回滚双方debits_pending/credits_pending的变化并将其应用到debits_posted/credits_posted余额。transfer0 : Transfer{ ID: ToUint128(6), DebitAccountID: ToUint128(101), CreditAccountID: ToUint128(102), Amount: ToUint128(10), Ledger: 1, Code: 1, Flags: TransferFlags{Pending: true}.ToUint16(), } transferResults, err : client.CreateTransfers([]Transfer{transfer0}) // Results handling omitted. transfer1 : Transfer{ ID: ToUint128(7), // Post the entire pending amount. Amount: AmountMax, PendingID: ToUint128(6), Flags: TransferFlags{PostPendingTransfer: true}.ToUint16(), } transferResults, err client.CreateTransfers([]Transfer{transfer1}) // Results handling omitted.注意Amount传AmountMax128 位最大值表示「post 全部 pending 金额」若传具体金额则必须是全部或部分且不能超过 pending 金额否则返回TransferExceedsPendingTransferAmount。10.2 Void 一笔 Pending 转账设置void_pending_transfer标志则相反TigerBeetle 回滚debits_pending/credits_pending的变化但不会应用到debits_posted/credits_posted即这笔转账被作废。transfer0 : Transfer{ ID: ToUint128(8), DebitAccountID: ToUint128(101), CreditAccountID: ToUint128(102), Amount: ToUint128(10), Timeout: 0, Ledger: 1, Code: 1, Flags: TransferFlags{Pending: true}.ToUint16(), } transferResults, err : client.CreateTransfers([]Transfer{transfer0}) // Results handling omitted. transfer1 : Transfer{ ID: ToUint128(9), Amount: ToUint128(0), PendingID: ToUint128(8), Flags: TransferFlags{VoidPendingTransfer: true}.ToUint16(), } transferResults, err client.CreateTransfers([]Transfer{transfer1}) // Results handling omitted.完整的两阶段生命周期验证可参考 samples/two-phase/main.go含 post 前后 pending/posted 余额断言和 samples/two-phase-many/main.go5 笔 pending 转账交替 post/void逐步断言DebitsPending从 1500 递减到 0。十一、转账查询LookupTransferstransfers, err : client.LookupTransfers([]Uint128{ToUint128(1), ToUint128(2)})与账户查询语义一致批量传入id返回匹配的转账未匹配的 ID 在响应中没有对应对象返回顺序不一定与请求顺序一致请依据响应中的id字段区分。注意转账查询目前只是精确查找不是灵活的查询 API。TigerBeetle 正在开发查询 API未来将新增转账查询方法。更多细节可参考 lookup_transfers 参考文档。十二、预览版查询 API以下四个 API 均为preview 状态在稳定查询 API 落地前可能发生破坏性变更。它们的响应都按timestamp升序或降序排列。12.1 GetAccountTransfers账户转账流水获取涉及某账户的转账支持基础过滤与分页对应操作TB_OPERATION_GET_ACCOUNT_TRANSFERS。filter : AccountFilter{ AccountID: ToUint128(2), UserData128: ToUint128(0), // No filter by UserData. UserData64: 0, UserData32: 0, Code: 0, // No filter by Code. TimestampMin: 0, // No filter by Timestamp. TimestampMax: 0, // No filter by Timestamp. Limit: 10, // Limit to ten transfers at most. Flags: AccountFilterFlags{ Debits: true, // Include transfer from the debit side. Credits: true, // Include transfer from the credit side. Reversed: true, // Sort by timestamp in reverse-chronological order. }.ToUint32(), } transfers, err : client.GetAccountTransfers(filter)AccountFilter结构体定义见 bindings.goAccountFilterFlags的位Debitsbit 0包含借记侧、Creditsbit 1包含贷记侧、Reversedbit 2时间倒序。12.2 GetAccountBalances账户时点余额获取某账户的时点余额point-in-time balances支持过滤与分页。前提只有创建账户时设置了history标志的账户才保留历史余额快照AccountFlags{History: true}.ToUint16()。余额响应同样按timestamp排序。filter : AccountFilter{ AccountID: ToUint128(2), UserData128: ToUint128(0), // No filter by UserData. UserData64: 0, UserData32: 0, Code: 0, // No filter by Code. TimestampMin: 0, // No filter by Timestamp. TimestampMax: 0, // No filter by Timestamp. Limit: 10, // Limit to ten balances at most. Flags: AccountFilterFlags{ Debits: true, // Include transfer from the debit side. Credits: true, // Include transfer from the credit side. Reversed: true, // Sort by timestamp in reverse-chronological order. }.ToUint32(), } account_balances, err : client.GetAccountBalances(filter)返回的AccountBalance包含DebitsPending、DebitsPosted、CreditsPending、CreditsPosted与Timestamp见 bindings.go。参考文档见 get_account_balances。12.3 QueryAccounts按字段交集查询账户按若干字段的交集和时间戳范围查询账户filter : QueryFilter{ UserData128: ToUint128(1000), // Filter by UserData UserData64: 100, UserData32: 10, Code: 1, // Filter by Code Ledger: 0, // No filter by Ledger TimestampMin: 0, // No filter by Timestamp. TimestampMax: 0, // No filter by Timestamp. Limit: 10, // Limit to ten accounts at most. Flags: QueryFilterFlags{ Reversed: true, // Sort by timestamp in reverse-chronological order. }.ToUint32(), } accounts, err : client.QueryAccounts(filter)12.4 QueryTransfers按字段交集查询转账filter : QueryFilter{ UserData128: ToUint128(1000), // Filter by UserData. UserData64: 100, UserData32: 10, Code: 1, // Filter by Code. Ledger: 0, // No filter by Ledger. TimestampMin: 0, // No filter by Timestamp. TimestampMax: 0, // No filter by Timestamp. Limit: 10, // Limit to ten transfers at most. Flags: QueryFilterFlags{ Reversed: true, // Sort by timestamp in reverse-chronological order. }.ToUint32(), } transfers, err : client.QueryTransfers(filter)QueryFilter与QueryFilterFlags定义见 bindings.goQueryFilterFlags目前仅有Reversedbit 0倒序。过滤条件的完整语义可参考 query_filter 参考文档 与 account-filter 参考文档。十三、链接事件Linked Events原子链在创建账户或转账时指定linked标志即可把该事件与批内下一个事件链接起来形成一条任意长度、全部成功或全部失败的事件链链的尾部是第一个没有linked标志的事件——因此批内最后一个事件绝不能带linked标志否则链是开放的、永不闭合一个批次内可以同时存在多条链或独立事件它们各自独立成功/失败链内事件按顺序执行出错时整体回滚链中每个事件的副作用对链内后续事件可见整条链对链外后续事件要么整体可见要么整体不可见第一个破坏链的事件会获得独特的错误结果链内其余事件统一返回linked_event_failed对应AccountLinkedEventFailed/TransferLinkedEventFailed。链式事件的完整示例注意其中故意构造了失败链与成功链的对比batch : []Transfer{} linkedFlag : TransferFlags{Linked: true}.ToUint16() // An individual transfer (successful): batch append(batch, Transfer{ID: ToUint128(1) /* ... rest of transfer ... */}) // A chain of 4 transfers (the last transfer in the chain closes the chain with linkedfalse): batch append(batch, Transfer{ID: ToUint128(2) /* ... , */, Flags: linkedFlag}) // Commit/rollback. batch append(batch, Transfer{ID: ToUint128(3) /* ... , */, Flags: linkedFlag}) // Commit/rollback. batch append(batch, Transfer{ID: ToUint128(2) /* ... , */, Flags: linkedFlag}) // Fail with exists batch append(batch, Transfer{ID: ToUint128(4) /* ... , */}) // Fail without committing // An individual transfer (successful): // This should not see any effect from the failed chain above. batch append(batch, Transfer{ID: ToUint128(2) /* ... rest of transfer ... */}) // A chain of 2 transfers (the first transfer fails the chain): batch append(batch, Transfer{ID: ToUint128(2) /* ... rest of transfer ... */, Flags: linkedFlag}) batch append(batch, Transfer{ID: ToUint128(3) /* ... rest of transfer ... */}) // A chain of 2 transfers (successful): batch append(batch, Transfer{ID: ToUint128(3) /* ... rest of transfer ... */, Flags: linkedFlag}) batch append(batch, Transfer{ID: ToUint128(4) /* ... rest of transfer ... */}) transferResults, err : client.CreateTransfers(batch) // Results handling omitted.链接事件的完整语义链式提交/回滚、linked_event_failed传播可参考 linked-events 文档。十四、导入历史事件Imported Events创建账户或转账时指定imported标志即可以用户自定义时间戳导入历史事件例如从旧系统迁移数据整个批次必须全部设置imported标志否则报AccountImportedEventExpected/AccountImportedEventNotExpected推荐把整个批次作为一条linked链提交任何事件失败则整批不提交集群时间戳保持不变这样应用可以修正失败事件后用相同的时间戳重新提交而不会让集群时间戳发生回退imported事件必须满足时间戳单调递增等约束见AccountImportedEventTimestampMustNotAdvance、TransferImportedEventTimestampMustNotRegress、TransferImportedEventTimestampMustPostdateDebitAccount等错误码。// External source of time. var historicalTimestamp uint64 0 historicalAccounts : []Account{ /* Loaded from an external source. */ } historicalTransfers : []Transfer{ /* Loaded from an external source. */ } // First, load and import all accounts with their timestamps from the historical source. accountsBatch : []Account{} for index, account : range historicalAccounts { // Set a unique and strictly increasing timestamp. historicalTimestamp 1 account.Timestamp historicalTimestamp account.Flags AccountFlags{ // Set the account as imported. Imported: true, // To ensure atomicity, the entire batch (except the last event in the chain) // must be linked. Linked: index len(historicalAccounts)-1, }.ToUint16() accountsBatch append(accountsBatch, account) } accountResults, err : client.CreateAccounts(accountsBatch) // Results handling omitted. // Then, load and import all transfers with their timestamps from the historical source. transfersBatch : []Transfer{} for index, transfer : range historicalTransfers { // Set a unique and strictly increasing timestamp. historicalTimestamp 1 transfer.Timestamp historicalTimestamp transfer.Flags TransferFlags{ // Set the transfer as imported. Imported: true, // To ensure atomicity, the entire batch (except the last event in the chain) // must be linked. Linked: index len(historicalAccounts)-1, }.ToUint16() transfersBatch append(transfersBatch, transfer) } transferResults, err : client.CreateTransfers(transfersBatch) // Results handling omitted.. // Since it is a linked chain, in case of any error the entire batch is rolled back and can be retried // with the same historical timestamps without regressing the cluster timestamp.导入的顺序要求是先导入全部账户、再导入转账转账引用的账户必须已存在。相关错误码见 bindings.go 中AccountImportedEvent*与TransferImportedEvent*系列常量如AccountImportedEventTimestampMustNotRegress、TransferImportedEventTimeoutMustBeZero即导入的转账Timeout必须为 0。十五、超时与取消可靠的幂等提交客户端会无限期重试不施加任何单请求超时取消cancellation只是被提供的一种机制具体取消策略由应用自行决定Client实例可在任何时刻Close()关闭后所有在途请求被取消并向调用方返回错误对应ErrClientClosed见 tb_client.go 中TB_CLIENT_INVALID与TB_PACKET_CLIENT_SHUTDOWN分支即使调用方收到错误请求仍可能已被 TigerBeetle 服务端处理——这正是重试场景下需要以 ID 做端到端幂等的原因。为保证转账可安全重试请使用稳定的转账ID实现端到端幂等如果重发同 ID 的转账服务端会返回TransferExists或TransferExistsWithDifferent*提示字段不一致从而避免重复入账。完整的可靠性策略见 reliable-transaction-submission 文档。十六、与其他客户端的协同认知本文介绍的 API 形态CreateAccounts、CreateTransfers、LookupAccounts、LookupTransfers以及四个 preview 查询 API与 TigerBeetle 其余语言客户端保持一致底层都通过 cgo 绑定同一份 tb_client.h 原生头文件与预编译静态库macOS/Linux/Windows 各平台各有对应的libtb_client_*目标文件见 tb_client.go 中的 cgo 链接指令。若需深入了解服务端请求处理与批次语义可继续阅读 requests 参考文档、请求语义文档 与 数据建模文档客户端接口全集含实验性GetChangeEvents见 tb_client.go 的Client接口定义。结语至此你已经掌握了 Go 客户端操作 TigerBeetle 的全部核心能力环境搭建与客户端初始化、账户与转账的批量创建和查询、两阶段转账的 post/void 生命周期、链式事件的原子性、历史数据的导入以及以批量化为核心的性能优化路径。把这些模式组合起来即可构建一套面向关键任务场景、具备端到端幂等保障的记账与支付核心。【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表