[Summer Code Camp] issues-2561 When the cluster capacity is almost full, make the cluster readonly

1. 项目背景和目标:

issue 要求在集群容量接近满时将集群设置为只读模式,以避免数据丢失和写操作失败。本项目的目标是实现该功能并确保在集群容量恢复到可用状态后重新设置为可读写模式。

2. 实现思路:

为了实现该功能,可以采取以下步骤:

步骤 1: 检测单个 chunkserver 空间不足
  • op_request.cpp 文件中的 WriteChunkRequest::OnApply 函数 和 copyset_node.cpp 文件中的 CopysetNode::on_err 函数中进行更改。
  • 添加逻辑以检测单个 chunkserver 空间是否足够,如果不足,则不进行直接写入操作。
  • 可以通过获取当前 chunkserver 空间使用情况并与预定义的阈值进行比较来判断空间是否足够。
步骤 2: 将集群设置为只读模式
  • 创建一个函数或方法来将集群设置为只读模式。
  • 在该函数中,将所有 chunkserver 的写入权限设置为只读,禁止写入操作。
  • 可以通过修改 chunkserver 的权限配置或设置一个全局的读写权限控制开关来实现这一点。
步骤 3: 恢复集群为可读写模式
  • 创建一个函数或方法来恢复集群为可读写模式。
  • 在该函数中,将所有 chunkserver 的写入权限设置为可读写,允许写入操作。
  • 可以通过修改 chunkserver 的权限配置或修改全局的读写权限控制开关来实现这一点。
步骤 4: 监控集群容量
  • 创建一个定时任务或监控程序来检测集群容量是否接近满。
  • 可以通过定期获取集群的空间使用情况并与预定义的阈值进行比较来判断集群是否接近满。

3. 实现方法:

  • 在 Curve 的代码库中,根据上述步骤的描述进行相应的修改和添加。
  • 对于步骤 1,修改 WriteChunkRequest::OnApply CopysetNode::on_err 函数,添加空间不足检测的逻辑。
  • 对于步骤 2 和步骤 3,创建相应的函数或方法,并在其中设置相应的权限配置或修改全局的读写权限控制开关。
  • 对于步骤 4,创建一个定时任务或监控程序,并在其中实现集群空间使用情况的监测。

4. 其他内容:

  • 单元测试:对修改的代码进行单元测试,确保功能的正确性和稳定性。
  • 集成测试:在适当的环境中进行集成测试,验证整个系统的功能和性能。
  • 文档更新:更新相应的文档,包括代码注释、API 文档和用户手册等。
  • 代码review:提交代码,根据review进行代码修改,确保修改后的代码能够顺利合入到正式仓库中。

你的方案过于简单了,细节方面考虑的也不对

  1. 问题可分为两个部分,集群总空间不足和单个chunkserver空间不足的情况,mds端要处理集群总空间不足的情况,chunkserver端要处理单个chunkserver空间不足的情况
  2. 检测chunkserver空间不足不可能在WriteChunkRequest::OnApply和CopysetNode::on_err里面做
  3. 集群设置只读模式在哪里设置,谁去设置?怎么通知client只读? IO怎么处理,返回错误还是hang住
  4. 集群恢复后,怎么恢复IO,inflight的IO怎么办,丢掉还是重试?怎么重试
  5. 集群容量目前有统计和上报mds,不需要额外的监控程序
  6. 另外,可以调研下ceph的实现,在集群容量接近满时是怎么个逻辑,可做参考

您说的很对,之前考虑的确实太简单了,我再去详细调研一下,修改一下设计方案,再回答您的问题。

@xuchaojie 参考了一下 ceph OSD 的处理方法,感觉 curve 和 ceph 的结构还是有些区别,个人理解chunkserver 类似于 ceph 中的 OSD, MDS 应该类似 ceph 中的 monitor ,一些细节部分了解的还不太清楚,后续如果遇到问题,可能还需要详细去看 ceph 的相关代码。

在 ceph 中,对 osd full 定义了五种状态:

s_names get_full_state(std::string type) const {
    if (type == "none")
      return NONE;
    else if (type == "failsafe")
      return FAILSAFE;
    else if (type == "full")
      return FULL;
    else if (type == "backfillfull")
      return BACKFILLFULL;
    else if (type == "nearfull")
      return NEARFULL;
    else
      return INVALID;
  }

参考 ceph 的设计,可以将本功能如下设计:

1. Chunkserver 端

  1. 通过心跳包将磁盘容量信息上报 MDS. (此处为已有逻辑,应该不需要额外开发)

  2. 在 client 有相关查询消息时,判断磁盘状态,如无法写入,向 client 返回当前 chunkserver 对应的state ,并附带提示信息,如返回 onlyread 状态,并告知 chunkserver 磁盘已满,当前为只读状态

2. MDS 端

  1. Chunkserver 通过心跳包定时将状态信息上报 MDS,MDS 收到心跳包之后,判断 chunkserver 的磁盘状态,一旦超过某个阈值,赋予其 chunkserver_full 标志

  2. 如所有 chunkserver 或达到一定阈值的 chunkserver 都变为 chunkserver_full 状态,则表示 curve 磁盘将满,当有 client 请求写入数据时,向客户端返回 MDS_full 状态,并提示 MDS 磁盘已满,当前为只读状态

当磁盘空间满时,获取可以做一些空间移动或减少缓存操作,释放一定空间?

3. Client 端

  1. 收到 chunkserver 返回的 state 信息后,判断其对应状态,根据状态不同,向用户提示相应的消息

  2. 收到 MDS 返回的 chunkserver 信息后,判断其对应状态,根据状态不同,向用户提示相应的消息

集群状态恢复

Chunkserver 恢复状态后,会将状态通过心跳上报给MDS,上报成功之后,MDS修改 chunkserver状态,集群恢复正常。

恢复后,Inflight 的 IO 操作,

如果先前的IO操作仍然有效且需要保证数据的完整性,进行重试,在重试之前,可以等待集群完全恢复和数据平衡完成,以确保最佳的读写性能和数据一致性。

如果之前IO操作无效或数据损坏,根据日志进行恢复操作,或者向客户端返回,恢复期间数据冲突,操作失败,请用户稍后重试

之前已经向用户返回失败状态的操作,不再重试

你这个做法是另一个题目【 [CurveBS] Add chunkserver-level statistics and allocation limits for the curvebs capacity statistics and allocation module · Issue #2559 · opencurve/curve · GitHub 】 要做的事情,这里有一些重复,你这里要解决的是另一件事情,当 单个 chunkserver 空间不足时,现有的这两个地方会导致chunkserver挂掉,你要做的是保证不让chunkserver挂掉,或者chunkserver挂掉之后仍然能够起来。你这个通过心跳的方案,由于心跳是15s一次,对容量的统计不可能是实时的,导致在极端情况下你是防止不住的,IO仍然会下来,目前的逻辑是chunkserver就会因为没有空间挂掉。
再补充一些吧,2559这个题目做的是当空间不足时不让新的空间分配,你的题目是,当用户IO下来时,空间不足了,此时chunkserver不能挂掉,需要做一些操作,hang住IO之类的,等待空间调度走后恢复IO,类似这样

好的,我再按这个方向调研一下