📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 6.8-7.17

1. 错误异常的基本描述 #

a snapshot is already running 是 Elasticsearch 在创建快照时抛出的并发控制异常。当你尝试在某个快照仓库(repository)上创建新快照,但该仓库已经有正在执行的快照任务时,就会触发此错误。Elasticsearch 的快照机制采用串行执行策略,同一仓库同一时间只能有一个活跃的快照任务。

常见现象 #

  • Elasticsearch 返回 HTTP 409 Conflict 状态码,表示并发冲突。
  • 创建快照的请求(PUT _snapshot/<repo>/<snapshot_name>)失败。
  • 在 Elasticsearch 服务端日志中会记录 ConcurrentSnapshotExecutionException
  • 如果是通过定时任务(SLM)或自动化脚本创建快照,会导致后续快照任务失败。
  • 如果前一个快照长时间不结束,后续所有新快照都会连续失败。

典型报错与异常栈 #

该异常的典型日志形态如下:

ConcurrentSnapshotExecutionException: [repo:snap-20260331] a snapshot is already running
    at org.elasticsearch.snapshots.ConcurrentSnapshotExecutionException.<init>(ConcurrentSnapshotExecutionException.java:...)
    at org.elasticsearch.snapshots.SnapshotsService.createSnapshot(SnapshotsService.java:...)
    at org.elasticsearch.action.admin.cluster.SnapshotsStatusTransportAction.shardOperation(SnapshotsStatusTransportAction.java:...)

通过 API 请求的响应通常如下:

{
  "error": {
    "root_cause": [
      {
        "type": "snapshot_creation_exception",
        "reason": "[repo:snap-20260331] a snapshot is already running"
      }
    ],
    "type": "snapshot_creation_exception",
    "reason": "[repo:snap-20260331] a snapshot is already running",
    "status": 409
  },
  "status": 409
}

另一种常见形态(查看当前快照状态):

GET /_snapshot/my_repo/_current
{
  "snapshots": [
    {
      "snapshot": "snap-20260330",
      "state": "STARTED",
      "start_time_in_millis": 1711856400000,
      "failures": [],
      "shards_stats": {...}
  ]
}

2. 为什么会发生这个错误 #

Elasticsearch 的快照服务(SnapshotsService)会维护一个 SnapshotsInProgress 状态机来管理当前仓库上正在执行的快照任务。源码中的逻辑是:

if (snapshots != null && snapshots.asStream()
    .anyMatch(entry -> (entry.state() == State.INIT && initializingSnapshots.contains(entry.snapshot()) == false) == false)) {
    throw new ConcurrentSnapshotExecutionException(repositoryName, snapshotName, "a snapshot is already running");
}

这意味着只要 SnapshotsInProgress 里还有未完成的快照条目,就不会允许新的快照在同一仓库上并发启动。

常见原因包括:

  • 上一个快照仍在执行:初始化、写分片文件或等待 finalize 阶段尚未完成。
  • 定时任务和人工操作重叠:SLM 策略、外部调度器和人工快照请求撞在同一时间窗口。
  • 快照任务耗时过长:因为主节点切换、慢存储或分片问题导致快照执行时间异常长。
  • 自动化脚本缺少排他控制:失败后立即重复发起新快照,没有检查当前状态。
  • 主节点切换后的短时现象:旧任务状态尚未清理,导致新任务被拒绝。
  • 仓库状态异常:某些分片的快照操作卡住,导致整个快照无法完成。

3. 如何排查和解决这个异常和解决这个异常 #

排查步骤 #

建议按以下顺序进行排查:

第一步:查看当前仓库正在执行的快照 #

# 查看当前仓库正在执行的快照
curl -X GET "localhost:9200/_snapshot/my_repo/_current?pretty"

# 查看所有快照及其状态
curl -X GET "localhost:9200/_snapshot/my_repo/_all?pretty" | jq '.snapshots[] | select(.state != "SUCCESS")'

# 查看快照状态详情
curl -X GET "localhost:9200/_snapshot/my_repo/_status?pretty"

第二步:查看活跃任务和主节点日志 #

# 查看是否存在长时间运行的快照相关任务
curl -X GET "localhost:9200/_tasks?actions=*snapshot*&pretty"

# 查看主节点日志中的快照相关信息
tail -n 500 /var/log/elasticsearch/elasticsearch.log | grep -A 10 "snapshot.*STARTED\|INIT\|FINALIZE"

第三步:判断快照卡在哪个阶段 #

# 查看快照进度
curl -X GET "localhost:9200/_snapshot/my_repo/snap-20260330/_status?pretty" | jq '.snapshots[0].shards_stats'

# 检查是否有失败的分片
curl -X GET "localhost:9200/_snapshot/my_repo/snap-20260330/_status?pretty" | jq '.snapshots[0].failures'

第四步:核对定时任务和人工操作时间线 #

# 查看 SLM 策略的执行历史
curl -X GET "localhost:9200/_slm/policy/my_policy/_stats?pretty"

# 查看最近修改的快照相关配置
curl -X GET "localhost:9200/_cluster/state/metadata?pretty" | jq '.metadata.persistent.indices.".tasks"'

排查时需要注意的问题 #

  • 区分并发运行和同名快照a snapshot is already running 是并发执行冲突,snapshot with the same name already exists 是历史记录冲突,两者不同。
  • 不要盲目重试:如果快照任务只是慢,急于重试会叠加更多同类请求。
  • 检查主节点状态:如果是主节点切换后的短时现象,结合 cluster state 日志判断旧任务是否真的已经结束。
  • 关注耗时异常长的快照:正常快照应该在分钟级别完成,如果运行数小时需要深入排查。

4. 如何解决这个错误 #

常用修复思路 #

方案一:等待当前快照完成(推荐) #

# 监控当前快照状态,等待完成
while true; do
  STATUS=$(curl -s "localhost:9200/_snapshot/my_repo/_current" | jq -r '.snapshots[0].state // "NONE"')
  echo "Current snapshot state: $STATUS"
  if [ "$STATUS" = "NONE" ] || [ "$STATUS" = "SUCCESS" ]; then
    echo "Snapshot completed, can create new one now"
    break
  fi
  sleep 30
done

# 然后创建新快照
curl -X PUT "localhost:9200/_snapshot/my_repo/snap-$(date +%Y%m%d)" -H 'Content-Type: application/json' -d '
{
  "indices": "my_index*",
  "ignore_unavailable": true,
  "include_global_state": false
}'

方案二:调整 SLM 和备份任务的时间窗口 #

# 查看当前 SLM 策略的调度配置
curl -X GET "localhost:9200/_slm/policy?pretty"

# 更新策略,避免时间重叠
curl -X PUT "localhost:9200/_slm/policy/my_policy" -H 'Content-Type: application/json' -d '
{
  "schedule": "0 2 * * *",  # 调整执行时间
  "name": "<my_index-{now/d}>",
  "repository": "my_repo",
  "config": {
    "indices": ["my_index*"]
  }
}'

方案三:为自动化脚本增加状态检查 #

# Python 示例:在创建快照前检查当前状态
import requests
import time

repo = "my_repo"
snapshot_name = "snap-20260331"

# 检查当前是否有运行中的快照
response = requests.get(f"http://localhost:9200/_snapshot/{repo}/_current")
if response.json().get('snapshots'):
    print(f"Snapshot already running, waiting...")
    while True:
        time.sleep(30)
        response = requests.get(f"http://localhost:9200/_snapshot/{repo}/_current")
        if not response.json().get('snapshots'):
            break

# 创建新快照
requests.put(f"http://localhost:9200/_snapshot/{repo}/{snapshot_name}", json={
    "indices": "my_index*",
    "ignore_unavailable": True
})

方案四:处理卡住的快照(谨慎操作) #

# 如果快照确实卡住无法完成,可以考虑删除仓库重建(⚠️ 危险操作)
# 1. 先关闭所有索引
curl -X POST "localhost:9200/my_index*/_close"

# 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/snapshots",
    "compress": true
  }
}'

