Cache Tiering

参考资料:

《Ceph源码分析》

https://my.oschina.net/u/2460844/blog/788172

Cache Tier

一、简介

Cache Tiering是Ceph中一种自动分层存储的技术,其中有两种存储池:高速池(缓存池,由SSD等高速设备组成)作为缓存层、低速池(数据池,由HDD等低速设备组成)作为数据层。对于客户端来说,Cache Tier是无感知的,它们把数据发给Objector Handles,并不关心数据是写到高速池或是低速池。这些内部数据流动由tiering agent来自动处理。数据分层的好处在于提高热点数据存储性能并且降低了存储成本(不需要全部使用SSD就能得到近似SSD性能)。缺点在于占用一定计算性能、数据IO。

在这里插入图片描述

图1.1 Cache Tier 原理图

通俗的讲,Cache Tier把用户经常访问的热数据放在高速池,把不经常访问的数据放在低速池,可以类比LRU缓存处理。站在用户的角度,一个访问总是能很快被响应,好像数据都是存放在高速设备一样,极大的提升了用户体验的同时又保证的成本不会太高(使用大量便宜的HDD和少量昂贵的SSD)。

实现这种分层存储的关键在于:

  • 数据访问行为的追踪、统计和分析

  • 数据如何迁移?

Cache Tier有四种模式:writeback、readproxy、readonly、proxy。后文会详细介绍每个模式的读写操作。

二、CacheTier 命令相关源码分析

所有命令都是在OSDMonitor::prepare_command_impl()方法中处理。

首先给出pool中与cache相关的部分参数,后面的命令基本和这些参数有关,在osd_types.h文件中。

pg_pool_t{

 std::set<uint64_t> tiers;   ///< pools that are tiers of us
  int64_t tier_of = -1;     ///< pool for which we are a tier
 // Note that write wins for read+write ops
  int64_t read_tier = -1;    ///< pool/tier for objecter to direct reads to
  int64_t write_tier = -1;   ///< pool/tier for objecter to direct writes to

}

命令1:ceph osd tier add {storagepool} {cachepool}

np是storagepool的pg_pool_t指针,ntp是cachepool的pg_pool_t指针。

 np->tiers.insert(tierpool_id);
 np->set_snap_epoch(pending_inc.epoch); // tier will update to our snap info
 ntp->tier_of = pool_id;
 ss << "pool '" << tierpoolstr << "' is now (or already was) a tier of '" << poolstr << "'";

上述代码中,在np中向其tiers队列插入cachepool_id;在ntp中tier_of设置为storagepool_id。

简而言之,在storagepool中添加cachepool_id,在cachepool中添加storagepool_id。分别保存在各自的pg_pool_t::tiers和pg_pool_t::tier_of中。

命令2:ceph osd tier remove/rm {storagepool} {cachepool}

np->tiers.erase(tierpool_id);
 ntp->clear_tier();
 ss << "pool '" << tierpoolstr << "' is now (or already was) not a tier of '" << poolstr << "'";

remove操作则是把storagepool的tiers队列中的cachepool_id给删除,同时把cachepool的tier_of给置为-1。

命令3:ceph osd tier set-overlay {storagepool} {cachepool}

read_tier和write_tier分别是读写操作的缓存池id。

pg_pool_t *np = pending_inc.get_new_pool(pool_id, p);
 np->read_tier = overlaypool_id;
 np->write_tier = overlaypool_id;
 np->set_last_force_op_resend(pending_inc.epoch);
 pg_pool_t *noverlay_p = pending_inc.get_new_pool(overlaypool_id, overlay_p);
 noverlay_p->set_last_force_op_resend(pending_inc.epoch);
 ss << "overlay for '" << poolstr << "' is now (or already was) '" << overlaypoolstr << "'";

set-overlay主要工作就是设置storagepool的read_tier和write_tier为cachepool_id。

命令4:ceph osd tier remove-overlay/rm-overlay {storagepool}

