Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Potential issue in invocation of FileBufferPool::evict_all_pages() in BplusTreeHandler::sync() #17

Closed
hotwords123 opened this issue May 11, 2024 · 6 comments
Labels
bug Something isn't working

Comments

@hotwords123
Copy link
Contributor

hotwords123 commented May 11, 2024

背景:Lab1 实现了 FileBufferPoolevict_all_pages 方法,实验文档的描述为:

evict_all_pages函数是在关闭文件时被调用的,因此需要将该文件对应的Frame都依次驱逐并刷盘

由于 FileBufferPool 在文件打开时,成员变量 hdr_frame_file_header_ 始终持有一份 page 0 的引用(用于记录文件元信息和页面分配情况),因此在引用释放前理论上不应该调用 evict_all_pages,否则将因为无法释放 page 0 而产生错误。FileBufferPoolclose_file 方法在调用 evict_all_pages() 前先执行了 hdr_frame_->unpin(),也印证了这一点。

问题:在 BplusTreeHandlersync 方法中(创建 index 时会在写入 FIRST_INDEX_PAGE 后调用它),也调用了 file_buffer_pool_->evict_all_pages(),目的应该是把文件更改刷盘。但这里文件并没有被关闭,因此这里的调用不符合 evict_all_pages 的先决条件,会导致错误。

可能的修复方案(不完全):从语义上看,这里只需要将内存中的页面刷盘即可,不需要驱逐页面,可以考虑添加一个 flush_all_pages 方法。或者在 evict_all_pages 中特判 page 0 的情况,但这样会使函数功能不明确(不符合实验文档的描述),增加维护复杂度。

@RkGrit
Copy link
Contributor

RkGrit commented May 12, 2024

BplusTreeHandler 的 sync 方法是在 BplusTreeHandler 的 create 方法中调用的,在调用前,会先执行bp->unpin_page(header_frame)。

@hotwords123
Copy link
Contributor Author

BplusTreeHandler 的 sync 方法是在 BplusTreeHandler 的 create 方法中调用的,在调用前,会先执行bp->unpin_page(header_frame)。

这里 unpin 的是 B+ 树索引的 header page,记录的是索引的元信息,它的 page_numFIRST_INDEX_PAGE,也就是 1。

Frame *header_frame;
rc = bp->allocate_page(&header_frame);
if (rc != RC::SUCCESS) {
LOG_WARN("failed to allocate header page for bplus tree. rc=%d:%s", rc, strrc(rc));
bpm.close_file(file_name);
return rc;
}
if (header_frame->page_num() != FIRST_INDEX_PAGE) {
LOG_WARN("header page num should be %d but got %d. is it a new file : %s",
FIRST_INDEX_PAGE, header_frame->page_num(), file_name);
bpm.close_file(file_name);
return RC::INTERNAL;
}

但是 hdr_frame_ 持有的是整个文件的 header page,记录文件的元信息,它的 page_num 是 0,不是同一个 page。前面提到的是这个 header page。

hdr_frame_->set_file_desc(fd);
hdr_frame_->access();
if ((rc = load_page(BP_HEADER_PAGE, hdr_frame_)) != RC::SUCCESS) {
LOG_ERROR("Failed to load first page of %s, due to %s.", file_name, strerror(errno));
evict_page(BP_HEADER_PAGE, hdr_frame_);
close(fd);
file_desc_ = -1;
return rc;
}
file_header_ = (FileHeader *)hdr_frame_->data();

@RkGrit
Copy link
Contributor

RkGrit commented May 12, 2024

是的,hdr_frame_ 持有的是整个文件的元信息,但它没有必要在每次sync的时候刷盘的,只需要在关闭索引文件的时候被刷盘也能保证正确性。

@hotwords123
Copy link
Contributor Author

是的,hdr_frame_ 持有的是整个文件的元信息,但它没有必要在每次sync的时候刷盘的,只需要在关闭索引文件的时候被刷盘也能保证正确性。

嗯,但是如果按原先的代码,在 sync 中调用 evict_all_pages 根据文档又应该驱逐 page 0,就产生了前面的问题,所以您觉得怎么改比较好呢?

@RkGrit
Copy link
Contributor

RkGrit commented May 13, 2024

那可以像你说的,增加一个flush_all_pages 方法

@ycycse ycycse added the bug Something isn't working label May 13, 2024
@hotwords123
Copy link
Contributor Author

那可以像你说的,增加一个flush_all_pages 方法

已经在 #19 中添加了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants