CEPH EC覆盖写实现

概述

EC由于需要对原始数据进行编码、解码,覆盖写时必须保证边界对齐。实际情况下客户端发起覆盖写入时传入的offset和length是随机的,因此EC执行时需要先将原始offset和length按stripe进行对齐,读取对齐后对应的数据片段,根据客户传入的新数据对读取的数据进行修改,完成后写入底层,整个流程为read-modify-write(rmw)。

内部实现

使用EC时,ECBackend负责每个pg的EC相关操作。由于覆盖写入经常需要先读取数据,因此在ECBackend中实现了一个cache:ExtenCache,用来管理每个pg已经读到本地内存中的extent,当一次PGTransaction完成后,这些extent会释放掉。

完成一次PGTransaction需要在ECBackend中生成一个Op,在不同阶段,Op位于不同队列中

  • Op初始化后放在waiting_state队列,等待进一步处理
  • Op被从waiting_state队列取出后,直接放入waiting_reads队列,并向其他shard发起读取请求
  • 所有读取请求都完成后,Op被移至waiting_commit队列,并向其他shard发起写入请求
  • 发往每个shard的写入请求完成后,Opwaiting_commit队列移除,至此,一个Op才算完成

在上面这些步骤中 EC 完成了 read-modify-write ,下面详细介绍。

  • READ

为了实现read,ECBackend在每个Op中放入了一个ECTransaction::WritePlan,write_plan 负责记录当前需要写入的extent(WritePlan::will_write)和由于对齐原因需要读取的extent(WritePlan::to_read)。将Op的write_plan 初始化后直接放入了waiting_state队列。计算 write_plan 过程并不复杂,根据用户的PGTransaction信息和目标对象的HashInfo扩展属性即可计算出为了按 stripe 对齐,还需要读取哪些数据。
有了 write_plan 后,就可以查找ExtentCache中是否包含 WritePlan::to_read 中的 extent,对于不在 cache 中的 extent,放入Op::remote_read中,表示需要从各个shard读取的数据(这些数据是stripe对齐的,和用户要写入的数据范围有重叠),而Op::remote_read_result则用来存放最终读取结果。
向各个 shard 发起读请求是通过两个回调类ClientAsyncReadStatusCollectionContext以及两个关键数据结构read_request_tReadOp完成,ECBackend::handle_sub_op_reply时会将结果放在ReadOp::complete中,需要的 shard 读取都完成后,通过ECBackend::complete_read_op()将 shard 的数据解码,最终放入Op::remote_read_result

  • MODIFY

modify 由ECTransaction::generate_transactions()实现,此函数根据PGTransaction对已经读到的数据进行修改(这些是没按stripe对齐的extent请求),已经 stripe 对齐的 extent 请求只需正常写入即可。最终生成发往每个shard 的ObjectStore::Transaction,返回给调用者一个写入后的最新数据,调用者据此更新cache。

  • WRITE

write 动作只是将已经生成好的ObjectStore::Transaction发送给各个shard,写入请求都返回后对cache进行清理,释放掉前面使用过的extent。

你可能感兴趣的:(CEPH EC覆盖写实现)