| 634 | } |
| 635 | |
| 636 | void TupleCacheMgr::SyncFileToDisk(const string& cache_key) { |
| 637 | Cache::UniqueHandle pos = cache_->Lookup(cache_key); |
| 638 | // The entry can be evicted while waiting to be synced to disk. If the entry no longer |
| 639 | // exists, there is nothing to do. |
| 640 | if (pos == nullptr) return; |
| 641 | UniqueHandle handle{new Handle()}; |
| 642 | handle->cache_handle = move(pos); |
| 643 | // If the entry has a state other than COMPLETE_UNSYNCED, it could have been |
| 644 | // evicted and recreated. There is nothing to do. |
| 645 | if (TupleCacheState::COMPLETE_UNSYNCED != GetState(handle.get())) { |
| 646 | return; |
| 647 | } |
| 648 | bool success = true; |
| 649 | // Some unit tests don't create a real file when testing the TupleCacheMgr, so |
| 650 | // only do the sync if there is a backing file |
| 651 | bool has_backing_file = !(debug_pos_ & DebugPos::NO_FILES); |
| 652 | if (has_backing_file) { |
| 653 | // Open the cache file associated with this key, then call Sync() on it, and |
| 654 | // close it. |
| 655 | std::string file_path = GetPath(handle); |
| 656 | std::unique_ptr<kudu::RWFile> file_to_sync; |
| 657 | kudu::RWFileOptions opts; |
| 658 | opts.mode = kudu::Env::OpenMode::MUST_EXIST; |
| 659 | kudu::Status s = kudu::Env::Default()->NewRWFile(opts, file_path, &file_to_sync); |
| 660 | if (!s.ok()) { |
| 661 | LOG(WARNING) << Substitute("SyncFileToDisk: Failed to open file $0: $1", file_path, |
| 662 | s.ToString()); |
| 663 | success = false; |
| 664 | } else { |
| 665 | s = file_to_sync->Sync(); |
| 666 | if (!s.ok()) { |
| 667 | LOG(WARNING) << Substitute("SyncFileToDisk: Failed to sync file $0: $1", |
| 668 | file_path, s.ToString()); |
| 669 | success = false; |
| 670 | } |
| 671 | // Close the file even if Sync() fails |
| 672 | s = file_to_sync->Close(); |
| 673 | if (!s.ok()) { |
| 674 | LOG(WARNING) << Substitute("SyncFileToDisk: Failed to close file $0: $1", |
| 675 | file_path, s.ToString()); |
| 676 | success = false; |
| 677 | } |
| 678 | } |
| 679 | } |
| 680 | if (success) { |
| 681 | bool update_succeeded = UpdateState(handle.get(), |
| 682 | TupleCacheState::COMPLETE_UNSYNCED, TupleCacheState::COMPLETE); |
| 683 | if (update_succeeded) { |
| 684 | tuple_cache_outstanding_writes_bytes_->Increment(-GetCharge(handle.get())); |
| 685 | } |
| 686 | // Only crash for a failed state change on debug builds. The sync completed |
| 687 | // and the state change doesn't really impact external behavior. It isn't |
| 688 | // worth crashing on a release build. |
| 689 | DCHECK(update_succeeded); |
| 690 | } else { |
| 691 | // In case of any error, erase this cache entry |
| 692 | VLOG_FILE << "Tuple Cache: SyncFileToDisk failed. Evicting " << GetPath(handle); |
| 693 | cache_->Erase(cache_->Key(handle->cache_handle)); |
no test coverage detected