news 2026/8/21 16:58:04

错误处理的艺术:deno-postgres 三类异常的捕获与恢复完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
错误处理的艺术:deno-postgres 三类异常的捕获与恢复完整指南

错误处理的艺术:deno-postgres 三类异常的捕获与恢复完整指南

【免费下载链接】postgresPostgreSQL driver for Deno项目地址: https://gitcode.com/gh_mirrors/postgr/postgres

deno-postgres 是专为 Deno 运行时打造的高性能 PostgreSQL 驱动,它以轻量、异步和开发者友好著称。无论你是刚接触 Deno 的新手,还是从 Node.js 生态迁移过来的老手,掌握 deno-postgres 错误处理都是写出健壮数据库应用的关键一步。本文将带你完整认识 deno-postgres 的三类核心异常——连接错误、SQL 执行错误与事务错误,并给出每一步的捕获方法与恢复技巧,让你从此告别"程序莫名崩溃"的噩梦。

为什么 deno-postgres 需要专门的错误处理体系?

数据库应用是错误的高发区:网络抖动、密码错误、SQL 语法错误、唯一约束冲突、死锁……每一种情况都以异常的形式抛出。deno-postgres 将不同类型的失败精心划分为独立异常类,全部定义在 client/error.ts 中,并在 mod.ts 统一导出。正确的做法不是"一把 catch 到底",而是按异常类型精准分流、分别处理。

第一类:连接异常,网络层出错的"前哨兵"

认识 ConnectionError 与 ConnectionParamsError

连接类错误负责守护"客户端与数据库之间的链路",包括两个兄弟异常:

  • ConnectionError:表示连接建立失败或连接中途断开。比如数据库没启动、端口不通、会话被强制终止(PG_TERMINATE_BACKEND)等。
  • ConnectionParamsError:表示连接参数本身不合法。比如缺少userpassword,或连接字符串解析失败,属于"出门前就发现车没油"的早期校验错误。

它们的抛出位置遍布 connection/connection.ts 与 connection/connection_params.ts,在调用client.connect()时最常见。

连接错误的捕获与自动重连恢复

deno-postgres 提供了优雅的自动重连机制:默认情况下每个客户端会重试 1 次,你也可以通过connection.attempts配置重试次数,并用interval实现指数退避:

