Test that scratch devices are blacklisted after a write error. The query that encountered the write error should not allocate more pages on that device, but existing pages on the device will remain in use and future queries will use the device.
| 1739 | // encountered the write error should not allocate more pages on that device, but |
| 1740 | // existing pages on the device will remain in use and future queries will use the device. |
| 1741 | void BufferPoolTest::TestWriteErrorBlacklist( |
| 1742 | const string& compression, bool punch_holes) { |
| 1743 | // Set up two file groups with two temporary dirs. |
| 1744 | vector<string> tmp_dirs = InitTmpFileMgr(2, compression, punch_holes); |
| 1745 | // Simulate two concurrent queries. |
| 1746 | const int TOTAL_QUERIES = 3; |
| 1747 | const int INITIAL_QUERIES = 2; |
| 1748 | const int MAX_NUM_PAGES = 6; |
| 1749 | const int PAGES_PER_QUERY = MAX_NUM_PAGES / TOTAL_QUERIES; |
| 1750 | const int64_t TOTAL_MEM = MAX_NUM_PAGES * TEST_BUFFER_LEN; |
| 1751 | const int64_t MEM_PER_QUERY = PAGES_PER_QUERY * TEST_BUFFER_LEN; |
| 1752 | BufferPool pool(test_env_->metrics(), TEST_BUFFER_LEN, TOTAL_MEM, TOTAL_MEM); |
| 1753 | global_reservations_.InitRootTracker(NewProfile(), TOTAL_MEM); |
| 1754 | vector<TmpFileGroup*> file_groups; |
| 1755 | vector<ClientHandle> clients(TOTAL_QUERIES); |
| 1756 | for (int i = 0; i < INITIAL_QUERIES; ++i) { |
| 1757 | file_groups.push_back(NewFileGroup()); |
| 1758 | ASSERT_OK(pool.RegisterClient("test client", file_groups[i], &global_reservations_, |
| 1759 | nullptr, MEM_PER_QUERY, NewProfile(), &clients[i])); |
| 1760 | ASSERT_TRUE(clients[i].IncreaseReservation(MEM_PER_QUERY)); |
| 1761 | } |
| 1762 | |
| 1763 | // Allocate files for all 2x2 combinations by unpinning pages. |
| 1764 | vector<vector<PageHandle>> pages(TOTAL_QUERIES); |
| 1765 | for (int i = 0; i < INITIAL_QUERIES; ++i) { |
| 1766 | CreatePages(&pool, &clients[i], TEST_BUFFER_LEN, MEM_PER_QUERY, &pages[i]); |
| 1767 | WriteData(pages[i], 0); |
| 1768 | UnpinAll(&pool, &clients[i], &pages[i]); |
| 1769 | for (int j = 0; j < PAGES_PER_QUERY; ++j) { |
| 1770 | LOG(INFO) << "Manager " << i << " Block " << j << " backed by file " |
| 1771 | << TmpFilePath(&pages[i][j]); |
| 1772 | } |
| 1773 | } |
| 1774 | for (int i = 0; i < INITIAL_QUERIES; ++i) WaitForAllWrites(&clients[i]); |
| 1775 | const int ERROR_QUERY = 0; |
| 1776 | const int NO_ERROR_QUERY = 1; |
| 1777 | const string& error_dir = tmp_dirs[0]; |
| 1778 | const string& good_dir = tmp_dirs[1]; |
| 1779 | // Delete one file from first scratch dir for first query to trigger an error. |
| 1780 | PageHandle* error_page = FindPageInDir(pages[ERROR_QUERY], error_dir); |
| 1781 | ASSERT_TRUE(error_page != NULL) |
| 1782 | << TmpFilePaths(pages[ERROR_QUERY]) << " not in " << DumpScratchDir(error_dir); |
| 1783 | const string& error_file_path = TmpFilePath(error_page); |
| 1784 | for (int i = 0; i < INITIAL_QUERIES; ++i) { |
| 1785 | ASSERT_OK(PinAll(&pool, &clients[i], &pages[i])); |
| 1786 | } |
| 1787 | DisableBackingFile(error_file_path); |
| 1788 | for (int i = 0; i < INITIAL_QUERIES; ++i) UnpinAll(&pool, &clients[i], &pages[i]); |
| 1789 | |
| 1790 | // At least one write should hit an error, but it should be recoverable. |
| 1791 | for (int i = 0; i < INITIAL_QUERIES; ++i) WaitForAllWrites(&clients[i]); |
| 1792 | |
| 1793 | // Both clients should still be usable - test the API. |
| 1794 | for (int i = 0; i < INITIAL_QUERIES; ++i) { |
| 1795 | ASSERT_OK(PinAll(&pool, &clients[i], &pages[i])); |
| 1796 | VerifyData(pages[i], 0); |
| 1797 | UnpinAll(&pool, &clients[i], &pages[i]); |
| 1798 | ASSERT_OK(AllocateAndFree(&pool, &clients[i], TEST_BUFFER_LEN)); |
nothing calls this directly
no test coverage detected