适用版本: 7.4-8.9
1. 错误异常的基本描述 #
cannot run cleanup on readonly repository 表示你正在尝试对一个只读快照仓库执行 cleanup 操作。cleanup 并不是只读查询,它会扫描并移除仓库中不再被引用的遗留 blob,因此本质上属于写操作或至少是需要修改仓库内容的维护动作。对于只读仓库,Elasticsearch 会直接拒绝这类请求。
从当前源码片段看,异常在 cleanup(...) 方法刚开始时就通过 isReadOnly() 判断抛出,这说明问题不在 cleanup 执行中途,而是在入口阶段就被策略拦截。只要仓库还是只读状态,cleanup 就不会继续执行。
常见现象 #
- 调用 repository cleanup 接口时立即失败,并提示仓库为 readonly。
- 仓库可能可以正常查看、校验甚至恢复快照,但只要执行 cleanup 就会被拒绝。
- 常见于灾备恢复仓库、跨集群共享仓库或被安全策略设为只读的对象存储仓库。
- 从业务侧看,表现通常不是搜索异常,而是仓库维护任务、容量回收任务或运维清理任务失败。
典型报错与异常栈 #
这类错误通常会与下面这些关键字一起出现:
cannot run cleanup on readonly repositoryrepository_exceptionreadonly repository
常见日志通常类似下面这样:
RepositoryException: [my_repo] cannot run cleanup on readonly repository
2. 为什么会发生这个错误 #
这个错误的根因非常直接:cleanup 需要对仓库内容做整理,而只读仓库不允许任何可能改变仓库状态的操作。也就是说,报错不是偶发故障,而是配置策略与运维动作不匹配。
常见原因通常包括:
- 仓库配置中明确设置了
readonly: true。 - 当前仓库是专门用于恢复或共享访问的,只允许读,不允许做 cleanup。
- 底层对象存储或文件系统权限本身就是只读,即使逻辑上想清理,实际也没有权限执行。
- 运维把 cleanup 任务错误地指向了只读仓库,而不是可写主仓库。
3. 如何排查和解决这个异常和解决这个异常 #
建议按“先确认仓库是否只读,再确认 cleanup 是否应该在这个仓库上执行”的顺序处理:
- 查看仓库配置,确认
readonly是否为true。 - 确认当前 cleanup 操作是不是误发到了恢复仓库、备份副本仓库或共享只读仓库。
- 如果业务上确实需要 cleanup,确认是否应该切换到对应的可写主仓库执行。
- 如果要把仓库改回可写,先核对底层存储权限和运维策略是否允许。
相关 Elasticsearch API 及调用说明 #
1. 查看仓库配置 #
curl -X GET "http://localhost:9200/_snapshot/my_repo?pretty"
重点看 settings.readonly 是否为 true。
2. 执行 cleanup #
curl -X POST "http://localhost:9200/_snapshot/my_repo/_cleanup?pretty"
如果仓库是只读的,这个接口会直接失败。
3. 更新仓库配置 #
如果业务上确定该仓库应允许维护操作,可以重新设置:
curl -X PUT "http://localhost:9200/_snapshot/my_repo?pretty" \
-H 'Content-Type: application/json' \
-d '{
"type": "fs",
"settings": {
"location": "/mount/backups/es",
"readonly": false
}
}'
但只有在仓库本来就该可写时才这样做,不能为了 cleanup 强行破坏恢复仓库的只读设计。
排查时需要注意的问题 #
- cleanup 是维护写操作,不要把它误认为“只是清理查看一下”这种只读操作。
- 如果仓库承担灾备恢复职责,通常应该保持只读,不建议为了 cleanup 临时改写策略。
- 修改 readonly 之前必须确认底层存储权限、团队流程和仓库用途都允许这样做。
4. 如何解决这个错误 #
常用修复思路 #
- 如果 cleanup 目标选错了,改到正确的可写仓库执行。
- 如果仓库本来就应当维护和回收空间,修正仓库配置为可写,并同步修复底层存储权限。
- 如果仓库设计上必须只读,就接受 cleanup 不能在此执行,并把清理动作放到主写仓库上完成。
后续注意事项与推荐建议 #
- 为每个仓库标明职责边界,例如“主写备份仓库”“只读恢复仓库”。
- 对 cleanup、delete snapshot、create snapshot 这类维护动作增加仓库类型检查,避免误操作只读仓库。
- 在运维平台中把 readonly 状态显式展示出来,减少认知偏差。
借助 INFINI 产品提升排障效率 #
- INFINI Console 可以帮助观察快照仓库相关任务、错误趋势和配置变更后的效果。
- INFINI Gateway 适合保留维护接口审计记录,帮助确认 cleanup 请求是不是发错仓库。
5. 小结 #
cannot run cleanup on readonly repository 的本质是仓库策略和运维动作冲突。只读仓库不接受 cleanup,这不是异常波动,而是明确的设计限制。
最有效的处理方式,是先确认这个仓库是否本来就不该被清理,再决定切换仓库还是调整其读写策略。只要把仓库用途区分清楚,这类错误通常很容易避免。
相关错误 #
- failed to delete snapshots:删除快照和 cleanup 一样都要求仓库具备写权限
- concurrent modification of the repository before cleanup started:即便仓库可写,cleanup 还可能败在并发修改上
- cannot add another snapshot to this repository as it …:仓库容量和数量治理常与 cleanup 场景同时出现
- failed to verify repository:调整只读策略或切换仓库后,最好先重新 verify
- failed to update snapshot in repository:仓库可写并不等于元数据提交一定成功,写回链路仍需单独检查
附:日志上下文 #
下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:
* @param listener Listener to complete when done
*/
public void cleanup(long repositoryStateId; Version repositoryMetaVersion; ActionListenerlistener) {
try {
if (isReadOnly()) {
throw new RepositoryException(metadata.name(); "cannot run cleanup on readonly repository");
}
MaprootBlobs = blobContainer().listBlobs();
final RepositoryData repositoryData = safeRepositoryData(repositoryStateId; rootBlobs);
final MapfoundIndices = blobStore().blobContainer(indicesPath()).children();
final SetsurvivingIndexIds = repositoryData.getIndices()





