Ceph EC pg backfill run
pg的backfill请求也是发送到osd的work queue中与业务IO一起竞争。
PGRecovery::run backfill
57 void PGRecovery::run( 58 OSD *osd, 59 OSDShard *sdata, 60 PGRef& pg, 61 ThreadPool::TPHandle &handle) 62 { 63 osd->do_recovery(pg.get(), epoch_queued, reserved_pushes, handle); 64 pg->unlock(); 65 }
OSD::do_recovery
- Call pg->start_recovery_ops
- 释放占用资源,并进行pg的下一轮恢复请求调度。
10018 void OSD::do_recovery(
10019 PG *pg, epoch_t queued, uint64_t reserved_pushes,
10020 ThreadPool::TPHandle &handle)
10021 {
...
10031 float recovery_sleep = get_osd_recovery_sleep();
...
10070 dout(10) << "do_recovery starting " << reserved_pushes << " " << *pg << dendl;
...
10075 bool do_unfound = pg->start_recovery_ops(reserved_pushes, handle, &started);
10076 dout(10) << "do_recovery started " << started << "/" << reserved_pushes
10077 << " on " << *pg << dendl;
...
10089 service.release_reserved_pushes(reserved_pushes);
10090 }
PrimaryLogPG::start_recovery_ops
- 入参的max即osd_recovery_max_single_start,决定了pg得到执行一次recovery op后能同时恢复多少个object。
- 执行recovery请求必须是primary pg
- 检查是否设置了nobackfill。
- 检查是否人为设置了norebalance。
- 检查是否完成资源预约。
- Call recover_backfill。
12452 bool PrimaryLogPG::start_recovery_ops(
12453 uint64_t max,
12454 ThreadPool::TPHandle &handle,
12455 uint64_t *ops_started)
12456 {
...
12461 ceph_assert(is_primary());
...
12465 ceph_assert(recovery_queued);
12466 recovery_queued = false; // recovery op已经得到,pg的下一轮恢复需要重新入队检查
...
12502 if (recovering.empty() &&
12503 state_test(PG_STATE_BACKFILLING) &&
12504 !backfill_targets.empty() && started < max &&
12505 missing.num_missing() == 0 &&
12506 waiting_on_backfill.empty()) {
12507 if (get_osdmap()->test_flag(CEPH_OSDMAP_NOBACKFILL)) {
12508 dout(10) << "deferring backfill due to NOBACKFILL" << dendl;
12509 deferred_backfill = true;
12510 } else if (get_osdmap()->test_flag(CEPH_OSDMAP_NOREBALANCE) &&
12511 !is_degraded()) {
12512 dout(10) << "deferring backfill due to NOREBALANCE" << dendl;
12513 deferred_backfill = true;
12514 } else if (!backfill_reserved) {
12515 dout(10) << "deferring backfill due to !backfill_reserved" << dendl;
12516 if (!backfill_reserving) {
12517 dout(10) << "queueing RequestBackfill" << dendl;
12518 backfill_reserving = true;
12519 queue_peering_event(
12520 PGPeeringEventRef(
12521 std::make_shared<PGPeeringEvent>(
12522 get_osdmap_epoch(),
12523 get_osdmap_epoch(),
12524 RequestBackfill())));
12525 }
12526 deferred_backfill = true;
12527 } else {
12528 started += recover_backfill(max - started, handle, &work_in_progress);
12530 }
12531
12532 dout(10) << " started " << started << dendl;
PrimaryLogPG::recover_backfill
- pg第一次执行backfill,需要把peer_backfill_info、backfill_info、backfills_in_flight重置。
- 更新primary的begin obj为last_backfill_started。
- 检查backfill info(记录恢复进度)是否要更新恢复对象的范围,一次最多scan 512个obj。
- 遍历backfill targets,更新所有peer_backfill_info的begin对象。
Backfill targets是要回填的目标pg shard,例如:
[1,28,120,278,90,17,210,322,184,186]/[1,121,192,278,106,17,60,322,31,53]为一个pg当前的up set和acting set。由于扩容导致up set与acting set不一致,需要backfill,backfill target为:
[28(1),90(4),120(2),184(8),186(9),210(6)]
- 如果backfill_info为空,表示没有要backfill的obj。
- 获取所有backfill targets中排序最小的obj作为check obj。
有了check obj之后,backfill target的pg shard都要向check obj看齐:
- check obj < primary的begin obj,说明拥有该check obj的peer需要删除该check obj
- check obj >= primary的begin obj,定义了need_ver_targs, missing_targs, keep_ver_targs, skip_targs 4个容器。
- 如果peer begin obj的版本与primary begin obj的版本不一致,拥有该check obj的peer需要加入need_ver_targs进行修复。
- Peer begin obj ver == begin ver的peer不需要修复,加入keep_ver_targs。
- 缺少begin obj的peer加入missing_targs进行修。
- peer的last backfill >= begin obj,该peer可以跳过该obj的恢复,加入skip_targs。
- 到prep_backfill_object_push说明已经确定需要恢复obj的peer,生成RecoveryOp,并记录当前正在恢复的obj到backfills_in_flight容器。
- ops++之后仍然小于max(osd_recovery_max_single_start),则pg可以继续执行下一个obj的恢复,这里可以作为提速。
- pgbackend->run_recovery_op 将recoveryOp封装成message。
13049 uint64_t PrimaryLogPG::recover_backfill(
13050 uint64_t max,
13051 ThreadPool::TPHandle &handle, bool *work_started)
13052 {
...
13061 if (new_backfill) { // pg第一次执行backfill,需要把peer_backfill_info、backfill_info、backfills_in_flight重置
13062 // on_activate() was called prior to getting here
13063 ceph_assert(last_backfill_started == earliest_backfill());
13064 new_backfill = false;
13065
13066 // initialize BackfillIntervals
13067 for (set<pg_shard_t>::iterator i = backfill_targets.begin();
13068 i != backfill_targets.end();
13069 ++i) {
13070 peer_backfill_info[*i].reset(peer_info[*i].last_backfill);
13071 }
13072 backfill_info.reset(last_backfill_started);
13073
13074 backfills_in_flight.clear();
13075 pending_backfill_updates.clear();
13076 }
13077
...
13089 // update our local interval to cope with recent changes
13090 backfill_info.begin = last_backfill_started; // 更新begin对象为last_backfill_started
13091 update_range(&backfill_info, handle);
13092
13093 unsigned ops = 0;
13094 vector<boost::tuple<hobject_t, eversion_t, pg_shard_t> > to_remove; // 放置要删除的obj
13095 set<hobject_t> add_to_stat;
13096
13097 for (set<pg_shard_t>::iterator i = backfill_targets.begin(); // 遍历backfill targets,更新peer_backfill_info的begin对象
13098 i != backfill_targets.end();
13099 ++i) {
13100 peer_backfill_info[*i].trim_to(
13101 std::max(peer_info[*i].last_backfill, last_backfill_started));
13102 }
13103 backfill_info.trim_to(last_backfill_started);
13104
13105 PGBackend::RecoveryHandle *h = pgbackend->open_recovery_op();
13106 while (ops < max) {
...
13116 dout(20) << " my backfill interval " << backfill_info << dendl;
...
13148 if (backfill_info.empty() && all_peer_done()) {
13149 dout(10) << " reached end for both local and all peers" << dendl;
13150 break;
13151 }
13152
13153 // Get object within set of peers to operate on and
13154 // the set of targets for which that object applies.
13155 hobject_t check = earliest_peer_backfill();
13156
13157 if (check < backfill_info.begin) {
...
13189 } else {
13190 eversion_t& obj_v = backfill_info.objects.begin()->second;
13191
13192 vector<pg_shard_t> need_ver_targs, missing_targs, keep_ver_targs, skip_targs;
13193 for (set<pg_shard_t>::iterator i = backfill_targets.begin();
13194 i != backfill_targets.end();
13195 ++i) {
13196 pg_shard_t bt = *i;
13197 BackfillInterval& pbi = peer_backfill_info[bt];
13198 // Find all check peers that have the wrong version
13199 if (check == backfill_info.begin && check == pbi.begin) {
13200 if (pbi.objects.begin()->second != obj_v) {
13201 need_ver_targs.push_back(bt);
13202 } else {
13203 keep_ver_targs.push_back(bt);
13204 }
13205 } else {
13206 pg_info_t& pinfo = peer_info[bt];
13207
13208 // Only include peers that we've caught up to their backfill line
13209 // otherwise, they only appear to be missing this object
13210 // because their pbi.begin > backfill_info.begin.
13211 if (backfill_info.begin > pinfo.last_backfill)
13212 missing_targs.push_back(bt);
13213 else
13214 skip_targs.push_back(bt);
13215 }
13216 }
...
13225 if (!need_ver_targs.empty() || !missing_targs.empty()) {
13227 ceph_assert(obc);
13228 if (obc->get_recovery_read()) {
13229 if (!need_ver_targs.empty()) {
13230 dout(20) << " BACKFILL replacing " << check
13231 << " with ver " << obj_v
13232 << " to peers " << need_ver_targs << dendl;
13233 }
13234 if (!missing_targs.empty()) {
13235 dout(20) << " BACKFILL pushing " << backfill_info.begin
13236 << " with ver " << obj_v
13237 << " to peers " << missing_targs << dendl;
13238 }
13239 vector<pg_shard_t> all_push = need_ver_targs;
13240 all_push.insert(all_push.end(), missing_targs.begin(), missing_targs.end()); // 记录所有要推送的target shard
13241
13242 handle.reset_tp_timeout(); // 防止线程心跳超时
13243 int r = prep_backfill_object_push(backfill_info.begin, obj_v, obc, all_push, h);
...
13249 ops++;
...
13276 }
...
13315 pgbackend->run_recovery_op(h, get_recovery_op_priority());
ECBackend::run_recovery_op
- 遍历RecoveryOp
- Call continue_recovery_op生成RecoveryMessages
- dispatch_recovery_messages完成ECSubRead请求发送
715 void ECBackend::run_recovery_op( 716 RecoveryHandle *_h, 717 int priority) 718 { 719 ECRecoveryHandle *h = static_cast<ECRecoveryHandle*>(_h); 720 RecoveryMessages m; 721 for (list<RecoveryOp>::iterator i = h->ops.begin(); 722 i != h->ops.end(); 723 ++i) { 724 dout(10) << __func__ << ": starting " << *i << dendl; 725 ceph_assert(!recovery_ops.count(i->hoid)); 726 RecoveryOp &op = recovery_ops.insert(make_pair(i->hoid, *i)).first->second; 727 continue_recovery_op(op, &m); 728 } 729 730 dispatch_recovery_messages(m, priority); 731 send_recovery_deletes(priority, h->deletes); 732 delete _h; 733 }
ECBackend::continue_recovery_op
恢复过程表示为一个状态机:
- IDLE:IDLE为recovery op的初始状态。读取将被启动,状态转为READING
- READING:正在等待一次读取操作,一旦完成,转到WRITING状态。
- WRITING:等待push完成,一旦完成,转到COMPLETE状态。
- get_min_avail_to_read_shards找到存在缺失object的pg shard,因为需要在这些pg shard上读取object来push到新的pg shard上。
- 读取请求封装到RecoveryMessages
567 void ECBackend::continue_recovery_op( 568 RecoveryOp &op, 569 RecoveryMessages *m) 570 { 571 dout(10) << __func__ << ": continuing " << op << dendl; 572 while (1) { 573 switch (op.state) { 574 case RecoveryOp::IDLE: { 575 // start read 576 op.state = RecoveryOp::READING; 577 ceph_assert(!op.recovery_progress.data_complete); 578 set<int> want(op.missing_on_shards.begin(), op.missing_on_shards.end()); 579 uint64_t from = op.recovery_progress.data_recovered_to; 580 uint64_t amount = get_recovery_chunk_size(); 581 582 if (op.recovery_progress.first && op.obc) { 583 /* We've got the attrs and the hinfo, might as well use them */ 584 op.hinfo = get_hash_info(op.hoid); 585 ceph_assert(op.hinfo); 586 op.xattrs = op.obc->attr_cache; 587 encode(*(op.hinfo), op.xattrs[ECUtil::get_hinfo_key()]); 588 } 589 590 map<pg_shard_t, vector<pair<int, int>>> to_read; 591 int r = get_min_avail_to_read_shards( 592 op.hoid, want, true, false, &to_read); 593 if (r != 0) { 594 // we must have lost a recovery source 595 ceph_assert(!op.recovery_progress.first); 596 dout(10) << __func__ << ": canceling recovery op for obj " << op.hoid 597 << dendl; 598 get_parent()->cancel_pull(op.hoid); 599 recovery_ops.erase(op.hoid); 600 return; 601 } 602 m->read( 603 this, 604 op.hoid, 605 op.recovery_progress.data_recovered_to, 606 amount, 607 std::move(want), 608 to_read, 609 op.recovery_progress.first && !op.obc); 610 op.extent_requested = make_pair( 611 from, 612 amount); 613 dout(10) << __func__ << ": IDLE return " << op << dendl; 614 return; 615 }
后面就是等待读取请求回复,收到所有的回复之后,发起push。收到所有的pushreply之后说明该obj完成backfill。以此重复,完成pg所有需要backfill的obj。
相关文章:
Ceph EC pg backfill run
pg的backfill请求也是发送到osd的work queue中与业务IO一起竞争。 PGRecovery::run backfill 57 void PGRecovery::run( 58 OSD *osd, 59 OSDShard *sdata, 60 PGRef& pg, 61 ThreadPool::TPHandle &handle) 62 { 63 osd->do_recovery(pg.get(), epoch_queued…...

腾讯云服务器地域怎么选?广州上海北京?
腾讯云服务器地域有什么区别?怎么选择比较好?地域选择就近原则,距离地域越近网络延迟越低,速度越快。关于地域的选择还有很多因素,地域节点选择还要考虑到网络延迟速度方面、内网连接、是否需要备案、不同地域价格因素…...

Apple Configurator iphone ipad 设备管控 描述文件使用方法
一、准备 App Store 下载安装 Apple Configurator 二、Apple Configurator 注册组织, -----------这个组织可以是个人,或者其它组织导出-------再导入进来: 三、描述文件配置:“” 根据管控需求进行配置 “” 四、使用 Ap…...
Linux 管道(pipe)用法
在 Linux 中,管道(pipe)是一种特殊的机制,用于连接一个进程的标准输出到另一个进程的标准输入。通过使用管道,可以将一个命令的输出直接传递给另一个命令进行处理,实现了进程之间的通信和数据传输。 管道的…...

元素隐式具有 “any“ 类型,因为类型为 “string“ 的表达式不能用于索引类型
今天在写ts文件的过程中,我遍历了一个对象,然后取值的时候发现爆红,如下图👇 经过我一通排查(原因我对ts也不是很熟练),了解到大致意思是说key的值类型不是string类型,在javascript中是默认给你…...

34、springboot切换内嵌Web服务器(Tomcat服务器)与 生成SSL证书来把项目访路径从 HTTP 配置成 HTTPS
知识点1:springboot切换内嵌Web服务器(Tomcat服务器) 知识点2:生成SSL证书来把项目访路径从 HTTP 配置成 HTTPS ★ Spring Boot默认的Web服务器(Tomcat) ▲ 基于Servlet的应用(使用Spring MV…...
3种CSS实现背景图片全屏铺满自适应的方式
01 margin:0px; background: url(images/bg.png) no-repeat; background-size:100% 100%; background-attachment:fixed; url(images/beijing.png)——图片路径的位置; no-repeat—— 图片不重复; center 0px——center是距离页面左边的定位…...

M1 Pro 利用docker 搭建pytho2的开发环境,以vscode连接开发为例
使用 M1 Pro (不支持python2的安装)开发,需要使用 Python 2.7 的环境,在使用 pyenv 安装 Python 2 时遇到了各种奇怪的问题。最终,我决定使用 Docker 搭建开发环境,并使用 VS Code 连接到本地容器。以下是详…...

MySQL概述,架构原理
一.MySQL简介 MySQL是一个关系型数据库管理系统,由瑞典的MySQL AB公司开发,后被oracle公司收购,MySQL是当下最流行的关系型数据库管理系统之一,在WEB应用方面,MySQL是最好的RDBMS(Relational Database Man…...

Three.js实现模型,模型材质可拖拽效果 DragControls
Three.js提供了一个拖拽的API DragControls 用于实现模型材质拖拽效果 DragControls:是一个用于在Three.js中实现拖拽控制的辅助类。它简化了在Three.js中实现拖拽物体的过程。 DragControls的构造函数接受三个参数: objects:一个包含需要…...
机器学习笔记之优化算法(二十)牛顿法与正则化
机器学习笔记之优化算法——再回首:牛顿法与正则化 引言回顾:经典牛顿法及其弊端牛顿法:算法步骤迭代过程中可能出现的问题正则化 Hessian Matrix \text{Hessian Matrix} Hessian Matrix与相应问题 引言 本节我们介绍经典牛顿法在训练神经网络过程中的迭…...

【Go 基础篇】深入探索:Go语言中的切片遍历与注意事项
嗨,Go语言学习者!在我们的编程旅程中,切片(Slice)是一个极其重要的工具。它可以帮助我们处理各种类型的数据,从而让我们的代码更加灵活和高效。本文将围绕Go语言中切片的遍历方法以及在遍历时需要注意的事项…...
一些经典的SQL语句
查sql中as的用法搜索到的一些经典的sql语句 convert(2008-11-20 18:03:50) In:等值连接,用来查找多表相同字段的记录 Not In:非等值连接,用来查找不存在的记录 Inner join:内连接,主要用来查找都符合条件的记录 Left join:左连接ÿ…...

〔018〕Stable Diffusion 之 批量替换人脸 篇
✨ 目录 🎈 下载插件🎈 插件基础使用🎈 基础使用效果🎈 批量处理图片🎈 多人脸部替换 🎈 下载插件 如果重绘图片的时候,你只想更换人物面部的话,可以参考这篇文章扩展地址ÿ…...
Unity字符串性能问题
前言 分享一些通过书籍和网络学到的知识 每次动态创建一个string,C#都会在堆内存分配一个内存用来分配字符串,因为C#没有对字符串的缓存机制,会导致每次连接、切割、组合的时候都会申请新的内存,并且抛弃原来的内存,等…...

深入浅出SSD:固态存储核心技术、原理与实战(文末赠书)
名字:阿玥的小东东 学习:Python、C/C 主页链接:阿玥的小东东的博客_CSDN博客-python&&c高级知识,过年必备,C/C知识讲解领域博主 目录 内容简介 作者简介 使用Python做一个计算器 本期赠书 近年来国家大力支持半导体行业࿰…...
关于layui+php,三级联动-编辑回显的问题。
注 忍不住吐槽一波。都什么年代了。现在都前后端分离,但是公司老项目非得用tplayui。。 代码如下 layui.use([form], function () {var form layui.form;//php代码渲染页面的时候,将一级分类id和二级分类id带过来,存到页面input框中&#x…...

lua的函数
1.一个示例实现列表的元素的求和 [root]# more funcAdd.lua function add(a)local sum 0for i 1,#a dosum sum a[i]endreturn sum enda {1,2,3,4,5,6}local sum add(a)print(sum)...
pytorch/tensorflow 直接给张量中的某个位置的值赋值,操作不可导。
问题:给一个tensor A中[i,j],赋值p。直接操作A[i,j]p可能会导致值覆盖,操作不可导。 解决方案:通过引入一个额外的mask实现。 mask[i,j] 0 mask tf.convert_to_tensor(mask, dtypetf.float32) A (A * mask) (p * (1-mask))p…...

如何使用CSS实现一个平滑滚动到页面顶部的效果(回到顶部按钮)?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 平滑滚动到页面顶部的效果(回到顶部按钮)⭐ 创建HTML结构⭐ 编写CSS样式⭐ 编写JavaScript函数⭐ 添加滚动事件监听器⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...