VeilSwap
← 全部文章
基础设施2026-11-048 分钟阅读

站点宕机时,你的钱到底在哪

2026 年 10 月 23 日,我们的宿主机掉电 22 分 38 秒。没有一笔订单因此丢失,因为入金地址属于流动性供应商,不属于我们。我们还实测了那张 503 页到底返回什么,以及源站消失时订单页还能显示什么。

2026 年 10 月 23 日,跑 veilswap.io 的宿主机掉电。内核日志停在 08:48:05 UTC,下一次启动的第一条记录是 09:10:43——中间 22 分 38 秒,源站根本不存在。那段时间有两个事实比任何安抚都值钱:第一,08:40 到 09:30 之间站内新建订单是 0,没有人正好卡在发款那一刻;第二,数据库回来后一行没少——今天库里 147 单,119 过期、21 已完成、5 失败、2 已退款。宕机没有被藏进缓存里。用户真正会问的问题更小也更难:页面打不开的时候,我的钱在哪?我还能不能查这单?这篇用实测回答这两个问题。

维护页到底是什么。 它是一支部署在 Cloudflare 边缘的 Worker,脚本名 veilswap-maintenance,完全不回源。它的 fetch 处理函数没有任何路径分支:页面请求、API 请求、POST,落到它手上返回的都是同一份 2,393 字节的 HTML。2026-11-04 10:04 UTC 实测:HTTP 503、content-type 是 text/html、cache-control: no-store, must-revalidate、retry-after: 300,外加标记头 x-veilswap-maint: 1。五次采样的首字节时间是 48–125 毫秒,因为答案产生在边缘、根本没有离开边缘。带 Accept-Language: zh-CN 时同一页渲染成 2,396 字节、lang 为 zh-CN。POST 也是 503——它同样不看方法。

