尊重原创,引用注明出处。
杨红刚 <rtlinux@163.com>
//
rgw gc
对象删除操作、对象覆盖操作都会产生需要被删除的对象。这些对象由垃圾回收线程进行周期性、批量删除。
1. 配置项
配置项 | 描述 | 默认 |
---|---|---|
rgw gc max objs | gc hint obj 个数 | 32 |
rgw gc obj min wait | 对象可被删除并由垃圾回收器处理前最少等待多长时间 | 7200s |
rgw gc processor max time | 锁定gc hint obj的超时时间 | 3600 |
rgw gc processor period | 垃圾回收进程的运行周期 | 3600 |
运行时查看gc相关配置:
[root@yhg-2 cmds]# ceph daemon /var/run/ceph/yhg-client.radosgw.yhg-yhg-yhg-2.asok config show | grep _gc_
"rgw_enable_gc_threads": "true",
"rgw_gc_max_objs": "32",
"rgw_gc_obj_min_wait": "7200",
"rgw_gc_processor_max_time": "3600",
"rgw_gc_processor_period": "3600",
2. 数据结构
18 class RGWGC {
// 用于访问ceph集群
19 CephContext *cct;
20 RGWRados *store;
// rgw gc max objs
21 int max_objs;
22 string *obj_names; // 用于gc控制的各个对象的名字数组
23 atomic_t down_flag; // 指示gc worker退出
24
25 int tag_index(const string& tag); // 形如“yhg-yhg.5457.98”
26
27 class GCWorker : public Thread {
28 CephContext *cct;
29 RGWGC *gc;
30 Mutex lock;
31 Cond cond;
32
33 public:
34 GCWorker(CephContext *_cct, RGWGC *_gc) : cct(_cct), gc(_gc), lock("GCWorker") {}
35 void *entry();
36 void stop();
37 };
38
39 GCWorker *worker; // 控制gc的工作线程
查看系统中的待gc的任务:
[root@yhg-2 cmds]# radosgw-admin gc list --cluster yhg --include-all
[
{
"tag": "yhg-yhg.5457.98",
"time": "2016-04-13 10:25:22.086624",
"objs": [
{
"pool": "dpool1",
"oid": "yhg-yhg.5457.9__shadow_.f06aUKl22Jmey6-vPeZijxeIhFNe4MA_1",
"key": "",
"instance": ""
},
{
"pool": "dpool1",
"oid": "yhg-yhg.5457.9__shadow_.f06aUKl22Jmey6-vPeZijxeIhFNe4MA_2",
"key": "",
"instance": ""
},
{
"pool": "dpool1",
"oid": "yhg-yhg.5457.9__shadow_.f06aUKl22Jmey6-vPeZijxeIhFNe4MA_3",
"key": "",
"instance": ""
}
]
}
]
Note
gc objs有两类索引,一类是 tag 索引,另一类是time索引。
查看用于gc的pool和对象:
// L版后需要加上 Namespace(-N),具体值,查看 zone 信息中对应pool中 ':' 之后的内容
// [root@hehe home]# for ((i=0;i<32;i++)); do echo ">>>"$i;rados -p .rgw.gc listomapkeys gc.$i --cluster xtao -N gc; done
[root@yhg-2 ~]# for ((i=0;i<32;i++)); do echo ">>>"$i;rados -p .rgw.gc listomapkeys gc.$i --cluster yhg; done
...
>>>16
0_yhg-yhg.5457.98 <<--------<<<<< tag信息
1_01460543122.086624419
>>>17
...
>>>31
[root@yhg-2 ~]# rados -p .rgw.gc listxattr gc.16 --cluster yhg
lock.gc_process
// 需要gc调的对象的名字以及所在的pool的信息
[root@yhg-2 ~]# rados -p .rgw.gc listomapvals gc.16 --cluster yhg
0_yhg-yhg.5457.98
value: (499 bytes) :
0000 : 01 01 ed 01 00 00 0f 00 00 00 79 68 67 2d 79 68 : ..........yhg-yh
0010 : 67 2e 35 34 35 37 2e 39 38 01 01 cc 01 00 00 03 : g.5457.98.......
0020 : 00 00 00 02 01 92 00 00 00 06 00 00 00 64 70 6f : .............dpo
0030 : 6f 6c 31 39 00 00 00 79 68 67 2d 79 68 67 2e 35 : ol19...yhg-yhg.5
0040 : 34 35 37 2e 39 5f 5f 73 68 61 64 6f 77 5f 2e 66 : 457.9__shadow_.f
0050 : 30 36 61 55 4b 6c 32 32 4a 6d 65 79 36 2d 76 50 : 06aUKl22Jmey6-vP
0060 : 65 5a 69 6a 78 65 49 68 46 4e 65 34 4d 41 5f 31 : eZijxeIhFNe4MA_1
0070 : 00 00 00 00 01 01 41 00 00 00 39 00 00 00 79 68 : ......A...9...yh
0080 : 67 2d 79 68 67 2e 35 34 35 37 2e 39 5f 5f 73 68 : g-yhg.5457.9__sh
0090 : 61 64 6f 77 5f 2e 66 30 36 61 55 4b 6c 32 32 4a : adow_.f06aUKl22J
00a0 : 6d 65 79 36 2d 76 50 65 5a 69 6a 78 65 49 68 46 : mey6-vPeZijxeIhF
00b0 : 4e 65 34 4d 41 5f 31 00 00 00 00 02 01 92 00 00 : Ne4MA_1.........
00c0 : 00 06 00 00 00 64 70 6f 6f 6c 31 39 00 00 00 79 : .....dpool19...y
00d0 : 68 67 2d 79 68 67 2e 35 34 35 37 2e 39 5f 5f 73 : hg-yhg.5457.9__s
00e0 : 68 61 64 6f 77 5f 2e 66 30 36 61 55 4b 6c 32 32 : hadow_.f06aUKl22
00f0 : 4a 6d 65 79 36 2d 76 50 65 5a 69 6a 78 65 49 68 : Jmey6-vPeZijxeIh
0100 : 46 4e 65 34 4d 41 5f 32 00 00 00 00 01 01 41 00 : FNe4MA_2......A.
0110 : 00 00 39 00 00 00 79 68 67 2d 79 68 67 2e 35 34 : ..9...yhg-yhg.54
0120 : 35 37 2e 39 5f 5f 73 68 61 64 6f 77 5f 2e 66 30 : 57.9__shadow_.f0
0130 : 36 61 55 4b 6c 32 32 4a 6d 65 79 36 2d 76 50 65 : 6aUKl22Jmey6-vPe
0140 : 5a 69 6a 78 65 49 68 46 4e 65 34 4d 41 5f 32 00 : ZijxeIhFNe4MA_2.
0150 : 00 00 00 02 01 92 00 00 00 06 00 00 00 64 70 6f : .............dpo
0160 : 6f 6c 31 39 00 00 00 79 68 67 2d 79 68 67 2e 35 : ol19...yhg-yhg.5
0170 : 34 35 37 2e 39 5f 5f 73 68 61 64 6f 77 5f 2e 66 : 457.9__shadow_.f
0180 : 30 36 61 55 4b 6c 32 32 4a 6d 65 79 36 2d 76 50 : 06aUKl22Jmey6-vP
0190 : 65 5a 69 6a 78 65 49 68 46 4e 65 34 4d 41 5f 33 : eZijxeIhFNe4MA_3
01a0 : 00 00 00 00 01 01 41 00 00 00 39 00 00 00 79 68 : ......A...9...yh
01b0 : 67 2d 79 68 67 2e 35 34 35 37 2e 39 5f 5f 73 68 : g-yhg.5457.9__sh
01c0 : 61 64 6f 77 5f 2e 66 30 36 61 55 4b 6c 32 32 4a : adow_.f06aUKl22J
01d0 : 6d 65 79 36 2d 76 50 65 5a 69 6a 78 65 49 68 46 : mey6-vPeZijxeIhF
01e0 : 4e 65 34 4d 41 5f 33 00 00 00 00 92 1e 0e 57 a3 : Ne4MA_3.......W.
01f0 : c8 29 05 : .).
1_01460543122.086624419
value: (499 bytes) :
0000 : 01 01 ed 01 00 00 0f 00 00 00 79 68 67 2d 79 68 : ..........yhg-yh
0010 : 67 2e 35 34 35 37 2e 39 38 01 01 cc 01 00 00 03 : g.5457.98.......
0020 : 00 00 00 02 01 92 00 00 00 06 00 00 00 64 70 6f : .............dpo
0030 : 6f 6c 31 39 00 00 00 79 68 67 2d 79 68 67 2e 35 : ol19...yhg-yhg.5
0040 : 34 35 37 2e 39 5f 5f 73 68 61 64 6f 77 5f 2e 66 : 457.9__shadow_.f
0050 : 30 36 61 55 4b 6c 32 32 4a 6d 65 79 36 2d 76 50 : 06aUKl22Jmey6-vP
0060 : 65 5a 69 6a 78 65 49 68 46 4e 65 34 4d 41 5f 31 : eZijxeIhFNe4MA_1
0070 : 00 00 00 00 01 01 41 00 00 00 39 00 00 00 79 68 : ......A...9...yh
0080 : 67 2d 79 68 67 2e 35 34 35 37 2e 39 5f 5f 73 68 : g-yhg.5457.9__sh
0090 : 61 64 6f 77 5f 2e 66 30 36 61 55 4b 6c 32 32 4a : adow_.f06aUKl22J
00a0 : 6d 65 79 36 2d 76 50 65 5a 69 6a 78 65 49 68 46 : mey6-vPeZijxeIhF
00b0 : 4e 65 34 4d 41 5f 31 00 00 00 00 02 01 92 00 00 : Ne4MA_1.........
00c0 : 00 06 00 00 00 64 70 6f 6f 6c 31 39 00 00 00 79 : .....dpool19...y
00d0 : 68 67 2d 79 68 67 2e 35 34 35 37 2e 39 5f 5f 73 : hg-yhg.5457.9__s
00e0 : 68 61 64 6f 77 5f 2e 66 30 36 61 55 4b 6c 32 32 : hadow_.f06aUKl22
00f0 : 4a 6d 65 79 36 2d 76 50 65 5a 69 6a 78 65 49 68 : Jmey6-vPeZijxeIh
0100 : 46 4e 65 34 4d 41 5f 32 00 00 00 00 01 01 41 00 : FNe4MA_2......A.
0110 : 00 00 39 00 00 00 79 68 67 2d 79 68 67 2e 35 34 : ..9...yhg-yhg.54
0120 : 35 37 2e 39 5f 5f 73 68 61 64 6f 77 5f 2e 66 30 : 57.9__shadow_.f0
0130 : 36 61 55 4b 6c 32 32 4a 6d 65 79 36 2d 76 50 65 : 6aUKl22Jmey6-vPe
0140 : 5a 69 6a 78 65 49 68 46 4e 65 34 4d 41 5f 32 00 : ZijxeIhFNe4MA_2.
0150 : 00 00 00 02 01 92 00 00 00 06 00 00 00 64 70 6f : .............dpo
0160 : 6f 6c 31 39 00 00 00 79 68 67 2d 79 68 67 2e 35 : ol19...yhg-yhg.5
0170 : 34 35 37 2e 39 5f 5f 73 68 61 64 6f 77 5f 2e 66 : 457.9__shadow_.f
0180 : 30 36 61 55 4b 6c 32 32 4a 6d 65 79 36 2d 76 50 : 06aUKl22Jmey6-vP
0190 : 65 5a 69 6a 78 65 49 68 46 4e 65 34 4d 41 5f 33 : eZijxeIhFNe4MA_3
01a0 : 00 00 00 00 01 01 41 00 00 00 39 00 00 00 79 68 : ......A...9...yh
01b0 : 67 2d 79 68 67 2e 35 34 35 37 2e 39 5f 5f 73 68 : g-yhg.5457.9__sh
01c0 : 61 64 6f 77 5f 2e 66 30 36 61 55 4b 6c 32 32 4a : adow_.f06aUKl22J
01d0 : 6d 65 79 36 2d 76 50 65 5a 69 6a 78 65 49 68 46 : mey6-vPeZijxeIhF
01e0 : 4e 65 34 4d 41 5f 33 00 00 00 00 92 1e 0e 57 a3 : Ne4MA_3.......W.
01f0 : c8 29 05 : .).
解析一个 gc_obj_info
[root@hehe home]# rados -p .rgw.gc --cluster xtao -N gc getomapval gc.17 1_01506495418.489421430 /tmp/gc Writing to /tmp/gc
[root@hehe home]# ceph-dencoder type cls_rgw_gc_obj_info import /tmp/gc decode dump_json
{
"tag": "9bea0c8d-2bc4-4698-822d-6d2155b55adc.44098.35\u0000",
"chain": {
"objs": [
{
"pool": "dpool",
"oid": "9bea0c8d-2bc4-4698-822d-6d2155b55adc.44098.4__multipart_bigfilehaha.2~qlAiA0f4PeAbDV1_ujf4JtZHImGnxSP.1",
"key": "",
"instance": ""
}
]
},
"time": "2017-09-27 02:56:58.0.489421s"
}
3. 过程分析
3.1 线程启动
在rgw 实例守护进程启动过程中,调用到如下函数中时
// rgw/rgw_rados.cc
1547 int RGWRados::init_complete()
...
1624 gc = new RGWGC(); // 创建gc对象
1625 gc->initialize(cct, this);
1626
1627 if (use_gc_thread)
1628 gc->start_processor(); // 启动gc worker线程
- RGWGC::initialize
// rgw/rgw_gc.cc
// 设置CephContext和RGWRados,用于和后端ceph 集群交互
24 void RGWGC::initialize(CephContext *_cct, RGWRados *_store) {
25 cct = _cct;
26 store = _store;
// 解析rgw gc max objs配置项
28 max_objs = cct->_conf->rgw_gc_max_objs;
29 if (max_objs > HASH_PRIME)
30 max_objs = HASH_PRIME;
// 初始化gc 对象数组
32 obj_names = new string[max_objs];
33
34 for (int i = 0; i < max_objs; i++) {
35 obj_names[i] = gc_oid_prefix;
36 char buf[32];
37 snprintf(buf, 32, ".%d", i);
38 obj_names[i].append(buf);
3.2 线程循环
// rgw/rgw_gc.cc
276 void *RGWGC::GCWorker::entry() {
277 do {
278 utime_t start = ceph_clock_now(cct);
279 dout(2) << "garbage collection: start" << dendl;
// 进行一次垃圾删除操作
280 int r = gc->process();
281 if (r < 0) {
282 dout(0) << "ERROR: garbage collection process() returned error r=" << r << dendl;
283 }
284 dout(2) << "garbage collection: stop" << dendl;
285
// 需要退出?
286 if (gc->going_down())
287 break;
288
289 utime_t end = ceph_clock_now(cct);
290 end -= start;
291 int secs = cct->_conf->rgw_gc_processor_period;
292
// 刚才的垃圾清理操作耗时超过了gc周期?
293 if (secs <= end.sec())
294 continue; // next round
295
296 secs -= end.sec();
297
298 lock.Lock();
// 在下一个gc周期到期前先睡会儿
299 cond.WaitInterval(cct, lock, utime_t(secs, 0));
300 lock.Unlock();
301 } while (!gc->going_down());
302
303 return NULL;
304 }
3.3 垃圾回收的过程
3.3.1 选择一个gc object
235 int RGWGC::process() 当中随机选择一个 gc object (index),然后调用
131 int RGWGC::process(int index, int max_secs)
随机选择gc object (index)的过程有点废,因为随机选中的gc object上不一定是记录有待删除对象信息。可能随机了一轮都没有选到含有待删除对象的index。
// rgw/rgw_gc.cc
235 int RGWGC::process()
236 {
237 int max_secs = cct->_conf->rgw_gc_processor_max_time;
238
239 unsigned start;
240 int ret = get_random_bytes((char *)&start, sizeof(start));
241 if (ret < 0)
242 return ret;
243
244 for (int i = 0; i < max_objs; i++) {
// 选择一个gc object/index,而这个对象上可能没有包含待删除对象列表信息
245 int index = (i + start) % max_objs;
// 该函数调用可能是空走一场,没有卵用
246 ret = process(index, max_secs);
247 if (ret < 0)
248 return ret;
249 }
3.3.2 处理选定的gc object上的待删除对象
// rgw/rgw_gc.cc
131 int RGWGC::process(int index, int max_secs)
- 锁定 gc object
// rgw/rgw_gc.cc
148 int ret = l.lock_exclusive(&store->gc_pool_ctx, obj_names[index]);
- 读取 gc object对象omap中的待删除对象信息列表
// rgw/rgw_gc.cc
160 int max = 100;
161 std::list<cls_rgw_gc_obj_info> entries;
// 调用了ceph 集群端的 rgw gc_list 服务函数
162 ret = cls_rgw_gc_list(store->gc_pool_ctx, obj_names[index], marker, max, true, entries, &truncated);
下面的entries 列表中值有一个项,该项包含一个包含了3个待删除对象的列表。
// 此处显示的entries和日志中的不同,是另一次跟踪的结果。
(gdb) print entries
$227 = std::list = {
[0] = {
tag = "yhg-yhg.5457.102",
chain = {
objs = std::list = {
[0] = {
pool = "dpool1",
key = {
name = "yhg-yhg.5457.9__shadow_.W1hIqrwLfgryxeyjYpuN1gXcOIlxji__1",
instance = ""
},
loc = ""
},
[1] = {
pool = "dpool1",
key = {
name = "yhg-yhg.5457.9__shadow_.W1hIqrwLfgryxeyjYpuN1gXcOIlxji__2",
instance = ""
},
loc = ""
},
[2] = {
pool = "dpool1",
key = {
name = "yhg-yhg.5457.9__shadow_.W1hIqrwLfgryxeyjYpuN1gXcOIlxji__3",
instance = ""
},
loc = ""
}
}
},
time = {
tv = {
tv_sec = 1460548119,
tv_nsec = 734893236
}
}
}
}
- 遍历entries列表的每一项,遍历每一项的对象列表的每一项,将其中的待删除对象删除
// rgw/rgw_gc.cc
189 ret = store->get_rados_handle()->ioctx_create(obj.pool.c_str(), *ctx);
...
202 dout(0) << "gc::process: removing " << obj.pool << ":" << key_obj.get_object() << dendl;
203 ObjectWriteOperation op;
// 见下面的 E1
204 cls_refcount_put(op, info.tag, true);
205 ret = ctx->operate(key_obj.get_object(), &op);
E1
cls_refcount_put 的对象的refs为空时,会将关联对象删除。
// cls/refcount/cls_refcount.cc
120 static int cls_rc_refcount_put(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
...
160 if (objr.refs.empty()) {
161 return cls_cxx_remove(hctx); // 删除对象
162 }
- 删除gc object(gc.XXX) omap上的tags (包含了待删除对象的列表)
删除 2. 数据结构 节中 listomapkeys 列举出来的信息。
// rgw/rgw_gc.cc
228 if (!remove_tags.empty())
229 RGWGC::remove(index, remove_tags);
3.3.3 gc日志
>> 2 garbage collection: start
>> 0 gc::process: removing dpool1:yhg-yhg.5457.9__shadow_.f06aUKl22Jmey6-vPeZijxeIhFNe4MA_1
>> 0 gc::process: removing dpool1:yhg-yhg.5457.9__shadow_.f06aUKl22Jmey6-vPeZijxeIhFNe4MA_2
>> 0 gc::process: removing dpool1:yhg-yhg.5457.9__shadow_.f06aUKl22Jmey6-vPeZijxeIhFNe4MA_3
>> 2 garbage collection: stop
3.3.4 .rgw.gc pool 中的对象的作用和内容
gc 对象pool中的对象gc.[0-31]有两个作用:一个是用于gc worker互斥控制,一个是在其omap中存放了待删除对象的名字和其对象的数据pool的名字。
4. 添加待删除对象到gc chain
gc chain (struct cls_rgw_obj_chain) 就是 3.3.2 处理选定的gc object上的待删除对象 中的chain。包含了多个待删除对象的名字和所在的pool信息等。
在下述的3中情况下,会添加待删除对象到gc chain。
4.1 RGWAbortMultipart 操作
Multipart上传被取消时,需要将已经上传的片段对象删除。
// rgw/rgw_op.cc
3140 void RGWAbortMultipart::execute()
4.2 RGWRados::Object::Write::write_meta
覆盖写(put一个已经存在的对象)时,旧的对象片段需要gc掉。
// rgw/rgw_rados.cc
3297 /**
3298 * Write/overwrite an object to the bucket storage.
3299 * bucket: the bucket to store the object in
3300 * obj: the object name/key
3301 * data: the object contents/value
3302 * size: the amount of data to write (data must be this long)
3303 * mtime: if non-NULL, writes the given mtime to the bucket storage
3304 * attrs: all the given attrs are written to bucket storage for the given object
3305 * exclusive: create object exclusively
3306 * Returns: 0 on success, -ERR# otherwise.
3307 */
3308 int RGWRados::Object::Write::write_meta(uint64_t size,
3309 map<string, bufferlist>& attrs)
// 在complete_atomic_modification中,调用了update_gc_chain/send_chain操作
3431 r = target->complete_atomic_modification();
4.3 RGWRados::Object::Delete::delete_obj
// rgw/rgw_rados.cc
4660 /**
4661 * Delete an object.
4662 * bucket: name of the bucket storing the object
4663 * obj: name of the object to delete
4664 * Returns: 0 on success, -ERR# otherwise.
4665 */
4666 int RGWRados::Object::Delete::delete_obj()
...
4773 if (removed) {
// 在complete_atomic_modification中,调用了update_gc_chain/send_chain操作
4774 int ret = target->complete_atomic_modification();
5. gc chain的工作原理
5.1 主要数据结构
// gc chain 结构
814 struct cls_rgw_obj_chain {
815 list<cls_rgw_obj> objs; // 该chain包含的对象信息
816
...
// 将<pool, key, loc> 标识的对象添加到chain中
819 void push_obj(string& pool, cls_rgw_obj_key& key, string& loc) {
820 cls_rgw_obj obj;
821 obj.pool = pool;
822 obj.key = key;
823 obj.loc = loc;
824 objs.push_back(obj);
825 }
// 表示chain中的一个对象
770 struct cls_rgw_obj {
771 string pool;
772 cls_rgw_obj_key key;
773 string loc;
// 表示一个对象
214 struct cls_rgw_obj_key {
215 string name; // 对象的名字
216 string instance;
5.2 操作
下述的3个操作会修改ceph集群端的 gc chain。而3.3.2 处理选定的gc object上的待删除对象 中会从gc chain上取对象列表并删除。
- RGWRados::update_gc_chain
将manifest中包含的所有对象添加到gc chain上。
// rgw/rgw_rados.cc
4439 void RGWRados::update_gc_chain(rgw_obj& head_obj, RGWObjManifest& manifest, cls_rgw_obj_chain *chain)
- RGWRados::send_chain_to_gc
调用ceph集群端 rgw相关的类的 gc_set_entry 方法,将gc chain传递到ceph集群端。本质上是更新了gc.XXX对象的omap信息。
// rgw/rgw_gc.cc
63 ObjectWriteOperation op;
// cls/rgw/cls_rgw_client.cc
580 op.exec("rgw", "gc_set_entry", in);
// rgw/rgw_gc.cc
68 if (sync)
69 return store->gc_operate(obj_names[i], &op);
70
71 return store->gc_aio_operate(obj_names[i], &op);
最终调用了如下函数:
// cls/rgw/cls_rgw.cc
2791 static int gc_update_entry(cls_method_context_t hctx, uint32_t expiration_secs,
2792 cls_rgw_gc_obj_info& info)
- RGWRados::Object::complete_atomic_modification
对 update_gc_chain/send_chain_to_gc 的封装。