const client = new Client({ connection: { attempts: 0, // 关闭自动重连,手动管理 }, }); try { await runQueryThatMayDisconnect(); } catch (e) { if (e instanceof ConnectionError) { console.log("连接已断开,正在手动恢复……"); await client.connect(); // 手动重连 } else { throw e; } }

要点:ConnectionError 抛出后,该连接会被标记为失效,后续查询前驱动会自动尝试重连,因此恢复成本远比想象中低。

第二类:SQL 执行异常,PostgresError 的完整解剖

PostgresError 里藏着哪些"金矿"?

当 SQL 语句执行失败——语法错误、约束冲突、权限不足——驱动会抛出PostgresError。它不只是简单的 Error 子类,还携带了数据库服务端返回的完整诊断信息(Notice 字段),以及触发错误的原始 SQL:

  • fields.message:人类可读的错误消息
  • fields.code:PostgreSQL 错误码,例如23505代表唯一约束冲突
  • fields.detail/fields.hint:详细说明与修复提示
  • fields.table/fields.column/fields.constraint:定位到具体对象
  • query:引发错误的 SQL 语句原文

按错误码精准捕获的实战姿势

import { PostgresError } from "https://deno.land/x/postgres/mod.ts"; try { await client.queryArray`INSERT INTO users (email) VALUES (${email})`; } catch (e) { if (e instanceof PostgresError) { if (e.fields.code === "23505") { console.log("邮箱已存在,走业务兜底逻辑"); } else { console.error(`SQL 出错:${e.fields.message},SQL:${e.query}`); } } }

这样既避免了模糊的catch (e)吞掉所有错误,又能针对数据库错误码做出精准业务响应。若想获取更丰富的调试信息,项目还提供了 debug.ts 调试选项,可以记录完整查询与结果。

第三类:事务异常,TransactionError 与自动回滚

事务错误为何是"终结者"?

PostgreSQL 事务有一条铁律:事务内一旦出现执行错误,整个事务立即进入 aborted 状态,此后的语句全部失败,直到ROLLBACKCOMMIT结束事务。deno-postgres 将这一行为封装得更为优雅——事务内的查询一旦抛出PostgresError,驱动会自动提交(终止)当前事务、释放被锁定的客户端,并抛出TransactionError。它的消息形如The transaction "xxx" has been aborted,并保留原始PostgresError作为cause。相关实现见 query/transaction.ts。

事务错误恢复的正确写法

const transaction = client.createTransaction("transfer"); try { await transaction.begin(); await transaction.queryArray`UPDATE accounts SET balance = balance - 100 WHERE id = 1`; await transaction.queryArray`UPDATE accounts SET balance = balance + 100 WHERE id = 2`; await transaction.commit(); } catch (e) { if (e instanceof TransactionError) { console.log("事务已自动终止,数据未受影响,可安全重试"); } else { throw e; } }

注意一个关键细节:只有数据库执行类错误会终止事务;像 savepoint 命名不合法这类"校验类错误"不会终结事务,事务可以继续使用。因此恢复逻辑要区分对待。

隔离级别与事务并发的可视化理解

选择合适的隔离级别能显著减少事务冲突异常(如序列化失败)的发生频率,这张对比图能帮你直观理解三种隔离级别的差异:

生产环境最佳实践:三招让错误处理固若金汤

  1. 用连接池代替裸 Client:在高并发场景下,事务会锁住当前客户端,导致其他查询排队失败。通过 pool.ts 的Pool.connect()获取独立连接执行事务,互不阻塞,用完记得release()
  2. 分层捕获,逐级收窄:最外层捕获ConnectionError决定是否重连,中层捕获PostgresError按错误码分流,内层捕获TransactionError决定是否重试整个事务,其余未知错误统一上抛并记录日志。
  3. 区分可恢复与不可恢复错误:唯一约束冲突(23505)、序列化失败(40001)通常可安全重试或走业务兜底;而连接断开则需等待重连完成再继续,切勿在错误状态上盲目重放整个事务。

小结

deno-postgres 错误处理的核心,是读懂它精心设计的异常家族:ConnectionError守护网络链路、PostgresError透传数据库诊断信息、TransactionError保证事务原子性。配合自动重连、事务自动终止和连接池三大恢复机制,你完全可以把"异常"从程序崩溃的源头,变成业务逻辑的有机组成部分。现在就动手在你的 Deno 项目中按这套方法重构错误处理吧,相信下一次故障排查会让你感谢今天的自己。

【免费下载链接】postgresPostgreSQL driver for Deno项目地址: https://gitcode.com/gh_mirrors/postgr/postgres

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/21 16:57:03

C++ 类的定义和实例化全解

1.类的定义1.1 类定义格式• class为定义类的关键字,Stack为类的名字,{}中为类的主体,注意类定义结束时后⾯分号不能省略。类体中内容称为类的成员:类中的变量称为类的属性或成员变量;类中的函数称为类的⽅法或者成员函…

作者头像 李华
网站建设 2026/8/21 16:53:15

从 $expand 字符串到 Expand Tree,深入理解 /IWBEP/IF_MGW_ODATA_EXPAND

在传统的 SAP Gateway 项目里,有一种性能问题很容易被低估。 一个页面只想显示 50 张销售订单,同时把订单行项目和业务伙伴一起带回来。前端发出的 OData 请求看上去很自然,类似下面这样。 SalesOrderSet?$top=50&$expand=ToLineItems,ToBusinessPartner 从 HTTP 请…

作者头像 李华