np->clear_read_tier();
 np->clear_write_tier();
 np->set_last_force_op_resend(pending_inc.epoch);
 ss << "there is now (or already was) no overlay for '" << poolstr << "'";

remove-overlay把storagepool中的read_tier和write_tier置为-1。

命令5:ceph osd tier cache-mode {cachepool} {cache-mode}

cache_mode表明当前池的缓存模式。目前支持:writeback、readonly、proxy、readproxy、none。

pg_pool_t *np = pending_inc.get_new_pool(pool_id, p);
 np->cache_mode = mode;
 // set this both when moving to and from cache_mode NONE. this is to
 // capture legacy pools that were set up before this flag existed.
 np->flags |= pg_pool_t::*FLAG_INCOMPLETE_CLONES*;
 ss << "set cache-mode for pool '" << poolstr
  << "' to " << pg_pool_t::get_cache_mode_name(mode);

设置cachepool的cache_mode字段为指定模式。

命令6:ceph osd tier add-cache {storagepool} {cachepool} {cachesize}

pg_pool_t *np = pending_inc.get_new_pool(pool_id, p);
 pg_pool_t *ntp = pending_inc.get_new_pool(tierpool_id, tp);
 if (np->tiers.count(tierpool_id) || ntp->is_tier()) {
  wait_for_finished_proposal(op, new C_RetryMessage(this, op));
  return true;
 }
 np->tiers.insert(tierpool_id);
 np->read_tier = np->write_tier = tierpool_id;
 np->set_snap_epoch(pending_inc.epoch); // tier will update to our snap info
 np->set_last_force_op_resend(pending_inc.epoch);
 ntp->set_last_force_op_resend(pending_inc.epoch);
 ntp->tier_of = pool_id;
 ntp->cache_mode = mode;
 ntp->hit_set_count = g_conf().get_val<uint64_t>("osd_tier_default_cache_hit_set_count");
 ntp->hit_set_period = g_conf().get_val<uint64_t>("osd_tier_default_cache_hit_set_period");
 ntp->min_read_recency_for_promote = g_conf().get_val<uint64_t>(
   "osd_tier_default_cache_min_read_recency_for_promote");
 ntp->min_write_recency_for_promote = g_conf().get_val<uint64_t>(
   "osd_tier_default_cache_min_write_recency_for_promote");
 ntp->hit_set_grade_decay_rate = g_conf().get_val<uint64_t>("osd_tier_default_cache_hit_set_grade_decay_rate");
 ntp->hit_set_search_last_n = g_conf().get_val<uint64_t>("osd_tier_default_cache_hit_set_search_last_n");
 ntp->hit_set_params = hsp;
 ntp->target_max_bytes = size;
 ss << "pool '" << tierpoolstr << "' is now (or already was) a cache tier of '" << poolstr << "'";

addcache集合了Cache Tier中多个命令,根据默认配置设置了Cache Tier所有参数。命令中必须要指明:storagepool、cachepool和size。size表示存储池最大大小,并不一定和底层block设备大小一致。

以下通过表格的方式展示addcache命令设置的所有参数。

参数名
storagepool
tiers{cachepool_id}
read_tier{cachepool_id}
write_tier{cachepool_id}
cachepool
tier_of{storagepool_id}
cache_modemode 默认writeback
hit_set_countosd_tier_default_cache_hit_set_count 默认 4
hit_set_periodosd_tier_default_cache_hit_set_period 默认1200
min_read_recency_for_promoteosd_tier_default_cache_min_read_recency_for_promote 默认1
min_write_recency_for_promoteosd_tier_default_cache_min_write_recency_for_promote 默认1
hit_set_grade_decay_rateosd_tier_default_cache_hit_set_grade_decay_rate 默认20
hit_set_search_last_nosd_tier_default_cache_hit_set_search_last_n 默认1
hit_set_paramshit_set 可在osd_tier_default_cache_hit_set_type配置参数中设置类型,有bloom、explicit_hash、explicit_object三种,默认为bloom。
target_max_bytes{cachesize}

