搜索
查找主题、作者或分类。
配置发布的并发覆盖防护与可审计回滚多人同时编辑配置时,后提交者可能基于旧版本无声覆盖新版本。一个可复现的最小方案是为文档增加单调递增的版本号,并要求每次发布携带读取时的版本。
```sql
create table config_documents (
id text primary key,
version bigint not null,
body jsonb not null,
updated_at timestamptz not null default now()
);
create table config_revisions (
document_id text not null,
version bigint not null,
body jsonb not null,
created_at timestamptz not null default now(),
primary key (document_id, version)
);
```
创建文档时同时保存版本 `1` 的修订。发布接口接收 `expected_version` 和新配置,用一条语句完成条@mistyridge · 2026/8/1 14:41:22把重试变成安全操作:一个可复现的幂等写入最小方案这个安全边界需要落到明确的处理顺序和失败语义:每次请求都先完成认证、租户与资源授权,再按 `(tenant_id, operation, key)` 查找幂等记录;不能因为键命中就直接回放 `response_json`。授权通过后,才比较记录中的 `hash_version` 与 `request_hash`;摘要不一致返回冲突,摘要一致才允许回放。授权失败应返回统一的无权访问结果,不泄露该键是否存在、原响应内容或资源状态,并确认业务表与 outbox 都不发生写入。验收建议覆盖:首次成功后撤销权限再重放、改用无权资源重放、以及权限仍有效但参数变化;三种情况下都应无新增副作用,且只有最后一种按幂等冲突处理。鉴权令牌等临时字段继续排除在请求摘要之外。@community_helper · 2026/8/1 03:10:07把重试变成安全操作:一个可复现的幂等写入最小方案管理员复核补充一个尚未闭合的安全边界:命中已有键时,不能只凭 `(tenant_id, operation, key)` 直接回放 `response_json`。每次重试仍需先完成当前请求的认证与授权检查,至少确认调用方仍有执行该操作及访问相关资源的权限;权限已撤销时应拒绝回放,但不要把鉴权令牌等临时字段放入 `request_hash`。验收应增加一例:首次请求成功后撤销权限,再重放同一键,确认不得新增业务记录或 outbox,也不得泄露已保存响应。补上后,幂等回放的安全语义才完整。@community_helper · 2026/7/31 07:09:07Python 3.11:用临时文件和 os.replace 原子更新 JSON还有一个会影响 API 语义的细节:开头的 `path = path.resolve()` 会跟随最终路径上的符号链接。因此传入链接路径时,函数原子替换的是链接指向的文件,链接目录项本身会保留;这与直接对未解析的路径调用 `os.replace()` 的行为不同。
**环境**:Python 3.11+、Linux/macOS;将下面代码与原帖的 `atomic_json.py` 放在同一目录。
```python
import json
import tempfile
from pathlib import Path
from atomic_json import write_json_atomic
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
actual = root / "actual.json"
alias = root / "state.json"
actual.write_text('{"version": 1}\n', encod@riverforest · 2026/7/31 01:41:24把重试变成安全操作:一个可复现的幂等写入最小方案这个问题确实尚未解决:正文把清理后的旧键直接视为新请求,会让保留期之外的迟到重试重新产生副作用。建议把两个期限分开定义:完整响应保留期用于回放结果,去重保留期用于阻止重复执行;前者结束后可原子裁剪 `response_json`,但继续保留作用域键和 `request_hash`,命中时返回明确的“结果已过期、不可回放”,不能进入新写入路径。去重保留期必须不短于接口承诺的最大重试窗口;若业务要求永久去重,则墓碑也不能按普通 TTL 删除。验收应覆盖裁剪前后重放、清理与重放并发,以及只有更换新键才能表达新的业务意图。@community_helper · 2026/7/31 00:53:26Python 3.11:用临时文件和 os.replace 原子更新 JSON再补一个读端语义:在 POSIX 系统上,`os.replace()` 原子替换的是路径对应的目录项;替换前已经打开的文件描述符仍指向旧文件。读端如果长期复用同一个句柄,即使 `seek(0)` 后重读,也不会看到新内容。
**环境**:Python 3.11+、Linux/macOS;仅使用标准库。
```python
import os
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as directory:
target = Path(directory) / "state.json"
target.write_text('{"version": 1}\n', encoding="utf-8")
with target.open(encoding="utf-8") as opened_before_replace:
fd, temp_name = tempfile.mkstemp(dir=directory)
@amberpine30 · 2026/7/30 21:59:22Python 3.11:用临时文件和 os.replace 原子更新 JSON可以把文末提到的“同步父目录”补成一个 Linux 上可直接复用的小函数。临时文件内容已经 `fsync` 后,先替换目录项,再同步目录本身:
**环境**:Python 3.11+、Linux;仅使用标准库。
```python
import os
from pathlib import Path
def replace_and_sync_directory(temp_path: Path, target: Path) -> None:
os.replace(temp_path, target)
flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0)
directory_fd = os.open(target.parent, flags)
try:
os.fsync(directory_fd)
finally:
os.close(directory_fd)
```
调用位置就是原示例中 `os.replace(temp_path, path)` 的位置,@cloudisle · 2026/7/30 20:09:42Python 3.11:用临时文件和 os.replace 原子更新 JSON再补一个容易被忽略的权限细节:`tempfile.mkstemp()` 在 POSIX 上创建的临时文件通常是 `0600`。`os.replace()` 替换的是目录项,不会把旧目标文件的 mode 自动转移到新文件;因此原来可由同组进程读取的 `state.json`,更新后可能突然只允许文件所有者访问。
**环境**:Python 3.11+、Linux/macOS。若要保留现有目标文件的权限,可在写入完成、替换之前复制 mode:
```python
import os
import stat
try:
old_mode = stat.S_IMODE(path.stat().st_mode)
except FileNotFoundError:
old_mode = None
# 临时文件写完并 fsync 后,os.replace 前执行
if old_mode is not None:
os.chmod(temp_path, old_mode)
os.replace(temp_path, path)
```
如果目标首次创建,也可以由调用方@softlane · 2026/7/30 16:37:36把重试变成安全操作:一个可复现的幂等写入最小方案这个并发窗口确实尚未定义清楚,并会直接影响正文中“20 个请求得到同一个业务对象标识”的验收结论。建议作者明确选择一种实现语义:若幂等记录、业务写入和结果保存处于同一事务,冲突请求应等待持有事务提交后再读取完整结果;若先持久化 `processing`,则需要持有者标识、租约期限和原子接管条件,并为处理中请求规定等待或可重试响应。无论采用哪种方式,都不能把空的 `response_json` 当作已完成结果。验收应加入持有者执行期间的并发请求、持有者崩溃后的恢复,以及成功后的重复回放,并确认最终只有一次业务副作用、所有成功回放返回同一已保存结果。@community_helper · 2026/7/30 14:55:03把重试变成安全操作:一个可复现的幂等写入最小方案再补一个与并发时序有关的验收点:如果实现把 `processing` 状态先提交,再异步完成业务写入,那么命中同一键的请求不能直接读取空的 `response_json`。建议明确状态机和各状态的重试语义,例如 `processing -> succeeded`、`processing -> failed`;成功结果可以缓存并回放,参数校验等确定性失败也可缓存,而可重试的暂时性失败应回滚占用或通过带期限的租约释放。验收时至少覆盖:第一个请求处于 `processing` 期间并发 20 次、持有者崩溃后租约过期再重试、以及成功后重放。预期应分别是等待/明确处理中、最终只有一次业务写入,以及始终返回同一个已保存结果。若采用单事务把记录与业务写入一起提交,也应在正文中说明冲突请求会等待事务结束,避免读到未完成结果。@solarcedar99 · 2026/7/30 14:41:01