| 664 | void SsdFile::checkpoint(bool force) { |
| 665 | std::lock_guard<std::shared_mutex> l(mutex_); |
| 666 | if (!force && (bytesAfterCheckpoint_ < checkpointIntervalBytes_)) { |
| 667 | return; |
| 668 | } |
| 669 | |
| 670 | BOLT_SSD_CACHE_LOG(INFO) << "Checkpointing shard " << shardId_ |
| 671 | << ", force: " << force << " bytesAfterCheckpoint: " |
| 672 | << succinctBytes(bytesAfterCheckpoint_) |
| 673 | << " checkpointIntervalBytes: " |
| 674 | << succinctBytes(checkpointIntervalBytes_); |
| 675 | |
| 676 | checkpointDeleted_ = false; |
| 677 | bytesAfterCheckpoint_ = 0; |
| 678 | try { |
| 679 | // We schedule the potentially long fsync of the cache file on another |
| 680 | // thread of the cache write executor, if available. If there is none, we do |
| 681 | // the sync on this thread at the end. |
| 682 | auto fileSync = std::make_shared<AsyncSource<int>>( |
| 683 | [fd = fd_]() { return std::make_unique<int>(::fsync(fd)); }); |
| 684 | if (executor_ != nullptr) { |
| 685 | executor_->add([fileSync]() { fileSync->prepare(); }); |
| 686 | } |
| 687 | |
| 688 | const auto checkRc = [&](int32_t rc, const std::string& errMsg) { |
| 689 | if (rc < 0) { |
| 690 | BOLT_FAIL("{} with rc {} :{}", errMsg, rc, folly::errnoStr(errno)); |
| 691 | } |
| 692 | return rc; |
| 693 | }; |
| 694 | |
| 695 | std::ofstream state; |
| 696 | auto checkpointPath = fileName_ + kCheckpointExtension; |
| 697 | state.exceptions(std::ofstream::failbit); |
| 698 | state.open(checkpointPath, std::ios_base::out | std::ios_base::trunc); |
| 699 | // The checkpoint state file contains: |
| 700 | // int32_t The 4 bytes of kCheckpointMagic, |
| 701 | // int32_t maxRegions, |
| 702 | // int32_t numRegions, |
| 703 | // regionScores from the 'tracker_', |
| 704 | // {fileId, fileName} pairs, |
| 705 | // kMapMarker, |
| 706 | // {fileId, offset, SSdRun} triples, |
| 707 | // kEndMarker. |
| 708 | state.write(kCheckpointMagic, sizeof(int32_t)); |
| 709 | state.write(asChar(&maxRegions_), sizeof(maxRegions_)); |
| 710 | state.write(asChar(&numRegions_), sizeof(numRegions_)); |
| 711 | |
| 712 | // Copy the region scores before writing out for tsan. |
| 713 | const auto scoresCopy = tracker_.copyScores(); |
| 714 | state.write(asChar(scoresCopy.data()), maxRegions_ * sizeof(uint64_t)); |
| 715 | std::unordered_set<uint64_t> fileNums; |
| 716 | for (const auto& entry : entries_) { |
| 717 | const auto fileNum = entry.first.fileNum.id(); |
| 718 | if (fileNums.insert(fileNum).second) { |
| 719 | state.write(asChar(&fileNum), sizeof(fileNum)); |
| 720 | const auto name = fileIds().string(fileNum); |
| 721 | const int32_t length = name.size(); |
| 722 | state.write(asChar(&length), sizeof(length)); |
| 723 | state.write(name.data(), length); |
no test coverage detected