Posts

A New Collection of Thoughtful Learning Apps — Now Available on iOS & Android

Image
I’m excited to share a set of mobile apps I’ve recently completed and published on both the Google Play Store and the Apple App Store. These apps are designed with a simple goal in mind: to make meaningful, structured content more accessible, whether you’re studying theology or improving your English vocabulary. 📱 Now Available on Both Platforms All apps are live and available for download: Google Play Developer Page: https://play.google.com/store/apps/dev?id=5835943159853189043 Apple App Store Developer Page: https://apps.apple.com/ca/developer/q-z-l-corp/id1888794100 📖 Theology & Confession Study Apps For those interested in Reformed theology and classical Christian teachings, I’ve developed a series of apps that present foundational texts in a clean, focused reading format: The Belgic Confession Canons of Dort Heidelberg Catechism Westminster Shorter Catechism Each app is designed to provide a distraction-free experience, making it easier to read, reflect, and revisit these im...

一次 403 错误的排查实录:Spring Cloud Gateway、WebFlux、PingFederate 与 CORS

Image
一次 403 错误的排查实录:Spring Cloud Gateway、WebFlux、PingFederate 与 CORS 一次真实的线上排错记录:一个看似 OAuth / JWT / Spring Security 的 403,最终却是 CORS 问题。 背景 我们在一次系统集成过程中,遇到了一个非常令人困惑的问题,涉及以下组件: Spring Cloud Gateway Spring Boot WebFlux 后端服务 PingFederate(OIDC / OAuth2) 本地运行的 React 前端( http://localhost:4200 ) 整体请求流程如下: 用户通过 PingFederate 登录(SSO) 前端获取 access token 前端发起 POST 请求 → Gateway → 后端服务 在以下场景中,一切都运行正常: 使用 Postman 直接调用后端 绕过 Gateway,直接调用后端 所有组件都在本地运行 但一旦请求通过 部署在 OpenShift 中的 Spring Cloud Gateway ,所有 POST 请求都会失败,并返回: 403 Forbidden 为什么这个问题如此迷惑 完全相同的 access token,在 Postman 中可以正常使用 GET 请求成功,POST 请求失败 系统中没有配置任何 role 或 scope 校验 不同环境下 401 / 403 的表现不一致 JWT 的 issuer、audience、签名全部正确 所有线索一开始都指向 OAuth 或 PingFederate 的配置问题——但这个判断是错误的。 关键线索:问题是如何被定位出来的 1. Postman 成功,浏览器失败 同一个请求,在不同客户端表现完全不同: Postman → 成功 浏览器(经 Gateway)→ 403 这一步非常关键,它强烈暗示: 问题并不在 token 本身 。 2. 在 Postman 中加上 Origin 头,请求立刻失败 我们在 Postman 中手动加入以下 header: O...

Debugging 403 Errors with Spring Cloud Gateway, WebFlux, PingFederate, and CORS

Image
Debugging 403 Errors with Spring Cloud Gateway, WebFlux, PingFederate, and CORS A real-world postmortem on a confusing 403 that turned out not to be OAuth, JWT, or Spring Security — but CORS. Background We recently ran into a frustrating issue while integrating: Spring Cloud Gateway Spring Boot WebFlux backend PingFederate (OIDC / OAuth2) A React frontend running locally ( http://localhost:4200 ) The request flow looked like this: User logs in via PingFederate (SSO) Frontend receives an access token Frontend sends POST requests → Gateway → Backend service Everything worked perfectly when: Calling the backend directly via Postman Calling the backend directly without the gateway Running everything locally But once the request went through Spring Cloud Gateway in OpenShift , all POST requests failed with: 403 Forbidden Why This Was Confusing The same access token worked in Postman GET requests succeeded, POST requests fa...

为什么 Vercel 部署后 PostgreSQL 仍然存在 Idle 连接

Image
为什么 Vercel 部署后 PostgreSQL 仍然存在 Idle 连接 如果你在 Vercel 上运行 Next.js , 并且直接连接 PostgreSQL ,你很可能遇到过下面的错误: remaining connection slots are reserved for roles with the SUPERUSER attribute 这个问题通常在重新部署后更容易出现,即使你的应用流量并不高。 常见现象 PostgreSQL 中存在大量 idle 状态的连接 Vercel 实例已经关闭,但连接仍未释放 新请求无法获取数据库连接 手动终止连接只能暂时缓解问题 真正的原因 这是 Serverless 架构的正常行为 。 Vercel 的工作方式大致如下: Vercel 根据请求启动多个 Serverless 实例 每个实例都会创建自己的数据库连接或连接池 实例结束后,无法保证数据库连接被优雅关闭 PostgreSQL 会继续保留这些连接直到超时 结果就是: 应用已经结束,但数据库连接仍然存在 。 为什么会导致连接数耗尽 在托管 PostgreSQL 服务中,一部分连接被保留给系统和 SUPERUSER。 当 Serverless 实例频繁创建连接时,很容易用完普通用户可用的连接数, 从而触发该错误。 这不是数据库 bug,而是架构不匹配导致的问题。 PgBouncer 如何解决 PgBouncer 位于应用和数据库之间: Vercel → PgBouncer → PostgreSQL 应用连接 PgBouncer,而不是直接连接数据库 PgBouncer 使用少量真实数据库连接服务大量请求 Serverless 实例关闭时,连接会被立即回收 PostgreSQL 不再堆积 idle 连接 对于 Serverless 场景,推荐使用 transaction pooling 模式。 推荐配置 启用 PgBouncer 应用连接 PgBouncer 的 host 和 port Node.js 连接池设置为 1–2 idleTimeo...

Why PostgreSQL Idle Connections Remain After Vercel Deployments

Image
Why PostgreSQL Idle Connections Remain After Vercel Deployments If you are running a Next.js application on Vercel with PostgreSQL , you may have encountered this error: remaining connection slots are reserved for roles with the SUPERUSER attribute This issue often appears right after a redeploy or during traffic spikes, even when your application traffic is relatively low. The Symptom PostgreSQL shows many connections in idle state Connections remain even after Vercel instances are shut down New requests fail with connection slot errors Manually terminating idle connections only fixes the problem temporarily What Is Really Happening This behavior is expected in serverless environments . Here is what happens behind the scenes: Vercel spins up serverless function instances Each instance creates its own PostgreSQL connection or connection pool When the instance finishes, it may shut down without gracefully closing the DB connection Post...

如何修复 PostgreSQL “剩余连接槽位保留给超级用户” 错误

Image
如何修复 PostgreSQL “剩余连接槽位保留给超级用户” 错误 ❌ 错误: 您的应用可能会出现如下报错: remaining connection slots are reserved for roles with the SUPERUSER attribute 这通常意味着 PostgreSQL 数据库的连接数已达到上限,部分连接可能是空闲事务会话,占用了可用槽位。 问题分析 PostgreSQL 通过 max_connections 限制并发连接数量。当所有普通槽位被占满时,只有超级用户可以连接。应用中长时间运行或空闲事务会话( idle-in-transaction )会占用连接,阻止新连接建立。 检查当前连接 ℹ️ 提示: 在终止任何连接之前,请先检查当前连接状态。 SELECT pid, usename, application_name, state, backend_start FROM pg_stat_activity ORDER BY backend_start; 注意查找 state 为 idle in transaction 的应用用户连接。 终止过期的空闲事务连接 ⚠️ 警告: 仅终止应用用户连接。不要操作 postgres 系统用户或后台工作进程。 SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND usename = 'your_app_user' AND now() - backend_start > interval '5 minutes'; 将 your_app_user 替换为应用使用的数据库用户名。此操作可以安全释放连接而不会影响正在执行的查询。 注意事项 ❌ 不要终止系统连接(如 TimescaleDB 工作进程、pg_cron 等) ✅...

How to Fix PostgreSQL ‘Remaining Connection Slots Reserved’ Error

Image
How to Fix PostgreSQL ‘Remaining Connection Slots Reserved’ Error If your application suddenly fails with an error like: remaining connection slots are reserved for roles with the SUPERUSER attribute It usually means your PostgreSQL database has reached its maximum number of allowed connections. Some of these connections may be idle-in-transaction sessions that are holding slots unnecessarily. This guide shows how to safely free up those connections. Understanding the Problem PostgreSQL limits the total number of concurrent connections through the max_connections setting. When all normal slots are used, only superusers can connect. Long-running or idle-in-transaction sessions from your app can consume slots and prevent new connections. Check Current Connections Before terminating anything, inspect your connections with: SELECT pid, usename, application_name, state, backend_start FROM pg_stat_activity ORDER BY backend_start; Look f...

安全数据清理:从大型 Oracle 表删除旧数据

Image
安全数据清理:如何从大型 Oracle 表中删除旧数据 为了遵守数据保留规定,我们定期清理旧数据。但是,从生产数据库删除数百万条记录需要非常谨慎。以下是我们的做法。 第一步:数据目录与计划 统计所有表并确定保留期限 与法律团队确认保留计划 与其他团队协作,识别下游依赖 第二步:备份表 删除前备份受影响表,确保出现问题时可以快速恢复。 第三步:理解表结构 先识别子表和父表。先删除子表,以避免外键约束错误。 第四步:批量删除 vs 直接删除 在 PT 环境中测试批量删除正常。但在生产环境中: 表超过 700 万行,需要删除超过 100 万行 20K 批量删除导致等待 1.5 小时并触发 FK 错误 出现自动回滚 现在采用增量删除: 每次删除 1 万条并提交 循环执行直到清理完成 速度慢但可控,易于定位 FK 错误 第五步:SQL 示例 -- 删除旧数据示例 SELECT '子表1', COUNT(*) FROM child_table_1 WHERE created_on < ADD_MONTHS(TRUNC(SYSDATE), -144); DELETE FROM child_table_1 WHERE created_on < ADD_MONTHS(TRUNC(SYSDATE), -144); COMMIT; DECLARE v_batch_size CONSTANT PLS_INTEGER := 20000; v_rows PLS_INTEGER; v_cutoff_date DATE := ADD_MONTHS(TRUNC(SYSDATE), -144); BEGIN LOOP DELETE FROM large_table WHERE created_on < v_cutoff_date AND ROWNUM <= v_batch_size; v...