与读缓存类似,写缓存也有大文件小文件的区分,这里先讨论写小文件。大致的思想就是将CacheVC::blocks中的数据拷贝到agg_buf中,整个流程如下:

Cache::open_write: 根据key生成一个新key作为earliest_key,不过小文件的话貌似earlist_key没用。根据CacheV->first_key计算的到vol。执行Vol::open_write,在Vol::open_write中进行了简单的aggregation buf的错误检查就执行了OpenDir::open_write。最后将CacheVC::openWriteMain设置为回调函数,流程结束。

OpenDir::open_write: 根据first_key计算bucket,原理就是最低32位处以bucket数量然后取余。遍历这个bucket所有的位置看是否已经有写操作了,如果没有的话,新建一个OpenDirEntry对象并且放倒bucket中

CacheVC::openWriteMain: 设置读写相关的字节数,将资源在reader中consume掉,向continuation发送VC_EVENT_WRITE_COMPLETE信号

CacheVC::die: 设置回调函数为CacheVC::openWriteClose并执行

CacheVC::openWriteClose: 执行openWriteCloseHead

CacheVC::openWriteCloseHead: 设置回调函数为CacheVC::updateVector,并执行

CacheVC::updateVector: 设置了回调函数为CacheVC::openWriteCloseHeadDone,执行CacheVC::do_write_call。

CacheVC::do_write_call函数对回调函数执行了push操作,CacheVC::handleWrite函数又执行了pop操作。CacheVC::do_write_call最终返回了EVENT_RETURN,CacheVC::updateVector函数最后会执行回调函数CacheVC::openWriteCloseHeadDone。

CacheVC::handleWrite: 将回调函数POP出。计算大概大小,将CacheVC对象加入到vol->agg中,vol->agg是一个队列,每个元素就是一个写缓存的CacheVC。判断是否已经有io操作正在进行了,如果没有,直接执行Vol::aggWrite

Vol::aggWrite: 循环遍历vol->agg队列,判断agg_buf是否还能装下当前的资源,如果可以装下,执行agg_copy,将CacheVC从vol->agg中删除。

agg_copy: 设置dir的各个bit,初始化vc->dir各个bit。一开始将一个Doc类型指针指向agg_buf,并对这个Doc进行初始化。执行iobufferblock_memcpy,将vc中的内从拷贝到doc->data()。

CacheVC::openWriteCloseHeadDone: 执行了dir_insert函数,dir_insert函数中在特定的segment的特定的bucket中找一个位置放置这个资源的dir。最后执行CacheVC::openWriteCloseDir

dir_insert: 通过key找到对应的bucket,在freelist中找到一个位置,将dir放进去。

CacheVC::openWriteCloseDir: 做一些善后工作,释放CacheVC的资源。


对于写大文件,CacheVC::openWriteMain处理的逻辑有一些不同,具体如下:


CacheVC::openWriteMain: 会执行若干次,每次会对vio中的数据做一些处理,主要是初始化CacheVC::blocks,标记一些已经处理过的数据长度,本次处理的数据长度。如果攒够了一个fragment的长度,就写到agg_buf中,并且开始处理下一个fragment的数据。CacheVC::openWriteMain函数中并没有实现数据的拷贝。


CacheVC::openWriteWriteDone:每个fragment数据处理完了会执行这个函数。函数执行了iobufferblock_skip,因为每次回向agg_buf中写一个fragment那么多数据,但是执行CacheVC::openWriteWriteDone时已经处理过的数据可能大于一个fragment的长度,所以需要重新计算CacheVC::length。获取了下一个key,将回调函数设置为CacheVC::openWriteMain并执行。