| 717 | } |
| 718 | |
| 719 | void BufferPool::Client::WriteDirtyPagesAsync(int64_t min_bytes_to_write) { |
| 720 | DCHECK_GE(min_bytes_to_write, 0) << DebugStringLocked(); |
| 721 | DCHECK_LE(min_bytes_to_write, dirty_unpinned_pages_.bytes()) << DebugStringLocked(); |
| 722 | if (file_group_ == NULL) { |
| 723 | // Spilling disabled - there should be no unpinned pages to write. |
| 724 | DCHECK_EQ(0, min_bytes_to_write); |
| 725 | DCHECK_EQ(0, dirty_unpinned_pages_.bytes()); |
| 726 | return; |
| 727 | } |
| 728 | // No point in starting writes if an error occurred because future operations for the |
| 729 | // client will fail regardless. |
| 730 | if (!write_status_.ok()) return; |
| 731 | |
| 732 | // Compute the ideal amount of writes to start. We use a simple heuristic based on the |
| 733 | // total number of writes. The FileGroup's allocation should spread the writes across |
| 734 | // disks somewhat, but doesn't guarantee we're fully using all available disks. In |
| 735 | // future we could track the # of writes per-disk. |
| 736 | const int64_t target_writes = FLAGS_concurrent_scratch_ios_per_device |
| 737 | * file_group_->tmp_file_mgr()->NumActiveTmpDevices(); |
| 738 | |
| 739 | int64_t bytes_written = 0; |
| 740 | while (!dirty_unpinned_pages_.empty() |
| 741 | && (bytes_written < min_bytes_to_write |
| 742 | || in_flight_write_pages_.size() < target_writes)) { |
| 743 | Page* page = dirty_unpinned_pages_.tail(); // LIFO. |
| 744 | DCHECK(page != NULL) << "Should have been enough dirty unpinned pages"; |
| 745 | { |
| 746 | lock_guard<SpinLock> pl(page->buffer_lock); |
| 747 | DCHECK(file_group_ != NULL); |
| 748 | DCHECK(page->buffer.is_open()); |
| 749 | Status status = file_group_->Write(page->buffer.mem_range(), |
| 750 | [this, page]( |
| 751 | const Status& write_status) { WriteCompleteCallback(page, write_status); }, |
| 752 | &page->write_handle, &counters_); |
| 753 | // Exit early on error: there is no point in starting more writes because future |
| 754 | /// operations for this client will fail regardless. |
| 755 | if (!status.ok()) { |
| 756 | write_status_.MergeStatus(status); |
| 757 | return; |
| 758 | } |
| 759 | COUNTER_ADD(counters().bytes_written, page->write_handle->on_disk_len()); |
| 760 | COUNTER_ADD(counters().write_io_ops, 1); |
| 761 | } |
| 762 | // Now that the write is in flight, update all the state |
| 763 | Page* tmp = dirty_unpinned_pages_.PopBack(); |
| 764 | DCHECK_EQ(tmp, page); |
| 765 | in_flight_write_pages_.Enqueue(page); |
| 766 | bytes_written += page->len; |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | void BufferPool::Client::WriteCompleteCallback(Page* page, const Status& write_status) { |
| 771 | #ifndef NDEBUG |
nothing calls this directly
no test coverage detected