系统资源读取与慢依赖隔离¶
背景¶
部署日志中的 scanner_info 更新、任务租约续期和 /api/scanner/network/public-ip
慢请求具有同一根因:扫描结果上传在 PostgreSQL 事务持有任务行锁和连接时等待
Elasticsearch。Elasticsearch 写入又使用 wait_for_active_shards=all;在单节点且索引
配置一个副本时,副本永远无法分配,重试会把一次上传放大到数分钟。后续心跳在锁上
排队,普通请求则在连接池上排队。
开发配置与部署配置是两套独立状态。本次代码不假定部署日志中的重试次数或连接池大小
与仓库 config.toml 相同;服务启动日志和部署配置应作为最终生效值的依据。
新的读取和持久化路径¶
系统资源采集仍按 log.status_duration 周期运行:
- 采集 CPU、RAM、磁盘、网络和 PostgreSQL 指标;数据库指标最多等待 3 秒。
- 先把完整快照发布到进程内
SystemResourceCache。 - 再采集 Elasticsearch 容量(同样最多 3 秒)并将同一快照写入
system_status。
若一次采集超过周期,定时器会跳过错过的 tick,不会在依赖恢复后突发补跑。
GET /api/data/system-data 只读内存中的最新完整快照,不申请 PostgreSQL 连接。
GET /api/data/system-resource-history 和数据库容量历史按请求范围从 PostgreSQL 的
system_status 聚合,以避免长期趋势数据占用进程内存。
缓存边界:仅保存一条最新完整快照。缓存为空时 system-data 返回空数据而不是等待
数据库;第一个采集周期完成后自动可用。
扫描上传事务边界¶
写入 Elasticsearch 的普通扫描和 DNS 批次采用两个短事务:
- 校验扫描器与任务、检查序列和 checksum,写入
storage_backend='pending'回执并提交。 - 不持有 PostgreSQL 连接或行锁,执行幂等 Elasticsearch Bulk。
- 重新开启短事务,锁定执行记录,把回执改为
dual/elasticsearch,写 PostgreSQL 事实(dual 模式)并完成任务。
若进程在步骤 2 或 3 中断,回执保留为 pending;扫描器使用相同 execution/upload/batch
和 checksum 重试时会继续幂等写入。不同内容复用同一批次身份仍返回 400。
心跳也拆成 scanner 状态更新和任务租约续期两个事务。事务使用本地
lock_timeout=1500ms,并设置 5–15 秒的语句上限;锁冲突会快速返回可重试错误,不再
形成分钟级锁队列。
配置建议¶
单节点 Elasticsearch:
[elasticsearch]
scan_number_of_replicas = 0
write_wait_for_active_shards = "1"
bulk_total_timeout_seconds = 25
bulk_max_concurrency = 2
scan_number_of_replicas 只影响新建/由服务管理的索引。部署环境已有业务索引需在维护
窗口单独把副本数改为 0;不要批量修改系统索引:
curl --cacert /etc/elasticsearch/certs/http_ca.crt -u elastic \
-X PUT 'https://localhost:9200/network-mapping-*/_settings' \
-H 'Content-Type: application/json' \
-d '{"index":{"number_of_replicas":0}}'
多节点环境可配置副本,但 write_wait_for_active_shards 不应高于故障期间实际可用的
分片副本数。
PostgreSQL 连接池默认快速失败边界:
[database]
pool_max_size = 10
pool_wait_timeout_ms = 2000
pool_create_timeout_ms = 5000
pool_recycle_timeout_ms = 3000
连接池扩容不能替代事务拆分。应先观察 pending 回执、锁等待和 Bulk 延迟,再根据稳定
并发量调整 pool_max_size。
上线检查¶
- 确认启动日志中的连接池和 Elasticsearch 配置与部署文件一致。
- 确认单节点业务索引
number_of_replicas=0,集群无业务副本未分配。 - 连续观察两个扫描周期:系统资源接口不应出现数据库连接等待。
- 检查未完成回执:
SELECT execution_id, upload_id, batch_index, created_at
FROM scan_result_batch
WHERE storage_backend = 'pending'
ORDER BY created_at;
- 正常运行时
pending只在 Bulk 执行期间短暂存在;长期存在表示扫描器尚未重试或 Elasticsearch 仍不可用。 - 监控 PostgreSQL
wait_event_type='Lock'、连接池 wait timeout、Elasticsearch Bulk 总预算超时,以及 HTTP 503。快速 503 比占满所有 worker 的长时间挂起更可恢复。