适用版本: 6.8-7.7+
1. 错误异常的基本描述 #
another snapshot is currently running cannot delete 是 Elasticsearch 在删除快照时抛出的并发控制错误。当你尝试删除某个快照(DELETE /_snapshot/<repo>/<snapshot>),但同一仓库中还有其他快照任务正在执行时,就会触发此错误。这个错误的核心是:仓库此刻仍被其他快照任务占用,不是目标快照本身在运行,而是其他快照在运行。
常见现象 #
- Elasticsearch 返回 HTTP
409 Conflict状态码,响应体中包含ConcurrentSnapshotExecutionException。 - 删除快照的请求(
DELETE /_snapshot/<repo>/<snapshot>)失败。 - 在 Elasticsearch 服务端日志中会记录详细的异常信息。
- 如果是通过定时任务(SLM)或自动化脚本删除快照,会导致清理任务失败。
- 可能导致旧快照积累,占用存储空间。
典型报错与异常栈 #
该异常的典型日志形态如下:
ConcurrentSnapshotExecutionException: [repo:snap-old] another snapshot is currently running cannot delete
at org.elasticsearch.snapshots.SnapshotsService.deleteSnapshot(SnapshotsService.java:...)
at org.elasticsearch.action.admin.cluster.SnapshotDeleteTransportAction.shardOperation(SnapshotDeleteTransportAction.java:...)
通过 API 请求的响应通常如下:
{
"error": {
"root_cause": [
{
"type": "snapshot_creation_exception",
"reason": "[repo:snap-old] another snapshot is currently running cannot delete"
}
],
"type": "snapshot_creation_exception",
"reason": "[repo:snap-old] another snapshot is currently running cannot delete",
"status": 409
}
}
另一种常见形态(查看当前快照):
GET /_snapshot/my_repo/_current
{
"snapshots": [
{
"snapshot": "snap-new",
"state": "STARTED",
...
}
]
}
2. 为什么会发生这个错误 #
Elasticsearch 的快照服务(SnapshotsService)会维护一个 SnapshotsInProgress 状态机来管理当前仓库上正在执行的快照任务。源码中的逻辑是:
SnapshotsInProgress.Entry snapshotEntry = snapshots != null ? snapshots.snapshot(snapshot) : null;
if (snapshotEntry == null) {
if (snapshots != null && !snapshots.entries().isEmpty()) {
throw new ConcurrentSnapshotExecutionException(snapshot, "another snapshot is currently running cannot delete");
}
}
这意味着:只要仓库中还有其他快照在运行(除了要删除的目标快照),删除操作就会被拒绝。
常见原因包括:
- SLM 定时快照还在运行:SLM 策略刚创建了一个新快照,还没结束,你又尝试删除旧快照。
- 快照执行时间长:由于数据量大或存储慢,快照运行时间拉长,导致清理窗口和备份窗口重叠。
- 自动化任务重叠:外部自动化同时做"创建新快照"和"清理旧快照",没有错开时间。
- 快照卡住:某个快照异常卡住,长时间不结束,导致后续所有删除操作被拒绝。
- 主节点切换:旧的快照条目可能由于主节点切换而未能正确清理,但仍然占用仓库。
- 并发快照操作:同一仓库有多个快照操作同时进行(虽然 Elasticsearch 通常不允许,但某些边缘情况可能发生)。
3. 如何排查和解决这个异常和解决这个异常 #
排查步骤 #
建议按以下顺序进行排查:
第一步:确认当前仓库正在运行的快照 #
# 查看当前仓库正在执行的快照
curl -X GET "localhost:9200/_snapshot/my_repo/_current?pretty"
# 查看所有快照及其状态
curl -X GET "localhost:9200/_snapshot/my_repo/_all?pretty" | jq '.snapshots[] | .snapshot, .state'
# 查看快照状态详情
curl -X GET "localhost:9200/_snapshot/my_repo/_status?pretty"
第二步:检查快照是否卡住或异常 #
# 查看快照进度
curl -X GET "localhost:9200/_snapshot/my_repo/snap-new/_status?pretty" | jq '.snapshots[0].shard_stats'
# 检查是否有失败的分片
curl -X GET "localhost:9200/_snapshot/my_repo/snap-new/_status?pretty" | jq '.snapshots[0].failures'
# 查看任务执行时间
curl -X GET "localhost:9200/_tasks?actions=*snapshot*&pretty" | jq '.tasks[] | select(.start_time_in_millis > 0)'
第三步:检查 SLM 或自动化任务时间线 #
# 查看 SLM 策略配置
curl -X GET "localhost:9200/_slm/policy?pretty"
# 查看 SLM 执行历史
curl -X GET "localhost:9200/_slm/policy/my_policy/_stats?pretty"
# 检查自动化脚本是否重叠执行
grep -E "snapshot|delete" /path/to/cron.log | tail -20
第四步:在测试环境验证 #
# 在测试环境先等待所有快照完成后,再执行删除
curl -X GET "localhost:9200/_snapshot/test_repo/_current?pretty"
# 应该返回 {"snapshots":[]}
# 然后删除旧快照
curl -X DELETE "localhost:9200/_snapshot/test_repo/snap-old"
排查时需要注意的问题_ #
- 区分"目标快照"和"其他快照":错误说的是其他快照在运行,不是你要删除的那个。
- 检查所有相关快照:可能是任何一个快照在运行,都会阻止删除操作。
- 注意 SLM 和手动操作:可能是 SLM 定时任务创建的快照在运行。
- 查看时间线:确认错误发生时是否有快照创建、恢复等操作重叠。
4. 如何解决这个错误 #
常用修复思路_ #
方案一:等待当前快照完成后再删除(推荐) #
# 监控当前快照状态,等待完成
while true; do
STATUS=$(curl -s "localhost:9200/_snapshot/my_repo/_current?pretty" | jq -r '.snapshots[0].state // "NONE"')
echo "Current snapshot state: $STATUS"
if [ "$STATUS" = "NONE" ]; then
echo "No active snapshot, proceeding with delete..."
break
fi
sleep 30
done
# 删除目标快照
curl -X DELETE "localhost:9200/_snapshot/my_repo/snap-old"
方案二:错开快照创建和删除时间窗口_ #
# 修改 SLM 策略,避免时间重叠
curl -X PUT "localhost:9200/_slm/policy/my_policy" -H 'Content-Type: application/json' -d '
{
"schedule": "0 1 * * *", # 调整到凌晨 1 点,避开清理时间
"name": "<my_index-{now/d}>",
"repository": "my_repo",
"config": {
"indices": ["my_index*"]
}
}'
# 清理脚本也调整到不同时间
# 例如:快照创建在凌晨 1 点,清理在凌晨 3 点
方案三:解决卡住的快照问题_ #
# 如果快照卡住,先查看详细信息
curl -X GET "localhost:9200/_snapshot/my_repo/snap-stuck?pretty"
# 如果确实卡住,考虑删除仓库重建(⚠️ 危险操作)
# 1. 先查看卡住的快照
# 2. 如果确定要删除仓库(会删除所有快照!)
curl -X DELETE "localhost:9200/_snapshot/my_repo"
# 3. 重新创建仓库
curl -X PUT "localhost:9200/_snapshot/my_repo" -H 'Content-Type: application/json' -d '
{
"type": "fs",
"settings": {
"location": "/mnt/snapshot/repo",
"compress": true
}
}'
方案四:在脚本中添加时间窗口检查_ #
#!/bin/bash
# 检查当前是否有快照在运行
ACTIVE_SNAPSHOT=$(curl -s "localhost:9200/_snapshot/my_repo/_current?pretty" | jq -r '.snapshots[0].snapshot // null')
if [ "$ACTIVE_SNAPSHOT" != "null" ] && [ "$ACTIVE_SNAPSHOT" != "" ]; then
echo "Warning: Snapshot [$ACTIVE_SNAPSHOT] is currently running, skipping delete..."
exit 0 # 不报错,只是跳过
fi
# 执行删除
curl -X DELETE "localhost:9200/_snapshot/my_repo/snap-old"
后续注意事项与推荐建议_ #
- 建立快照时间窗口规范:为 SLM 快照创建和旧快照清理分配不同的时间窗口,避免重叠。
- 监控快照执行状态:通过 INFINI Console 或 Elasticsearch 监控功能,及时发现长时间运行的快照。
- 在脚本中添加检查:对于自动化清理任务,先检查
_current状态再决定是否执行删除。 - 设置合理的超时:如果快照确实卡住,需要有超时机制来处理异常状态。
- 定期清理旧快照:建立自动化的快照生命周期管理,避免手动删除时的并发冲突。
借助 INFINI 产品提升排障效率_ #
INFINI Console 提供快照状态的可视化监控界面,可以直观地查看、管理和调试快照任务。通过 Console 的快照管理功能,可以快速发现正在运行的快照、查看执行进度,并直接删除旧快照。
INFINI Gateway 可以作为 Elasticsearch API 的代理层,在删除请求到达 Elasticsearch 之前进行拦截和检查。Gateway 可以自动检测仓库状态,并根据预定义的策略(如排队等待、返回友好错误、转发到备用仓库等)进行处理。
对于依赖快照进行数据备份的团队,建议结合 INFINI Console 的快照监控功能和 INFINI Gateway 的请求治理能力,建立从快照创建、清理、到监控的完整自动化流程,减少因并发冲突导致的删除失败。
5. 小结_ #
another snapshot is currently running cannot delete 是一个典型的并发控制错误,根源在于仓库中还有其他快照任务正在执行。虽然报错信息直接指向并发冲突,但解决思路需要根据具体情况来决定:是等待当前快照完成、错开时间窗口,还是解决卡住的快照。
在实际工作中,为避免此类问题,建议建立快照时间窗口规范、错开并发操作时间、并在自动化脚本中添加状态检查。更重要的是,考虑使用 INFINI Console 来实现可视化的快照管理,使用 INFINI Gateway 来拦截和预处理快照删除请求,从源头减少并发冲突的发生。
相关错误_ #
- a-snapshot-is-already-running:快照正在运行
- snapshot-with-the-same-name-is-already-in-progress:同名快照已在进行中
- snapshot-with-the-same-name-already-exists:同名快照已存在
- failed-to-perform-snapshot-index-files:快照执行失败
- snapshot-is-already-in-progress-cannot-delete:快照已在进行中无法删除
参考文档_ #
附:日志上下文_ #
下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:
SnapshotsInProgress.Entry snapshotEntry = snapshots != null ? snapshots.snapshot(snapshot) : null;
if (snapshotEntry == null) {
if (snapshots != null && !snapshots.entries().isEmpty()) {
throw new ConcurrentSnapshotExecutionException(snapshot, "another snapshot is currently running cannot delete");
}
}