命令7:ceph osd pool set {cachepool} {key} {value}

set 可以设置pool的属性,这里仅介绍cachepool(非数据池)相关属性。

此命令源码比较简单,即把pool响应的参数改写,这里不贴出源码。

以下给出所有可以通过此命令设置的cachepool相关参数。

参数名含义
hit_set_typehitset类型
hit_set_period每过该段时间,系统要重新产生一个新的hitset对象来记录独享的缓存统计信息
hit_set_count记录系统保存最近的多少个hitset记录
hit_set_fpp只在hitset类型为bloom时有效,设置其假阳性概率 bloom过滤器仅当判断结果为不在数据集中为100%正确,不能完全肯定一个数据是否在数据集中,因此需要设置fpp参数来手动调整判断存在的概率。
target_max_objectscachepool存储的最大对象数量
target_max_bytescachepool存储的最大字节数
cache_target_full_ratio数据满比例,当数据达到这个比例时,就认为cachepool已经满了
cache_target_dirty_ratio目标脏数据率,当脏数据率达到这个值时,后台cache agent开始flush数据。 脏数据:未写入数据池的数据 flush:把数据从缓存池转移到数据池
cache_target_dirty_high_ratio高速脏数据比例,当达到此比例时,后台cache agent 高速flush数据
use_gmt_hitsethitset archive对象命名规则
cache_min_flush_age缓存池脏数据被flush的最小时间,单位秒
cache_min_evict_age缓存池干净数据被evict的最小时间,单位秒 干净数据:数据已经写入数据池 evict:把数据从缓存池中删除
hit_set_grade_decay_ratehitset每轮衰减比率。 例如衰减率为20,一个hitset刚创建时grade为1M,等到再创建一个hitset时,之前创建的hitset的grade衰减为1M*(1-(rate)/100)=0.8M,上上个hitset则为0.8M*(1-(rate)/100)=0.64M,依次类推
hit_set_search_last_naccumulate atmost N hit_sets for temperature
min_read_recency_for_promote读操作需要promote时,检查最近的几个hitset,0表示直接通过检查,n表示检查最近的n个hitset。如果在hitset中命中检查的对象,则对对象进行提升操作。
min_write_recency_for_promote写操作需要promote时,检查最近的几个hitset,0表示直接通过检查,n表示检查最近的n个hitset。如果在hitset中命中检查的对象,则对对象进行提升操作。

三、Cache Tier源码解析

3.1 Cache Tier 的初始化

Cache Tier的初始化有两个时机:

  • on_activate_complete:每个pool在所有PG处于activate状态后会尝试建立cache tier。

  • plgp_on_pool_change:当Monitor通知osdmap相关信息发生变化时,会尝试建立cache tier。

cache tier的初始化包含两部分:hitset初始化和agent初始化。hitset用来记录对象在cachepool中的命中情况,agent用来处理对象的load、flush、evict等操作。

3.1.1 hitset

hitset用来跟踪和统计对象的访问行为。可以把hitset想象成一个set,里面保存了对象的唯一标识记录,set支持插入、查找等功能。cachepool用它来记录池中保存了哪些对象,这样在接到client的读写请求时,可以快速判断对象是否在缓存池中,便于进行后续操作。

目前hitset有三种实现方式:

  • ExplicitObjectHitSet:set保存了hobject整个数据结构,查询正确率100%,但速度慢,占用内存大。
  • ExplicitHashHitSet:set保存了对象32位的哈希值。查询正确率100%,占用空间小,速度较慢。
  • BloomHitSet:默认模式。不再使用set,而是使用Bloom过滤器,速度最快,占用空间最小。只能保证缓存中不存在的判定结果是肯定正确的。

在这里插入图片描述

hitset初始化步骤如上图流程图。

3.1.2 agent

在这里插入图片描述

agent的建立比较简单,一是创建了 agent_state,其中保存了pg执行flush和evict的相关参数,例如:hitset、flush_mode、num_objects、target_max_bytes、cache_target_dirty_ratio_micro等等;二是把pg加入到agent_queue。