后续注意事项与推荐建议 #

  • 建立快照监控:监控快照执行状态、耗时和成功率,及时发现异常长的快照任务。
  • 错开定时任务时间:确保 SLM 策略、外部备份任务和人工操作的时间窗口不重叠。
  • 增加排他控制逻辑:在自动化脚本中,先检查 _current 状态再决定是否提交新快照。
  • 设置合理的超时:为快照操作设置合理的超时时间,避免长时间占用执行窗口。
  • 定期清理旧快照:建立快照生命周期管理,避免仓库中积累过多历史快照。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 提供快照状态的可视化监控界面,可以直观地查看、管理和调试快照任务。通过 Console 的快照管理功能,可以快速发现正在运行的快照、查看进度和失败分片,并直接删除异常卡住的快照。

  • INFINI Gateway 可以作为 Elasticsearch API 的代理层,在快照创建请求到达 Elasticsearch 之前进行拦截和检查。Gateway 可以自动检测并发冲突,并根据预定义的策略(如排队等待、返回友好错误、转发到备用仓库等)进行处理。

  • 对于依赖快照进行数据备份的团队,建议结合 INFINI Console 的快照监控功能和 INFINI Gateway 的请求治理能力,建立从快照创建、监控、到恢复的完整自动化流程,减少因并发冲突导致的备份失败。

5. 小结 #

a snapshot is already running 是一个典型的并发控制错误,根源在于同一仓库同一时间只能有一个活跃的快照任务。虽然报错信息直接指向并发冲突,但解决思路需要根据快照的实际状态来决定:是等待完成、调整时间窗口,还是处理卡住的任务。

在实际工作中,为避免此类问题,建议建立快照监控体系、错开定时任务时间、并在自动化脚本中增加状态检查逻辑。更重要的是,考虑使用 INFINI Console 来实现可视化的快照管理,使用 INFINI Gateway 来拦截和预处理快照创建请求,从源头避免并发冲突的发生。

相关错误 #

参考文档 #

附:日志上下文 #

下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:

if (snapshots != null && snapshots.asStream()
    .anyMatch(entry -> (entry.state() == State.INIT && initializingSnapshots.contains(entry.snapshot()) == false) == false)) {
    throw new ConcurrentSnapshotExecutionException(repositoryName, snapshotName, "a snapshot is already running");
}