十秒上线,十秒下线。 开关是路由,不是 Worker。把 veilswap.io/__maint-probe/* 这条规则绑到脚本上,10 秒内生效(t+5s 还是 404,t+10s 已经 503);删掉它同样 10 秒(t+5s 仍 503,t+10s 回到 404)。这点重要,因为这个循环只有一半是自动的:守护脚本每 5 分钟跑一次,从机内探源站(3001 端口被防火墙 DROP,机外 curl 永远 000),应用回 200 就把维护路由逐条删掉。加路由是手工的。这是坦白的缺口,也是那张诚实的 503 页并非必然出现的原因:没人去挂路由时,访客看到的是 Cloudflare 的 522,而 522 什么也解释不了。

维护模式下,订单页只剩骨架。 /order/<id> 是一个 32,795 字节的空壳,状态根本不在 HTML 里,是浏览器再向 /api/order/<id> 取的。挂着维护路由时,这条 API 路径返回的也是那份 503 HTML——这是实测的,不是推测,因为 Worker 里没有针对 /api 的分支。客户端逻辑写在组件里:响应不是 ok 就直接返回、继续每 5 秒轮询;而订单对象仍然是 null,于是页面停在灰色脉冲占位上。没有错误文案、没有标签、没有解释。真拿 JSON 解析器去读那个 body,先抛异常。这是刻意的取舍:维护期把写接口一起挡住,临时环境才不会产生真实订单。代价同样清楚——源站宕机的这段时间,确实没东西可查。要做离线查询,得在别处再放一份数据库,而我们现在没有。

币从来不在那台机器上。 VeilSwap 是非托管的,代码把这句话写得很字面:订单上的入金地址,就是报价那家流动性供应商返回的 payinAddress。我们不持有它的私钥。所以源站死掉时,入金地址照常工作——供应商自己的基础设施还在盯着它,宕机期间发出的款由他们入账,不是我们。我们手上只有一行记录:订单号、上游订单号、金额、地址、状态。这一行才是后来变回页面的东西。它同时解释了唯一真正糟糕的情况:把订单号弄丢。没有它,这行记录很难找回来,而整个流程里唯一说「兑换不受影响」的那屏,就是 404 兜底页。

三种宕机,三种症状。 源站宕机:边缘 503、所有路径、48–125 毫秒、维护页文案。上游 API 挂了而源站还活着:/api/order 返回 200,带库里最后一次状态和一个 pollError 字段,订单页出现琥珀色横幅「状态同步稍有延迟」,继续轮询、不编造终态。你自己的网络断了:什么都解析不出来,而在这台机器上测到的 1.11 秒报价耗时对你没有参考价值。分开看很关键,因为对策是相反的。第一种:等,留着链接。第二种:别重发,钱可能已经在池方入账。第三种:换线路,不是换订单。

恢复之后,状态是自己长回来的。 cron 里每 5 分钟跑一次 vs_sweep.sh(日志写 ok:true 加各类计数),把所有非终态订单按建单时存下的上游订单号重新轮询。本地时钟是建单起 30 分钟;上游仍说等待入金,就把这行标成过期、打上 local_timeout 标记——今天 147 单里有 87 单带这个标记。第二遍扫描回看 6 小时,把「本地已取消但上游后来收到款」的单翻回已支付。上游完全查不到的单,24 小时后强制作废。诚实说明:这条 6 小时回看路径到目前为止记录到的真实案例是 0,所以把它当机制看,不要当业绩看。

哈希只在你还在看的时候存在。 已完成的订单返回状态、上游状态、金额、地址和 validUntil——没有交易哈希。这是 2026-11-04 对一笔已完成订单的实测,而数据库表里也没有哈希列,所以订单一旦进入终态,哈希在我们这边就没了,它只存在于上游记录和你自己的钱包里。这里还有一处小矛盾,值得点出来而不是藏起来:维护页承诺「恢复后订单链接会显示同样的状态和交易哈希」。状态会给,哈希不会。发款那一刻就把 txid 从自己钱包里抄下来,或者趁订单还在确认中从页面复制。

宕机当天该做的六件事。 一,留住订单链接,它是唯一的钥匙。二,关掉钱包前先把入金 txid 抄下来。三,不要为同一笔付款再建一单——原来那个地址照常收。四,页面是灰的就别重发,先查链(BTC、TRC20、ZEC、LTC 都有公开浏览器;XMR 没有,门罗只能看池方地址的动静)。五,站点回来后把链接打开两次、间隔十分钟——会动的状态才是活状态。六,如果一直不对,带着订单链接和 txid 发邮件到 [email protected];那个邮箱跑在另一套基础设施上,不受源站宕机影响。 (Zcash 与门罗 2026 对比)

我们不承诺什么。 我们不承诺那张诚实 503 页一定出现,因为挂路由今天还是手工的。我们不承诺离线查单,因为数据库只有源站这一份,每天 04:00 UTC 的备份是给恢复用的,不是给查询用的。我们不承诺开着不动的页面能扛过一次重启,因为它轮询的就是刚死掉的那个源站。能站住的承诺更窄、但每条都能验证:入金地址属于供应商,订单行在 SQLite(WAL)里活着,轮询靠上游订单号自己接上,而已完成的兑换是链上的事实——我们和供应商都改不了。页面和链不一致时,以链为准。

常见问题

网站宕机时我的钱在哪?

在你那笔订单的供应商入金地址上,我们从没持有它的私钥,所以源站宕机碰不到它。宕机期间发出的款由供应商入账;源站恢复后,我们的订单记录会按上游订单号重新轮询它。

维护页挂着的时候,我能查订单状态吗?

不能。维护 Worker 没有路径分支,所以 /api/order/<id> 拿到的和首页一样是那份 HTML 503(2026-11-04 实测),订单页会退成一个灰色骨架、每 5 秒重试。这是刻意的:维护期把写接口一起挡住,临时环境才不会产生真实订单。

钱明明到了,为什么订单显示已过期?

因为本地时钟是建单起 30 分钟,而供应商有它自己的截止时间,我们的时限过了它可能还在等。这行会被打上 local_timeout 标记为过期,之后 6 小时回看里,供应商一报款就翻回已支付。这个窗口里不要重发。诚实提醒:这条恢复路径到目前为止记录到的真实案例是 0,所以当机制看,别当历史业绩看。

准备好换币了吗?

无账户 · 无 KYC · 无邮箱

立即兑换
站点宕机时,你的钱到底在哪 — VeilSwap