agent_queue是agent线程的工作队列,其中保存了OSD中所有归属于cachepool的PG。agent线程是在OSDService中创建的专门用来处理cache tier数据迁移的线程,线程名叫:osd_srv_agent。其作用就是循环遍历agent_queue中的所有pg,并对他们执行agent_work()操作。

在这里插入图片描述

a gent_work()就是遍历PG中所有对象,去寻找已经过期的、失效的需要flush或者evict的对象并对它们执行相应操作。注意:osd_srv_agent线程是一个OSD上所有PG公用的,为了保证效率,设置了严格的限流参数:osd_pool_default_cache_max_evict_check_size限制依次遍历对象的总数,达到后立刻切换退出循环在osd_srv_agent中切换PG;osd_agent_max_ops设置了一个循环中最多能够处理几次flush或者evict操作。

agent_maybe_evict()处理了需要evict的对象。这里再次介绍下evict是针对cachepool中已经过期或过冷的干净数据,只需要把它从cachepool中删除即可。此函数的处理逻辑如下:

  1. 如果对象处于以下情况之一,则不能进行evict操作,直接退出。多数判断在agent_work()中也做过。

    • 脏数据

    • 处于scrubbing状态,或者说等待scrub

    • 处于被监视状态

    • block状态,正在对此对象进行读写操作

    • cache_pinned,被定在cachepool中

    • snap,镜像数据

  2. 当evict_mode为EVICT_MODE_SOME,需要对对象进行判断后决定是否执行evict。判断规则如下:

    • 对象存活时间如果小于cache_min_evict_age,则不做evict。
    • 使用hitset对对象进行打分,计算的分数需要满足(1000000 - temp_upper < evict_effort)(temp_upper为对象热度值,evict_effort为PG在agent队列里的优先级分数),否则不做evict。
  3. 如果evict_mode为EVICT_MODE_FULL,则直接驱逐对象,不做打分。

agent_maybe_flush()中把脏数据刷新到storagepool,脏数据是只保存在cachepool中,还未写入storagepool的数据。此函数处理逻辑如下:

  1. 如果对象处于以下情况之一,则不做flush操作。

    • 不是脏数据。
    • cache_pinned,被定在cachepool中。
    • 对象正在被flushing,只是脏数据状态未更新。
  2. 如果evict_mode不是EVICT_MODE_FULL,对象snap状态为CEPH_NOSNAP,并且对象存活时间小于cache_min_flush_age,则不做flush操作。

  3. 调用start_flush()执行后续操作。

start_flush()具体流程如下:

  1. 获取snapset。在O版之前(不包括O版),需要对snapset做一次get_filtered(),目的是把已经删除的快照过滤掉。O版之后则不需要做过滤操作。
  2. 判断obc->obs.oi.manifest.is_chunked(),如果为true,返回EOPNOTSUPP。
  3. 检查比当前clone对象版本更早的克隆对象:如果该克隆对象为missing状态,返回ENOENT;如果该克隆对象存在,且为脏对象,返回EBUSY。
  4. 设置对象blocked状态为true。
  5. 如果flush_ops队列中已经存在当前对象,则执行try_flush_mark_clean()

3.2 读写流程

接下来介绍Cache Tier是如何在读写过程中起作用的。

3.2.1 client的OSD选择

在这里插入图片描述

上图是client写操作的流程图,其中_calc_target()完成了关键的地址寻址工作:通过Crushmap计算出一组OSD,并根据一定规则找出消息的即将发往的主OSD。

这里顺便介绍下_calc_target()。

