继续分析Ceph 读写路径上的源代码,本文主要ObjectContex这个比较重要的数据结构。
数据结构
MOSDOp
OSDOp
struct OSDOp {
ceph_osd_op op;
//具体操作数据的封装
sobject_t soid;
//src oid, 并不是op操作的对象,而是源数据对象
//例如rados_clone_range 需要 dest obj 和 src obj
bufferlist indata, outdata;
//操作的输入输出的data
int32_t rval;
//操作返回值
OSDOp() : rval(0) {
memset(&op, 0, sizeof(ceph_osd_op));
}
class MOSDOp : public Message {
object_t oid;
//操作的对象
object_locator_t oloc;
pg_t pgid; //pg
public:
vector<OSDOp> ops;
//针对oid的多个操作集合
private:
//快照相关
snapid_t snapid;
//snapid,如果是CEPH_NOSNAP,就是head对象,否则就是等于snap_seq
snapid_t snap_seq;
//如果是head对象,就是最新的 快照序号
//如果是snap对象,就是snap对应的seq
vector<snapid_t> snaps;
//所有的snap列表
uint64_t features;
osd_reqid_t reqid; // reqid explicitly set by sender
}
MOSDOp封装了一些基本的请求。在ops里分装了多个OSDOp操作。每个OSDOp操作里又有一个soid, 这个soid和 MOSDOp的 oid 之间是什么关系?
在这里,MOSDOp 封装的操作都是关于oid相关的操作,也就是说,一个MOSDOp只封装针对同一个oid 的操作。但是对于rados_clone_range这样的操作,有一个dest oid, 还有一个src oid,那么src oid 就保存在OSDOp的soid中。
object_info_t
struct object_info_t {
hobject_t soid;
eversion_t version, prior_version;
version_t user_version;
osd_reqid_t last_reqid;
uint64_t size;
utime_t mtime;
utime_t local_mtime; // local mtime
// note: these are currently encoded into a total 16 bits; see
// encode()/decode() for the weirdness.
typedef enum {
FLAG_LOST = 1<<0,
FLAG_WHITEOUT = 1<<1, // object logically does not exist
FLAG_DIRTY = 1<<2,
// object has been modified since last flushed or undirtied
FLAG_OMAP = 1 << 3, // has (or may have) some/any omap data
FLAG_DATA_DIGEST = 1 << 4, // has data crc
FLAG_OMAP_DIGEST = 1 << 5, // has omap crc
FLAG_CACHE_PIN = 1 << 6, // pin the object in cache tier
// ...
FLAG_USES_TMAP = 1<<8, // deprecated; no longer used.
} flag_t;
flag_t flags;
......
vector<snapid_t> snaps; // [clone]
uint64_t truncate_seq, truncate_size;
map<pair<uint64_t, entity_name_t>, watch_info_t> watchers;
// opportunistic checksums; may or may not be present
__u32 data_digest; ///< data crc32c
__u32 omap_digest; ///< omap crc32c
}
object_info_t 做为 对象的属性,保存在xattr中,key为 OI_ATTR (”_”),value就是object_info_t 的decode后的值
ObjectState
struct ObjectState {
object_info_t oi;
bool exists;
//the stored object exists (i.e., we will remember the object_info_t)
ObjectState() : exists(false) {}
ObjectState(const object_info_t &oi_, bool exists_)
: oi(oi_), exists(exists_) {}
};
SnapSetContext 保存了SnapSet的上下文信息。关于SnapSet的内容,可以参考相关的文章介绍。
struct SnapSetContext {
hobject_t oid; //对象
int ref; //本结构的引用计数
bool registered; //是否在SnapSet Cache中记录
SnapSet snapset; //SnapSet 对象快照相关的记录
bool exists; //snapset是否存在
SnapSetContext(const hobject_t& o) :
oid(o), ref(0), registered(false), exists(true) { }
};
对象ObjectContext保存了一个对象的上下文信息
struct ObjectContext {
ObjectState obs;
//obs,主要包括object_infot_t, 描述了对象的状态信息
SnapSetContext *ssc; // may be null
//快照上下文信息
Context *destructor_callback;
private:
Mutex lock;
public:
Cond cond;
int unstable_writes, readers, writers_waiting, readers_waiting;
//正在写操作的数目,正在读操作的数目
//等待写操作的数目,等待读操作的数目
// set if writes for this object are blocked
//on another objects recovery
ObjectContextRef blocked_by; // object blocking our writes
set<ObjectContextRef> blocking; // objects whose writes we block
// any entity in obs.oi.watchers
//MUST be in either watchers or unconnected_watchers.
map<pair<uint64_t, entity_name_t>, WatchRef> watchers;
// attr cache
map<string, bufferlist> attr_cache;
list<OpRequestRef> waiters; ///< ops waiting on state change
int count; ///< number of readers or writers
State state:4; ///< rw state
/// if set, restart backfill when we can get a read lock
bool recovery_read_marker:1;
/// if set, requeue snaptrim on lock release
bool snaptrimmer_write_marker:1;
......
}
ReplicatedPG::get_object_context
函数get_object_context获取一个对象的object_context信息。输入参数为:
ObjectContextRef
ReplicatedPG::get_object_context(const hobject_t& soid,
bool can_create,
map<string, bufferlist> *attrs)
soid 要获取的对象
bool can_create 是否需要创建,如果是写操作,就设置
*attrs 对象的属性
- 首先从object_contexts的lru cache里获取,如果获取成功,就之间返回
- 如果lru cache里没有查找到:
- 如果attrs 输入参数里没有给出,就调用pgbackend->objects_get_attr(soid, OI_ATTR, &bv)
- decode后获取object_info_t
- 调用get_snapset_context获取SnapSetContext
- 设置obc相关的参数,并返回obc
ReplicatedPG::get_snapset_context
获取snapset_context
这个函数和get_object_context类似
- 从snapset_contexts 获取snapset_context,如果成功,直接返回结果
- 如果不存在,并且can_create,就调用pgbackend->objects_get_attr(oid.get_head(), SS_ATTR, &bv)获取SS_ATTR属性。 只有head对象有ATTR属性,如果head对象不存在,就获取snapdir
ReplicatedPG::find_object_context
这个函数查找对象的object_context,这个要理解snapshot相关的知识。根据snap_seq 正确获取相应的clone对象,然后获取相应的object_context
int ReplicatedPG::find_object_context(
const hobject_t& oid, 要查找的对象
ObjectContextRef *pobc, 输出对象的ObjectContext
bool can_create, 是否需要创建
bool map_snapid_to_clone, 映射snapid到clone对象
hobject_t *pmissing 如果对象不存在,返回缺失的对象
}
参数map_snapid_to_clone, 指该snap是否可以直接对应一个clone对象,也就是snap对象的snap_id在SnapSet的 clones 列表中。
- 首先如果是head对象,就调用函数get_object_context获取ObjectContext对象,如果失败,设置pmissing对象
- 其次检查如果是snapdir对象,先获取head对象的object_context,如果失败,在获取snapdir的object_context
- 如果!map_snapid_to_clone 并且该snap 已经标记删除了,就直接返回
- 调用函数获取 get_snapset_context
-
如果是map_snapid_to_clone
- 如果oid.snap > ssc->snapset.seq ,说明该snap是最新做的快照,首次相关的操作,osd端还没有相关的信息更新。直接返回head对象object_context
- 否则,直接检查SnapSet的 clones 列表, 如果没有,就之间返回-ENOENT
- 如果找到,检查对象是否missing,如果没有,就获取该clone对象的object_context
-
如果不是 map_snapid_to_clone, 就不能从snap_id直接获取clone对象,需要更加snaps和clones 列表,计算snap_id对应的clone对象
- 如果oid.snap > ssc->snapset.seq,获取head对象
- 计算oid.snap 首次大于 ssc->snapset.clones 列表中的clone对象,就是oid对应的clone对象
- 构建soid对象
- 检查该soid对象是否是 missing
- 调用函数get_object_context 获取 object_context
-
最后调用snapid_t first = obc->obs.oi.snaps[obc->obs.oi.snaps.size()-1];
snapid_t last = obc->obs.oi.snaps[0];
验证了first <= oid.snap