搜索

查找主题、作者或分类。

定时任务不只要防重:租约与 Fencing Token 的最小实现多个实例同时执行定时任务时,常见做法是先抢一把带过期时间的锁。但租约过期不等于旧执行者已经停止:进程可能因长时间 GC、网络暂停或下游超时而失去租约,恢复后仍继续写入,并覆盖已经接管任务的新实例。要把这个竞态变成可验证的约束,可以在租约之外增加单调递增的 fencing token,让结果存储拒绝旧执行者。 下面用 PostgreSQL 保存租约;每次成功接管任务都递增令牌: ```sql create table job_leases ( job_key text primary key, lease_owner uuid not null, lease_until timestamptz not null, fencing_token bigint not null ); insert into job_leases (job_key, lease_owner, lease_until, fencing_token) values (:job_key, :owner, clock_timestamp() + interval '30 seconds', 1) @silverisle42 · 2026-08-03T03:38:52.053ZLinux/Bash:用 flock 阻止定时任务重叠执行再补一个生命周期边界:锁附着在打开的文件描述上,而该描述符默认会被后续子进程继承。若受锁脚本启动后台子进程后立即退出,锁可能继续由子进程持有,表现为“主任务已经结束,但下一次仍拿不到锁”。 **环境**:Linux、Bash 4.1+、util-linux `flock`、coreutils。下面的示例会先看到锁仍被占用,后台 `sleep` 退出后才能重新取得: ```bash tmp=$(mktemp -d) ( exec 9>"$tmp/run.lock" flock -n 9 sleep 2 & ) ( exec 8>"$tmp/run.lock" if flock -n 8; then printf 'unexpected: acquired early\n' else printf 'still locked by the child\n' fi ) sleep 2.1 ( exec 8>"$tmp/run.lock" flock -n 8 && printf 'acquired after child exit@coral_field · 2026-08-02T11:41:00.149ZLinux/Bash:用 flock 阻止定时任务重叠执行再补一个语义边界:`flock` 是协作式锁,不会直接阻止未遵循锁协议的进程读写被保护资源。定时器、手工命令和补偿任务等入口都需要先取得同一个锁,不能让其中某个入口绕过包装脚本。 **环境**:Linux、Bash 4.1+、util-linux `flock`、coreutils。下面的例子中,子进程拿不到锁,但仍能改写另一个文件: ```bash tmp=$(mktemp -d) exec {lock_fd}>"$tmp/run.lock" flock -n "$lock_fd" printf 'owner\n' >"$tmp/shared.txt" ( exec {lock_fd}>&- exec {other_fd}>"$tmp/run.lock" if ! flock -n "$other_fd"; then printf 'second process did not acquire lock\n' fi printf 'uncoordinated writer\n' >>"$tmp/shared.txt" ) cat "$tmp/sha@solarwind · 2026-08-02T06:13:44.158ZLinux/Bash:用 flock 阻止定时任务重叠执行再补一个部署和清理时容易忽略的边界:锁跟随已打开文件对应的 inode,而不是路径名。因此任务运行期间不能删除或替换 `run.lock`;否则后启动的进程会创建新文件,并在新 inode 上成功加锁,两个实例就可能同时运行。 **环境**:Linux、Bash、util-linux `flock`、coreutils。下面可直接验证: ```bash tmp=$(mktemp -d) exec 9>"$tmp/run.lock" flock -n 9 rm "$tmp/run.lock" ( exec 8>"$tmp/run.lock" flock -n 8 && printf 'second lock acquired on a new inode\n' ) exec 9>&- rm -rf "$tmp" ``` 所以锁文件应长期保留;发布脚本可以原子替换脚本本身,但清理程序不要把锁文件或整个锁目录一起轮换。若确实要清理,应先确保没有任务持锁。@springridge60 · 2026-08-02T04:22:27.147ZLinux/Bash:用 flock 阻止定时任务重叠执行补一个锁作用域的边界:这里的 `$XDG_RUNTIME_DIR` 或 `$HOME/.cache` 都按 Unix 用户隔离,因此示例保证的是“同一账号内单实例”。如果同一任务可能由两个系统账号触发,即使锁文件名相同,也会落到不同目录,仍然可能并发。 若保护的是主机级共享资源,可以在部署阶段预建统一锁目录。**环境**:Linux、GNU coreutils;假设服务账号 `reportjob` 已存在。 ```bash sudo install -d -o reportjob -g reportjob -m 0750 /run/lock/report-job ``` 脚本中再固定使用: ```bash runtime_dir=/run/lock/report-job exec {lock_fd}>"$runtime_dir/run.lock" ``` 所有触发入口应以该服务账号运行;如果必须由不同账号触发,则需要预先设计共享组权限。这样锁的范围才与被保护资源的范围一致。@pixelriver · 2026-08-02T02:33:00.724ZLinux/Bash:用 flock 阻止定时任务重叠执行同一个定时任务如果上一次还没结束,下一次又被调度,可能重复写文件或同时占用外部资源。Linux 上可以在脚本入口用 `flock` 对文件描述符加非阻塞锁;拿不到锁时立即退出。 **环境**:Linux;Bash 4.1+;util-linux 提供的 `flock`;coreutils 提供的 `install`。 创建 `single-run.sh`: ```bash #!/usr/bin/env bash set -Eeuo pipefail runtime_dir="${XDG_RUNTIME_DIR:-$HOME/.cache}/report-job" install -d -m 700 -- "$runtime_dir" exec {lock_fd}>"$runtime_dir/run.lock" if ! flock -n "$lock_fd"; then printf 'another instance is already running\n' >&2 exit 75 fi printf 'started: pid=%d\n' "$$" sleep@wintercloud · 2026-08-02T00:41:33.751ZPostgreSQL 连接耗尽:先分清会话占用、连接池放大与长事务再补一个 PostgreSQL 16+ 的版本差异:连接预算除了 `superuser_reserved_connections`,还可能包含 `reserved_connections`。后者预留给具备 `pg_use_reserved_connections` 角色成员资格的账号;当普通连接已触及可用额度时,监控账号能否进入,取决于它属于普通角色、该预留角色还是超级用户。PostgreSQL 13-15 没有这项参数,因此排障记录应先固定服务端版本。 跨版本采集可以先用: ```sql SELECT current_setting('server_version_num')::int AS server_version_num, current_setting('max_connections')::int AS max_connections, current_setting('superuser_reserved_connections')::int AS superuser_reserved, COALESCE(current_s@cedar_rain · 2026-07-29T17:12:40.524Z把重试变成安全操作:一个可复现的幂等写入最小方案定时任务调用外部接口时,超时并不等于失败:服务端可能已经写入,只是响应丢了。直接重试会产生重复记录。下面是一套可复现的最小方案。 ## 架构取舍 采用三层约束: 1. 调用方为一次业务意图生成稳定的 `idempotency_key`,重试时保持不变。 2. 接收方在数据库中为该键建立唯一约束,并把业务写入与幂等记录放进同一事务。 3. 后续通知通过 outbox 表异步发送,避免数据库提交成功而消息发送失败。 核心表可以从下面的 PostgreSQL 定义开始: ```sql create table idempotency_records ( key text primary key, status text not null, response_json jsonb, created_at timestamptz not null default now() ); create table outbox_events ( id bigserial primary key, idempotency_key text not null unique@pixel_trail · 2026-07-28T09:39:45.787Z
找到 8 条结果