_calc_target() 是源码中计算主 osd 目标方法,大致流程如下:

  1. 根据 poolid 获取 pool 信息,包括 type、crush id、pg 数量等。
  2. 判断是否强制重发,标志位:force_resend
  3. 判断是否有缓存池,若有,则更新 pool 信息为缓存池信息,若没有,则不操作。
  4. 根据发送对象的信息(name,key,namespce)和 poolid,来计算 pg 信息,得到关键参数 m_seed。
  5. 根据 pg 信息,使用 crush 算法计算对象发送到哪组 osd 以及主 osd。
  6. 根据本次发送的目标是否和上一次发送的一致(对象和 pool 都一样才为一致)以及 any_change 参数,重置 force_resend。
  7. 读取 osd 状态(CEPH_OSDMAP_PAUSERD、CEPH_OSDMAP_PAUSEWR),判断是否暂停发送。
  8. 判断是否合法变更,标志位:legacy_change。
  9. 根据 pool 的 pg 数量是否变化,若变化,则 split_or_merge 标志位置真。
  10. 更新 op_target_t 参数,包括发送主 osd 以及 osd 组,成功返回 RECALC_OP_TARGET_NEED_RESEND。

上述过程中,第3步设置了target_oloc.pool。根据basepool是否有read_tier或者write_tier,把target_oloc.pool设置为对应读或者写操作的pool。这样后续计算出的OSD也是在cachepool中的OSD。

//apply tiering

t->target_oid = t->base_oid;
 t->target_oloc = t->base_oloc;
 if ((t->flags & *CEPH_OSD_FLAG_IGNORE_OVERLAY*) == 0) {
  if (is_read && pi->has_read_tier())
   t->target_oloc.pool = pi->read_tier;
  if (is_write && pi->has_write_tier())
   t->target_oloc.pool = pi->write_tier;
  pi = osdmap->get_pg_pool(t->target_oloc.pool);
  if (!pi) {
   t->osd = -1;
   return *RECALC_OP_TARGET_POOL_DNE*;
  }
 }

总结:

client的Cache Tier的应用主要体现在计算OSD的过程中,通过判断basepool的参数,来决定是否要更新targetpool:读操作时,如果有read_tier,则更新为read_tier pool;写操作时,如果有write_tier,则更新为write_tier pool。read_tier和write_tier与pool是否开启Cache Tier有关,第二节有过相关介绍。

3.2.2 server的处理流程

server就是上一节calc_target()中计算出的OSD所在主机。

在这里插入图片描述

上图大致描述了OSD处理请求的调用流程,Cache Tier在do_op()函数中体现。

do_request()步骤如下:

  1. 在waiting_for_map中尝试查找当前op的source,如果找到,说明waiting_for_map中有来自相同客户端的旧的op,则把当前op加入wait_for_map中的list队列,并直接返回。如果未找到,执行下一步操作。
  2. 判断op的min_epoch是否大于当前PG保存的OSDmap的epoch,如果大于,说明op的Epoch更新,则把当前op加入waiting_for_map队列,并直接返回,否则,进行下一步操作。
  3. 判断是否可以丢弃(can_discard_request)。如果是,直接返回,否则进行下一步操作。
  4. 执行pg_wide backoffs相关操作。
  5. 检查PG是否处于Peer状态,如果不处于,则判断是否可以由PGBackend处理,如果可以,则交由对应的PGBackend处理(handle_message()),如果不可以,则加入waiting_for_peered队列。如果PG处于Peer状态,则进行下一步操作。
  6. 判断PG是否有正在执行的flush操作,如果由则把当前op加入waiting_for_flush队列,并直接返回。否则进行下一步操作。
  7. 根据op的msg_type执行对应的处理函数。如类型为CEPH_MSG_OSD_OP,则调用do_op()处理op。

在这里插入图片描述

如果开启了Cache Tier,将会在do_op中执行以下操作:

  1. 首先判断hit_set中是否包含待操作的对象(hit_set->contains(obc->obs.oi.soid)),如果不包含,则把对象添加到hit_set中。添加对象后,如果hit_set满了,或者hit_set超时,则调用hit_set_persist()。
  2. 执行agent_choose_mode(),设置agent相关参数,如flush_mode、num_objects、num_bytes等。
  3. 执行maybe_handle_cache()。这里处理cache执行逻辑。
  4. 如果maybe_handle_cache()中调用maybe_handle_cache_detail(),如果成功处理了op请求,则直接return,否则会继续执行后续操作(说明不需要从datapool读取数据或者转发请求到datapool,可以直接在此osd命中查询的对象),由本OSD执行读取操作。
