为什么一个字节能卡住 · znzryb
先把这个字节从进程里一路跟到数据库,看它在哪一站出事。

黄色箭头表示「NUL 还带在身上」。前三站对它都无所谓:
- **沙箱**只负责把程序打印的字节原样收下来;
- **截断**只负责「最多存 64 KB」,只看长度不看内容;
- **JSON** 能合法地表示这个字节(写成 `\u0000`),评测机发给仲裁器的那一跳完全正常。
一直到最后一站,数据库才第一次认真看内容,然后拒收。
## 这个字节到底是什么

`'\0'` 在 C++ 里是值为 0 的那个字符,十六进制 `00`;空格是 `20`。终端里看不出区别,很多 OJ 的比对也会把它当成「空白」放过去,所以这种笔误平时很难发现。
但 Postgres 的文本列**不许出现 0x00**。原因要追到 C 语言:在 C 里,0 这个字节的意思是「字符串到此结束」。Postgres 是用 C 写的,干脆规定文本里不准有它。图上黄色那三格,任何一格都会让整条写入失败。
## 为什么会空转三分半钟
如果数据库一拒,这条运行马上判成「系统错误」,那顶多是结果不对,不会有这三分半钟。空转是另一个问题造成的:

仲裁器写库失败时,不管原因是什么,一律回 503「暂时不可用」。评测机收到 503 的约定是「过一会儿再交」—— 这本来是为网络抖动、数据库重启这类**临时故障**准备的,等一等确实可能就好了。
可这次是**永久错误**:同样的字节交一万次,数据库拒一万次。给它挂上「临时故障」的标签,评测机就会认真地重试,直到租约过期、换机器、再重试。
所以这次其实是两个问题叠在一起:
1. 进数据库之前,没有把选手输出里数据库收不下的字节洗掉;
2. 数据本身的问题被当成了链路故障,于是重试救不了它,只能空转。
下一节讲怎么修。