ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Gel Python 客户端高级用法指南:事务选项、重试策略与 State 执行上下文

2026/9/23 9:35:12 拓冰建站 浏览量
Gel Python 客户端高级用法指南:事务选项、重试策略与 State 执行上下文 Gel Python 客户端高级用法指南事务选项、重试策略与 State 执行上下文【免费下载链接】edgedbGel supercharges Postgres with a modern data model, graph queries, Auth AI solutions, and much more.项目地址: https://gitcode.com/gh_mirrors/ed/edgedb本文以 gel-python 官方驱动文档为核心系统讲解gel.Client/gel.AsyncIOClient的三类高级配置TransactionOptions事务选项、RetryOptions重试策略以及由默认模块、模块别名、会话配置与全局变量共同组成的State执行上下文。读完本文你将掌握如何通过with_*系列方法创建浅拷贝客户端副本为不同业务场景分别定制事务隔离级别、自动重试规则与查询执行环境并理解其与 EdgeQL 中set module、set alias、configure session、set global等命令的对应关系。本文基于 advanced.rst 展开并结合 client.rst 中的 API 定义、仓库测试用例与底层实现进行印证。gel-python 是 Gel 数据库的官方 Python 驱动同时提供阻塞式Blocking与 asyncio 两种实现二者 API 基本一致文中的示例默认以 asyncio 风格为主阻塞式写法完全同理把async for tx in .../async with tx:换成for tx in .../with tx:即可。前置准备快速创建客户端所有高级用法都建立在客户端对象之上。通过gel.create_async_client()或gel.create_client()创建客户端后它会自动从project init目录、环境变量或连接参数中发现数据库import asyncio import gel client gel.create_async_client() async def main(): await client.ensure_connected() # 显式触发首次连接尽早暴露配置错误 print(await client.query(select 2 2;)) # [4] asyncio.run(main())客户端内部维护一个动态大小的连接池连接按需lazily创建。这一点对理解本文至关重要每次调用with_*方法得到的浅拷贝客户端与原客户端共享同一个连接池并不会新建连接因此可以放心地在运行时按需派生出多个定制客户端而不会产生连接资源开销参见 client.rst 中关于连接池的说明。事务选项Transaction OptionsTransactionOptions 与 IsolationLevelTransactionOptions用于定制后续client.transaction()开启的事务行为构造参数如下参数类型默认值说明isolationIsolationLevelIsolationLevel.Serializable事务隔离级别readonlyboolFalse为True时事务为只读deferrableboolFalse为True时事务为可延迟deferrableIsolationLevel是一个枚举包含三个取值Serializable可串行化隔离级别即默认值RepeatableRead可重复读隔离级别仅支持在只读事务中使用PreferRepeatableRead如果服务器分析后认为某条查询支持可重复读则使用可重复读否则回退到可串行化。TransactionOptions还提供了类方法defaults()返回默认的事务选项对象可用于把派生客户端的选项重置回默认状态。在客户端上设置事务选项通过gel.Client.with_transaction_options或gel.AsyncIOClient.with_transaction_options方法设置。这两个方法返回当前客户端的浅拷贝原对象与返回的新对象都可以继续使用但各自应用不同的事务选项互不影响import gel client gel.create_async_client() # 只读 可重复读隔离级别的客户端副本 ro_client client.with_transaction_options( gel.TransactionOptions( isolationgel.IsolationLevel.RepeatableRead, readonlyTrue, ) ) async def main(): # 在 ro_client 上开启的事务是只读、可重复读的 async for tx in ro_client.transaction(): async with tx: await tx.execute(select 1;) # 只读事务中仅允许查询 # 原 client 仍以默认的 Serializable 隔离级别工作 async for tx in client.transaction(): async with tx: await tx.execute(insert Movie { title : Iron Man };) asyncio.run(main())事务选项只对后续调用Client.transaction()/AsyncIOClient.transaction()生效单条查询如query、execute不开启显式事务不受这些选项影响。仓库测试中也大量使用了该模式例如 test_edgeql_delete.py 用with_transaction_options(TransactionOptions(readonlyTrue))验证只读事务下执行写操作会报错test_edgeql_insert.py 与 test_edgeql_update.py 同样用它构造只读事务场景。由此可见readonlyTrue是实际应用中最常见的定制点之一适合报表、审计类只读逻辑。重试选项Retry Options默认重试行为gel-python 会对单个 EdgeQL 命令或整个事务块在可重试错误retryable errors上自动重试。默认策略为最多尝试3 次采用指数退避exponential backoff起始间隔100ms每次等待时间额外加上小于 100ms 的随机抖动random hash避免多个客户端同时重试造成重试风暴。RetryOptions 与 RetryCondition默认策略可通过RetryOptions精细定制参数类型说明attemptsint重试的总尝试次数含首次backoffCallable[[int], float or int]退避函数接收当前尝试次数返回下次重试前等待的秒数RetryOptions.with_rule(condition, attemptsNone, backoffNone)可以为特定条件添加重试规则condition取自RetryCondition枚举TransactionConflict当发生TransactionConflictError事务冲突/序列化失败时触发NetworkError当发生ClientError网络类错误时触发。这种按条件分规则的机制让你可以为网络抖动和事务冲突分别配置不同的重试次数与退避节奏。RetryOptions.defaults()返回默认重试选项。在客户端上设置重试选项通过gel.Client.with_retry_options或gel.AsyncIOClient.with_retry_options设置同样返回浅拷贝import gel client gel.create_async_client() # 针对高并发写入场景事务冲突多退避几次网络错误少退避 optimistic_client client.with_retry_options( gel.RetryOptions.defaults().with_rule( conditiongel.RetryCondition.TransactionConflict, attempts5, backofflambda attempt: min(0.1 * 2 ** attempt, 2.0), ).with_rule( conditiongel.RetryCondition.NetworkError, attempts2, backoffgel.default_backoff, ) ) async def main(): async for tx in optimistic_client.transaction(): async with tx: value await tx.query_single(select Counter.value;) await tx.execute( update Counter set { value : int64$value };, valuevalue 1, ) asyncio.run(main())仓库中 test_backend_ha.py 使用edgedb.RetryOptions(60, lambda x: 1)构造固定退避 1 秒、最多 60 次尝试的策略来测试主备切换场景test_server_concurrency.py 则用RetryOptions(attempts1, backoffedgedb.default_backoff)关闭重试只尝试一次以精确验证并发事务冲突的报错路径。这两个测试分别代表了重试选项的两种典型用法加大重试以容忍瞬时故障以及关闭重试以让错误立即暴露。事务块内代码会被重放需要特别强调的是transaction()是一个可重试的事务循环retryable transaction loop对应 RFC 1004 的设计重试发生时整个嵌套代码块会重新执行。因此事务块内的 Python 代码不应有副作用、不应耗时过长——例如发送欢迎邮件这类操作写在事务块里可能因重试而被执行多次。正确做法是把带副作用的操作移到事务块之外或先提交事务再执行。这是使用重试选项时必须牢记的约束。State执行上下文State 的四个组成部分State是影响 EdgeQL 命令执行的执行上下文包含四类信息State(default_moduleNone, module_aliases{}, config{}, globals_{})参数类型说明default_modulestr or None后续命令执行的默认模块None表示使用服务端默认模块通常为defaultmodule_aliasesdict[str, str]模块别名映射别名 - 目标模块configdict[str, object]非系统级的会话配置配置名 - 配置值globals_dict[str, object]全局变量值全局名 - 值其中全局变量的命名有两条规则可以是限定名如my_mod::glob2也可以是默认模块下的简单名简单名会自动以前缀加上当前默认模块限定名中的模块别名会被解析为实际模块名。State 的调整方法State提供了一系列返回新 State 副本的调整方法每个方法都对应一条 EdgeQL 会话命令方法等价命令说明with_default_module(moduleNone)set module/reset module调整默认模块传None重置为默认with_module_aliases(aliases_dictNone, /, **aliases)set alias合并新增模块别名without_module_aliases(*aliases)reset alias删除指定别名不传参数则清空全部with_config(config_dictNone, /, **config)configure session set合并设置会话配置without_config(*config_names)configure session reset重置指定配置不传参数则重置全部会话配置with_globals(globals_dictNone, /, **globals_)set global合并设置全局变量without_globals(*global_names)reset global重置指定全局变量不传参数则清空全部几个容易踩坑的注意点with_default_module()和with_module_aliases()不会影响已经存入该 State 的简单名/别名形式的全局变量——它们的名字在写入时就已经被解析成实际全限定名了新模块只影响之后调用with_globals()添加的全局变量。with_config/with_globals支持位置参数 dict 关键字参数两种写法关键字参数在 dict 之后应用后写覆盖先写。可用配置参数以 cfg.rst 中cfg::AbstractConfig列出的配置项为准例如apply_access_policies等会话级配置系统级instance 级配置不允许通过 State 设置。在客户端上设置 State通过gel.Client.with_state或gel.AsyncIOClient.with_state设置返回共享连接池的浅拷贝影响拷贝上执行的所有后续命令import gel client gel.create_async_client() # 用 State 一次性定制执行上下文 admin_client client.with_state( gel.State( default_moduleapp, module_aliases{cfg: cfg, std: std}, config{apply_access_policies: False}, globals_{current_user_id: 00000000-0000-0000-0000-000000000000}, ) ) async def main(): # 在 admin_client 上执行的查询 # 1. 默认模块是 app可省略 app:: 前缀 # 2. 全局变量 current_user_id 立即可用 result await admin_client.query_single( select User { * } filter .id ? global current_user_id; ) print(result) asyncio.run(main())客户端上的快捷方法除了统一的with_state()客户端还直接暴露了等价快捷方法见 client.rst 中Client与AsyncIOClient的 API 参考with_default_module(moduleNone)with_module_aliases(aliases_dictNone, /, **aliases)与without_module_aliases(*aliases)with_config(config_dictNone, /, **config)与without_config(*config_names)with_globals(globals_dictNone, /, **globals_)与without_globals(*global_names)它们与with_state行为完全一致只是只调整对应的那部分状态值。其中with_globals是应用开发中最常用的方法——例如在请求处理中为每条查询注入当前用户上下文async def handle_request(client, user_id: str): scoped client.with_globals(current_user_iduser_id) return await scoped.query_single( select Post { title, author: { name } } filter .author.id global current_user_id; )仓库测试 test_edgeql_globals.py 演示了con.with_globals(cur_userAlice)后查询select { cur_user : global cur_user }返回{cur_user: Alice}的完整流程同一个文件后续用例还验证了错误类型赋值如把int赋给应接收其他类型的全局变量会正确报错说明with_globals传值同样遵循类型校验。多租户与请求隔离的推荐实践综合上述机制一个常见的多租户/多角色应用模式是维护一个共享连接池的根客户端按请求动态派生带不同 State 的浅拷贝客户端。由于浅拷贝共享连接池派生成本极低由于 State 相互独立不同请求间的默认模块、全局变量、会话配置天然隔离且不会污染根客户端。这与 client.rst 中的建议一致应只创建一次客户端实例避免重复建池运行时通过with_*派生配置。小结gel-python 的高级配置围绕共享连接池、按需派生浅拷贝这一核心思想展开事务选项TransactionOptionsIsolationLevel定制transaction()的隔离级别、只读与可延迟属性PreferRepeatableRead提供了性能与一致性的折中重试选项RetryOptionsRetryCondition把默认的3 次、指数退避 随机抖动细化为按错误条件事务冲突 / 网络错误分别配置的规则同时必须警惕事务块内代码会被重放State把默认模块、模块别名、会话配置、全局变量打包成可复用的执行上下文其各调整方法与 EdgeQL 会话命令一一对应并可通过客户端快捷方法直接使用。三者的 API 在阻塞式gel.Client与异步gel.AsyncIOClient上完全对称掌握其中一种即可无缝迁移到另一种。深入阅读可继续查看 advanced.rst、client.rst 以及仓库测试 test_backend_ha.py、test_edgeql_globals.py、test_server_concurrency.py 中的真实用法。【免费下载链接】edgedbGel supercharges Postgres with a modern data model, graph queries, Auth AI solutions, and much more.项目地址: https://gitcode.com/gh_mirrors/ed/edgedb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考