3.2.3 agent_choose_mode()

此函数中计算了一个PG的flush和evict行为的相关参数。

  • dirty_micro与full_micro,分别是脏数据的比率和数据满的比率。

    1. 如果设置了target_max_bytes,就按照字节数计算:

      dirty_micro = 脏对象数目 × 每个对象的平均大小 / 每个PG的平均字节数 × 100000

      full_micro = 用户对象数目 × 每个对象平均大小 / 每个PG的平均字节数 × 1000000

    2. 如果设置了target_max_objects,则按照对象数量计算:

      dirty_micro = 脏对象数目 / 每个PG的平均对象数量 × 100000

      full_micro = 用户对象数目 / 每个PG的平均对象数量 × 1000000

    3. 如果同时设置了以上两个参数,则dirty_micro和full_micro为1)、2)中的最大值。

  • flush_target、flush_high_target、flush_slop,分别是执行flush的脏数据率,高速flush的脏数据率和修正参数

    1. 先获取初始值:

      flush_target = cache_target_dirty_ratio_micro

      flush_high_target = cache_target_dirty_high_ratio_micro

      flush_slop = osd_agent_slop

    2. 执行修正操作:

      a. 如果restart或者flush_mode == FLUSH_MODE_IDLE

      flush_target = flush_target + flush_slop

      flush_high_target = flush_high_target + flush_slop

      b. !restart 并且 flush_mode != FLUSH_MODE_IDLE

      flush_target = flush_target – min(flush_target, flush_slop)

      flush_high_target = flush_high_target – min(flush_high_target, flush_slop)

  • 重新设置flush_mode

    1. 如果dirty_micro > flush_high_target,flush_mode设为FLUSH_MODE_HIGH。
    2. 如果dirty_micro < flush_high_target,并且dirty_micro > flush_target,flush_mode设为FLUSH_MODE_LOW。
  • evict_target、evict_slop,分别是evict操作的满数据率和evict修正参数

  1. 获取初始值

    evict_target = cache_target_full_ratio_micro

    evict_slop = osd_agent_slop

  2. 执行修正操作

    a. 如果(restart || evict_mode == EVICT_MODE_IDLE)

    evict_target = evict_target + evict_slop

    b. 否则

    evict_target = evict_target – min(evict_target, evict_slop)

  • 重新设置evict_mode,计算evict_effort(PG在agent工作队列里的优先级)

    1. 如果full_micro > 1000000

      evict_mode = EVICT_MODE_FULL

      evict_effort = 1000000

    2. 如果full_micro > evict_target

      evict_mode = EVICT_MODE_SOME

      over = full_micro – evict_target

      span = 1000000 – evict_target

      evict_effort = max((over * 1000000 / span), (osd_agent_min_evict_effort * 1000000))

      inc = cct->_conf->osd_agent_quantize_effort * 1000000

      was = evict_effort

      evict_effort = evict_effort - evict_effort % inc

      evict_effort = max(evict_effort, inc)

3.2.4 maybe_handle_cache_detail

maybe_handle_cache_detail()首先对传进来的op做了一系列的判断,如果可以直接由当前OSD处理的(缓存命中),则直接返回NOOP;如果当前OSD不是主OSD,则由osd报告给client一个错误,要求重发消息给正确的主osd;如果以上两种情况都不满足,则需要处理cache的细节,如请求转发,提升对象等,具体操作由cache_mode决定。

在这里插入图片描述

先介绍几个后续用到的处理请求的方式:

  1. do_proxy_read():cachepool向datapool请求数据,并把获得的数据返回给cli ent。注意:cachepool不会保存请求的数据。

在这里插入图片描述

  1. do_proxy_write():cachepool把写请求转发给datapool。注意:cachepool中没有写入该对象。

在这里插入图片描述

  1. block_write_on_full_cache():因为cachepool已满,所以把请求加入请求等待队列,把要改写的对象加入对象阻塞队列,并标记op为delayed延迟操作。
void PrimaryLogPG::block_write_on_full_cache(
   const hobject_t &_oid, OpRequestRef op) {
  objects_blocked_on_cache_full.insert(oid);
  waiting_for_cache_not_full.push_back(op);
  op->mark_delayed("waiting for cache not full");
 }

在PG运行过程中,每次涉及到evict_mode变化的时候,都会尝试把waiting_for_cache_not_full中被阻塞的请求重新添加到OSD的op_shardedwq队列中,再由OSD重发给对应的pg实例去执行op操作。

  1. promote_object():从datapool中读取指定对象并写入cachepool中。

在这里插入图片描述

  1. do_cache_direct():把client请求转发给datapool,cachepool不接收返回的消息。
void PrimaryLogPG::do_cache_redirect(OpRequestRef op) {
  auto m = op->get_req<MOSDOp>();
  int flags = m->get_flags() & (*CEPH_OSD_FLAG_ACK* | *CEPH_OSD_FLAG_ONDISK*);
  MOSDOpReply *reply = new MOSDOpReply(m, -ENOENT, get_osdmap_epoch(),flags, false);
  request_redirect_t redir(m->get_object_locator(), pool.info.tier_of);
  reply->set_redirect(redir);
  dout(10) << "sending redirect to pool " << pool.info.tier_of << " for op "
      << op << dendl;
  m->get_connection()->send_message(reply);
  return;
 }
  • WRITEBACK

    1. evict_mode == EVICT_MODE_FULL,此参数在agent_choose_mode()中设定,说明cache pool需要全速evict对象,池使用量接近满了。

      读操作:do_proxy_read()

      写操作:block_write_on_full_cache()

    2. 写操作

      首先do_proxy_write(),然后maybe_promote()。

      maybe_promote()中对对象做了一个判断:是否把该对象从datapool中提取到cachepool?如果是,则执行promote_object();如果否,则return false。

      判断的规则:1. 检查recency标志位,其数值表示promote操作需要检查多少个hit_set,0表示直接判定提升无需检查,n表示检查当前的最近的n个hit_set。2. 如果在hit_set中命中了改对象,则执行提升,否则return false。3. 做最后的限流检查(osd->promote_throttle()),通过后执行promote_object(),并return true。

    3. 读操作

      首先do_proxy_read(),然后maybe_promote()。

  • READONLY

    读操作:promote_object()

    写操作:do_cache_redirect()

  • PROXY

    1. must_promote == false

      写操作:do_proxy_write()

      读操作:do_proxy_read()

    2. evict_mode == EVICT_MODE_FULL

      block_write_on_full_cache(),阻塞op,等待后续cachepool空间释放。

    3. 以上两种情况都不符合

    4. promote_object(),直接提升对象,在cachepool中读写操作,后续由angent保证datapool数据一致。

  • READPROXY

    1. 写操作或者must_promote == true

      如果evict_mode == EVICT_MODE_FULL,则block_write_on_full_cache()。

      否则直接提升,promote_object()。

(),然后maybe_promote()。

  • READONLY

    读操作:promote_object()

    写操作:do_cache_redirect()

  • PROXY

    1. must_promote == false

      写操作:do_proxy_write()

      读操作:do_proxy_read()

    2. evict_mode == EVICT_MODE_FULL

      block_write_on_full_cache(),阻塞op,等待后续cachepool空间释放。

    3. 以上两种情况都不符合

    4. promote_object(),直接提升对象,在cachepool中读写操作,后续由angent保证datapool数据一致。

  • READPROXY

    1. 写操作或者must_promote == true

      如果evict_mode == EVICT_MODE_FULL,则block_write_on_full_cache()。

      否则直接提升,promote_object()。

    2. 读操作:do_proxy_read()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值