]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | // This source code is licensed under both the GPLv2 (found in the | |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
5 | // | |
6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. | |
7 | // Use of this source code is governed by a BSD-style license that can be | |
8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. | |
9 | #ifndef ROCKSDB_LITE | |
10 | ||
11 | #include "db/db_test_util.h" | |
1e59de90 | 12 | #include "file/sst_file_manager_impl.h" |
20effc67 TL |
13 | #include "port/stack_trace.h" |
14 | #include "rocksdb/io_status.h" | |
20effc67 TL |
15 | #include "rocksdb/sst_file_manager.h" |
16 | #if !defined(ROCKSDB_LITE) | |
17 | #include "test_util/sync_point.h" | |
18 | #endif | |
19 | #include "util/random.h" | |
20 | #include "utilities/fault_injection_env.h" | |
21 | #include "utilities/fault_injection_fs.h" | |
22 | ||
23 | namespace ROCKSDB_NAMESPACE { | |
24 | ||
25 | class DBErrorHandlingFSTest : public DBTestBase { | |
26 | public: | |
27 | DBErrorHandlingFSTest() | |
1e59de90 | 28 | : DBTestBase("db_error_handling_fs_test", /*env_do_fsync=*/true) { |
20effc67 TL |
29 | fault_fs_.reset(new FaultInjectionTestFS(env_->GetFileSystem())); |
30 | fault_env_.reset(new CompositeEnvWrapper(env_, fault_fs_)); | |
31 | } | |
32 | ||
33 | std::string GetManifestNameFromLiveFiles() { | |
34 | std::vector<std::string> live_files; | |
35 | uint64_t manifest_size; | |
36 | ||
37 | Status s = dbfull()->GetLiveFiles(live_files, &manifest_size, false); | |
38 | if (!s.ok()) { | |
39 | return ""; | |
40 | } | |
41 | for (auto& file : live_files) { | |
42 | uint64_t num = 0; | |
43 | FileType type; | |
44 | if (ParseFileName(file, &num, &type) && type == kDescriptorFile) { | |
45 | return file; | |
46 | } | |
47 | } | |
48 | return ""; | |
49 | } | |
50 | ||
51 | std::shared_ptr<FaultInjectionTestFS> fault_fs_; | |
52 | std::unique_ptr<Env> fault_env_; | |
53 | }; | |
54 | ||
55 | class ErrorHandlerFSListener : public EventListener { | |
56 | public: | |
57 | ErrorHandlerFSListener() | |
58 | : mutex_(), | |
59 | cv_(&mutex_), | |
60 | no_auto_recovery_(false), | |
61 | recovery_complete_(false), | |
62 | file_creation_started_(false), | |
63 | override_bg_error_(false), | |
64 | file_count_(0), | |
65 | fault_fs_(nullptr) {} | |
66 | ~ErrorHandlerFSListener() { | |
67 | file_creation_error_.PermitUncheckedError(); | |
68 | bg_error_.PermitUncheckedError(); | |
1e59de90 | 69 | new_bg_error_.PermitUncheckedError(); |
20effc67 TL |
70 | } |
71 | ||
72 | void OnTableFileCreationStarted( | |
73 | const TableFileCreationBriefInfo& /*ti*/) override { | |
74 | InstrumentedMutexLock l(&mutex_); | |
75 | file_creation_started_ = true; | |
76 | if (file_count_ > 0) { | |
77 | if (--file_count_ == 0) { | |
78 | fault_fs_->SetFilesystemActive(false, file_creation_error_); | |
79 | file_creation_error_ = IOStatus::OK(); | |
80 | } | |
81 | } | |
82 | cv_.SignalAll(); | |
83 | } | |
84 | ||
85 | void OnErrorRecoveryBegin(BackgroundErrorReason /*reason*/, Status bg_error, | |
86 | bool* auto_recovery) override { | |
87 | bg_error.PermitUncheckedError(); | |
88 | if (*auto_recovery && no_auto_recovery_) { | |
89 | *auto_recovery = false; | |
90 | } | |
91 | } | |
92 | ||
1e59de90 | 93 | void OnErrorRecoveryEnd(const BackgroundErrorRecoveryInfo& info) override { |
20effc67 TL |
94 | InstrumentedMutexLock l(&mutex_); |
95 | recovery_complete_ = true; | |
96 | cv_.SignalAll(); | |
1e59de90 | 97 | new_bg_error_ = info.new_bg_error; |
20effc67 TL |
98 | } |
99 | ||
100 | bool WaitForRecovery(uint64_t /*abs_time_us*/) { | |
101 | InstrumentedMutexLock l(&mutex_); | |
102 | while (!recovery_complete_) { | |
103 | cv_.Wait(/*abs_time_us*/); | |
104 | } | |
105 | if (recovery_complete_) { | |
106 | recovery_complete_ = false; | |
107 | return true; | |
108 | } | |
109 | return false; | |
110 | } | |
111 | ||
112 | void WaitForTableFileCreationStarted(uint64_t /*abs_time_us*/) { | |
113 | InstrumentedMutexLock l(&mutex_); | |
114 | while (!file_creation_started_) { | |
115 | cv_.Wait(/*abs_time_us*/); | |
116 | } | |
117 | file_creation_started_ = false; | |
118 | } | |
119 | ||
120 | void OnBackgroundError(BackgroundErrorReason /*reason*/, | |
121 | Status* bg_error) override { | |
122 | if (override_bg_error_) { | |
123 | *bg_error = bg_error_; | |
124 | override_bg_error_ = false; | |
125 | } | |
126 | } | |
127 | ||
128 | void EnableAutoRecovery(bool enable = true) { no_auto_recovery_ = !enable; } | |
129 | ||
130 | void OverrideBGError(Status bg_err) { | |
131 | bg_error_ = bg_err; | |
132 | override_bg_error_ = true; | |
133 | } | |
134 | ||
135 | void InjectFileCreationError(FaultInjectionTestFS* fs, int file_count, | |
136 | IOStatus io_s) { | |
137 | fault_fs_ = fs; | |
138 | file_count_ = file_count; | |
139 | file_creation_error_ = io_s; | |
140 | } | |
141 | ||
1e59de90 TL |
142 | Status new_bg_error() { return new_bg_error_; } |
143 | ||
20effc67 TL |
144 | private: |
145 | InstrumentedMutex mutex_; | |
146 | InstrumentedCondVar cv_; | |
147 | bool no_auto_recovery_; | |
148 | bool recovery_complete_; | |
149 | bool file_creation_started_; | |
150 | bool override_bg_error_; | |
151 | int file_count_; | |
152 | IOStatus file_creation_error_; | |
153 | Status bg_error_; | |
1e59de90 | 154 | Status new_bg_error_; |
20effc67 TL |
155 | FaultInjectionTestFS* fault_fs_; |
156 | }; | |
157 | ||
158 | TEST_F(DBErrorHandlingFSTest, FLushWriteError) { | |
159 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
160 | new ErrorHandlerFSListener()); | |
161 | Options options = GetDefaultOptions(); | |
162 | options.env = fault_env_.get(); | |
163 | options.create_if_missing = true; | |
164 | options.listeners.emplace_back(listener); | |
1e59de90 | 165 | options.statistics = CreateDBStatistics(); |
20effc67 TL |
166 | Status s; |
167 | ||
168 | listener->EnableAutoRecovery(false); | |
169 | DestroyAndReopen(options); | |
170 | ||
171 | ASSERT_OK(Put(Key(0), "val")); | |
172 | SyncPoint::GetInstance()->SetCallBack("FlushJob::Start", [&](void*) { | |
173 | fault_fs_->SetFilesystemActive(false, IOStatus::NoSpace("Out of space")); | |
174 | }); | |
175 | SyncPoint::GetInstance()->EnableProcessing(); | |
176 | s = Flush(); | |
177 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
178 | SyncPoint::GetInstance()->DisableProcessing(); | |
179 | fault_fs_->SetFilesystemActive(true); | |
180 | s = dbfull()->Resume(); | |
1e59de90 TL |
181 | ASSERT_OK(s); |
182 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
183 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
184 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
185 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
186 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
187 | ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT)); | |
188 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
189 | ERROR_HANDLER_AUTORESUME_COUNT)); | |
190 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
191 | ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT)); | |
192 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
193 | ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT)); | |
20effc67 TL |
194 | |
195 | Reopen(options); | |
196 | ASSERT_EQ("val", Get(Key(0))); | |
197 | Destroy(options); | |
198 | } | |
199 | ||
1e59de90 TL |
200 | // All the NoSpace IOError will be handled as the regular BG Error no matter the |
201 | // retryable flag is set of not. So the auto resume for retryable IO Error will | |
202 | // not be triggered. Also, it is mapped as hard error. | |
203 | TEST_F(DBErrorHandlingFSTest, FLushWriteNoSpaceError) { | |
204 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
205 | new ErrorHandlerFSListener()); | |
206 | Options options = GetDefaultOptions(); | |
207 | options.env = fault_env_.get(); | |
208 | options.create_if_missing = true; | |
209 | options.listeners.emplace_back(listener); | |
210 | options.max_bgerror_resume_count = 2; | |
211 | options.bgerror_resume_retry_interval = 100000; // 0.1 second | |
212 | options.statistics = CreateDBStatistics(); | |
213 | Status s; | |
214 | ||
215 | listener->EnableAutoRecovery(false); | |
216 | DestroyAndReopen(options); | |
217 | ||
218 | IOStatus error_msg = IOStatus::NoSpace("Retryable IO Error"); | |
219 | error_msg.SetRetryable(true); | |
220 | ||
221 | ASSERT_OK(Put(Key(1), "val1")); | |
222 | SyncPoint::GetInstance()->SetCallBack( | |
223 | "BuildTable:BeforeFinishBuildTable", | |
224 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
225 | SyncPoint::GetInstance()->EnableProcessing(); | |
226 | s = Flush(); | |
227 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
228 | SyncPoint::GetInstance()->DisableProcessing(); | |
229 | fault_fs_->SetFilesystemActive(true); | |
230 | s = dbfull()->Resume(); | |
231 | ASSERT_OK(s); | |
232 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
233 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
234 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
235 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
236 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
237 | ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT)); | |
238 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
239 | ERROR_HANDLER_AUTORESUME_COUNT)); | |
240 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
241 | ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT)); | |
242 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
243 | ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT)); | |
244 | Destroy(options); | |
245 | } | |
246 | ||
247 | TEST_F(DBErrorHandlingFSTest, FLushWriteRetryableError) { | |
20effc67 TL |
248 | std::shared_ptr<ErrorHandlerFSListener> listener( |
249 | new ErrorHandlerFSListener()); | |
250 | Options options = GetDefaultOptions(); | |
251 | options.env = fault_env_.get(); | |
252 | options.create_if_missing = true; | |
253 | options.listeners.emplace_back(listener); | |
254 | options.max_bgerror_resume_count = 0; | |
1e59de90 | 255 | options.statistics = CreateDBStatistics(); |
20effc67 TL |
256 | Status s; |
257 | ||
258 | listener->EnableAutoRecovery(false); | |
259 | DestroyAndReopen(options); | |
260 | ||
261 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
262 | error_msg.SetRetryable(true); | |
263 | ||
264 | ASSERT_OK(Put(Key(1), "val1")); | |
265 | SyncPoint::GetInstance()->SetCallBack( | |
266 | "BuildTable:BeforeFinishBuildTable", | |
267 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
268 | SyncPoint::GetInstance()->EnableProcessing(); | |
269 | s = Flush(); | |
1e59de90 | 270 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); |
20effc67 TL |
271 | SyncPoint::GetInstance()->DisableProcessing(); |
272 | fault_fs_->SetFilesystemActive(true); | |
273 | s = dbfull()->Resume(); | |
274 | ASSERT_OK(s); | |
1e59de90 TL |
275 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( |
276 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
277 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
278 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
279 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
280 | ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT)); | |
281 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
282 | ERROR_HANDLER_AUTORESUME_COUNT)); | |
283 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
284 | ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT)); | |
285 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
286 | ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT)); | |
20effc67 TL |
287 | Reopen(options); |
288 | ASSERT_EQ("val1", Get(Key(1))); | |
289 | ||
290 | ASSERT_OK(Put(Key(2), "val2")); | |
291 | SyncPoint::GetInstance()->SetCallBack( | |
292 | "BuildTable:BeforeSyncTable", | |
293 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
294 | SyncPoint::GetInstance()->EnableProcessing(); | |
295 | s = Flush(); | |
1e59de90 | 296 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); |
20effc67 TL |
297 | SyncPoint::GetInstance()->DisableProcessing(); |
298 | fault_fs_->SetFilesystemActive(true); | |
299 | s = dbfull()->Resume(); | |
300 | ASSERT_OK(s); | |
301 | Reopen(options); | |
302 | ASSERT_EQ("val2", Get(Key(2))); | |
303 | ||
304 | ASSERT_OK(Put(Key(3), "val3")); | |
305 | SyncPoint::GetInstance()->SetCallBack( | |
306 | "BuildTable:BeforeCloseTableFile", | |
307 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
308 | SyncPoint::GetInstance()->EnableProcessing(); | |
309 | s = Flush(); | |
1e59de90 TL |
310 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); |
311 | SyncPoint::GetInstance()->DisableProcessing(); | |
312 | fault_fs_->SetFilesystemActive(true); | |
313 | s = dbfull()->Resume(); | |
314 | ASSERT_OK(s); | |
315 | Reopen(options); | |
316 | ASSERT_EQ("val3", Get(Key(3))); | |
317 | ||
318 | Destroy(options); | |
319 | } | |
320 | ||
321 | TEST_F(DBErrorHandlingFSTest, FLushWriteFileScopeError) { | |
322 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
323 | new ErrorHandlerFSListener()); | |
324 | Options options = GetDefaultOptions(); | |
325 | options.env = fault_env_.get(); | |
326 | options.create_if_missing = true; | |
327 | options.listeners.emplace_back(listener); | |
328 | options.max_bgerror_resume_count = 0; | |
329 | Status s; | |
330 | ||
331 | listener->EnableAutoRecovery(false); | |
332 | DestroyAndReopen(options); | |
333 | ||
334 | IOStatus error_msg = IOStatus::IOError("File Scope Data Loss Error"); | |
335 | error_msg.SetDataLoss(true); | |
336 | error_msg.SetScope( | |
337 | ROCKSDB_NAMESPACE::IOStatus::IOErrorScope::kIOErrorScopeFile); | |
338 | error_msg.SetRetryable(false); | |
339 | ||
340 | ASSERT_OK(Put(Key(1), "val1")); | |
341 | SyncPoint::GetInstance()->SetCallBack( | |
342 | "BuildTable:BeforeFinishBuildTable", | |
343 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
344 | SyncPoint::GetInstance()->EnableProcessing(); | |
345 | s = Flush(); | |
346 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
347 | SyncPoint::GetInstance()->DisableProcessing(); | |
348 | fault_fs_->SetFilesystemActive(true); | |
349 | s = dbfull()->Resume(); | |
350 | ASSERT_OK(s); | |
351 | Reopen(options); | |
352 | ASSERT_EQ("val1", Get(Key(1))); | |
353 | ||
354 | ASSERT_OK(Put(Key(2), "val2")); | |
355 | SyncPoint::GetInstance()->SetCallBack( | |
356 | "BuildTable:BeforeSyncTable", | |
357 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
358 | SyncPoint::GetInstance()->EnableProcessing(); | |
359 | s = Flush(); | |
360 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
361 | SyncPoint::GetInstance()->DisableProcessing(); | |
362 | fault_fs_->SetFilesystemActive(true); | |
363 | s = dbfull()->Resume(); | |
364 | ASSERT_OK(s); | |
365 | Reopen(options); | |
366 | ASSERT_EQ("val2", Get(Key(2))); | |
367 | ||
368 | ASSERT_OK(Put(Key(3), "val3")); | |
369 | SyncPoint::GetInstance()->SetCallBack( | |
370 | "BuildTable:BeforeCloseTableFile", | |
371 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
372 | SyncPoint::GetInstance()->EnableProcessing(); | |
373 | s = Flush(); | |
374 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
375 | SyncPoint::GetInstance()->DisableProcessing(); | |
376 | fault_fs_->SetFilesystemActive(true); | |
377 | s = dbfull()->Resume(); | |
378 | ASSERT_OK(s); | |
379 | Reopen(options); | |
380 | ASSERT_EQ("val3", Get(Key(3))); | |
381 | ||
382 | // not file scope, but retyrable set | |
383 | error_msg.SetDataLoss(false); | |
384 | error_msg.SetScope( | |
385 | ROCKSDB_NAMESPACE::IOStatus::IOErrorScope::kIOErrorScopeFileSystem); | |
386 | error_msg.SetRetryable(true); | |
387 | ||
388 | ASSERT_OK(Put(Key(3), "val3")); | |
389 | SyncPoint::GetInstance()->SetCallBack( | |
390 | "BuildTable:BeforeCloseTableFile", | |
391 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
392 | SyncPoint::GetInstance()->EnableProcessing(); | |
393 | s = Flush(); | |
394 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
20effc67 TL |
395 | SyncPoint::GetInstance()->DisableProcessing(); |
396 | fault_fs_->SetFilesystemActive(true); | |
397 | s = dbfull()->Resume(); | |
398 | ASSERT_OK(s); | |
399 | Reopen(options); | |
400 | ASSERT_EQ("val3", Get(Key(3))); | |
401 | ||
402 | Destroy(options); | |
403 | } | |
404 | ||
1e59de90 TL |
405 | TEST_F(DBErrorHandlingFSTest, FLushWALWriteRetryableError) { |
406 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
407 | new ErrorHandlerFSListener()); | |
408 | Options options = GetDefaultOptions(); | |
409 | options.env = fault_env_.get(); | |
410 | options.create_if_missing = true; | |
411 | options.listeners.emplace_back(listener); | |
412 | options.max_bgerror_resume_count = 0; | |
413 | Status s; | |
414 | ||
415 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
416 | error_msg.SetRetryable(true); | |
417 | ||
418 | listener->EnableAutoRecovery(false); | |
419 | SyncPoint::GetInstance()->SetCallBack( | |
420 | "DBImpl::SyncClosedLogs:Start", | |
421 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
422 | SyncPoint::GetInstance()->EnableProcessing(); | |
423 | ||
424 | CreateAndReopenWithCF({"pikachu, sdfsdfsdf"}, options); | |
425 | ||
426 | WriteOptions wo = WriteOptions(); | |
427 | wo.disableWAL = false; | |
428 | ASSERT_OK(Put(Key(1), "val1", wo)); | |
429 | ||
430 | s = Flush(); | |
431 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
432 | SyncPoint::GetInstance()->DisableProcessing(); | |
433 | fault_fs_->SetFilesystemActive(true); | |
434 | auto cfh = dbfull()->GetColumnFamilyHandle(1); | |
435 | s = dbfull()->DropColumnFamily(cfh); | |
436 | ||
437 | s = dbfull()->Resume(); | |
438 | ASSERT_OK(s); | |
439 | ASSERT_EQ("val1", Get(Key(1))); | |
440 | ASSERT_OK(Put(Key(3), "val3", wo)); | |
441 | ASSERT_EQ("val3", Get(Key(3))); | |
442 | s = Flush(); | |
443 | ASSERT_OK(s); | |
444 | ASSERT_EQ("val3", Get(Key(3))); | |
445 | ||
446 | Destroy(options); | |
447 | } | |
448 | ||
449 | TEST_F(DBErrorHandlingFSTest, FLushWALAtomicWriteRetryableError) { | |
450 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
451 | new ErrorHandlerFSListener()); | |
452 | Options options = GetDefaultOptions(); | |
453 | options.env = fault_env_.get(); | |
454 | options.create_if_missing = true; | |
455 | options.listeners.emplace_back(listener); | |
456 | options.max_bgerror_resume_count = 0; | |
457 | options.atomic_flush = true; | |
458 | Status s; | |
459 | ||
460 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
461 | error_msg.SetRetryable(true); | |
462 | ||
463 | listener->EnableAutoRecovery(false); | |
464 | SyncPoint::GetInstance()->SetCallBack( | |
465 | "DBImpl::SyncClosedLogs:Start", | |
466 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
467 | SyncPoint::GetInstance()->EnableProcessing(); | |
468 | ||
469 | CreateAndReopenWithCF({"pikachu, sdfsdfsdf"}, options); | |
470 | ||
471 | WriteOptions wo = WriteOptions(); | |
472 | wo.disableWAL = false; | |
473 | ASSERT_OK(Put(Key(1), "val1", wo)); | |
474 | ||
475 | s = Flush(); | |
476 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
477 | SyncPoint::GetInstance()->DisableProcessing(); | |
478 | fault_fs_->SetFilesystemActive(true); | |
479 | auto cfh = dbfull()->GetColumnFamilyHandle(1); | |
480 | s = dbfull()->DropColumnFamily(cfh); | |
481 | ||
482 | s = dbfull()->Resume(); | |
483 | ASSERT_OK(s); | |
484 | ASSERT_EQ("val1", Get(Key(1))); | |
485 | ASSERT_OK(Put(Key(3), "val3", wo)); | |
486 | ASSERT_EQ("val3", Get(Key(3))); | |
487 | s = Flush(); | |
488 | ASSERT_OK(s); | |
489 | ASSERT_EQ("val3", Get(Key(3))); | |
490 | ||
491 | Destroy(options); | |
492 | } | |
493 | ||
494 | // The flush error is injected before we finish the table build | |
20effc67 TL |
495 | TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError1) { |
496 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
497 | new ErrorHandlerFSListener()); | |
498 | Options options = GetDefaultOptions(); | |
499 | options.env = fault_env_.get(); | |
500 | options.create_if_missing = true; | |
501 | options.listeners.emplace_back(listener); | |
502 | options.max_bgerror_resume_count = 0; | |
1e59de90 | 503 | options.statistics = CreateDBStatistics(); |
20effc67 TL |
504 | Status s; |
505 | ||
506 | listener->EnableAutoRecovery(false); | |
507 | DestroyAndReopen(options); | |
508 | ||
509 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
510 | error_msg.SetRetryable(true); | |
511 | ||
512 | WriteOptions wo = WriteOptions(); | |
513 | wo.disableWAL = true; | |
514 | ASSERT_OK(Put(Key(1), "val1", wo)); | |
515 | SyncPoint::GetInstance()->SetCallBack( | |
516 | "BuildTable:BeforeFinishBuildTable", | |
517 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
518 | SyncPoint::GetInstance()->EnableProcessing(); | |
519 | s = Flush(); | |
520 | ASSERT_OK(Put(Key(2), "val2", wo)); | |
521 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
522 | ASSERT_EQ("val2", Get(Key(2))); | |
523 | SyncPoint::GetInstance()->DisableProcessing(); | |
524 | fault_fs_->SetFilesystemActive(true); | |
525 | s = dbfull()->Resume(); | |
1e59de90 | 526 | ASSERT_OK(s); |
20effc67 TL |
527 | ASSERT_EQ("val1", Get(Key(1))); |
528 | ASSERT_EQ("val2", Get(Key(2))); | |
529 | ASSERT_OK(Put(Key(3), "val3", wo)); | |
530 | ASSERT_EQ("val3", Get(Key(3))); | |
531 | s = Flush(); | |
532 | ASSERT_OK(s); | |
533 | ASSERT_EQ("val3", Get(Key(3))); | |
1e59de90 TL |
534 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( |
535 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
536 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
537 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
538 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
539 | ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT)); | |
540 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
541 | ERROR_HANDLER_AUTORESUME_COUNT)); | |
542 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
543 | ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT)); | |
544 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
545 | ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT)); | |
20effc67 TL |
546 | |
547 | Destroy(options); | |
548 | } | |
549 | ||
1e59de90 TL |
550 | // The retryable IO error is injected before we sync table |
551 | TEST_F(DBErrorHandlingFSTest, FLushWriteNoWALRetryableError2) { | |
20effc67 TL |
552 | std::shared_ptr<ErrorHandlerFSListener> listener( |
553 | new ErrorHandlerFSListener()); | |
554 | Options options = GetDefaultOptions(); | |
555 | options.env = fault_env_.get(); | |
556 | options.create_if_missing = true; | |
557 | options.listeners.emplace_back(listener); | |
558 | options.max_bgerror_resume_count = 0; | |
559 | Status s; | |
560 | ||
561 | listener->EnableAutoRecovery(false); | |
562 | DestroyAndReopen(options); | |
563 | ||
564 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
565 | error_msg.SetRetryable(true); | |
566 | ||
567 | WriteOptions wo = WriteOptions(); | |
568 | wo.disableWAL = true; | |
569 | ||
570 | ASSERT_OK(Put(Key(1), "val1", wo)); | |
571 | SyncPoint::GetInstance()->SetCallBack( | |
572 | "BuildTable:BeforeSyncTable", | |
573 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
574 | SyncPoint::GetInstance()->EnableProcessing(); | |
575 | s = Flush(); | |
576 | ASSERT_OK(Put(Key(2), "val2", wo)); | |
577 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
578 | ASSERT_EQ("val2", Get(Key(2))); | |
579 | SyncPoint::GetInstance()->DisableProcessing(); | |
580 | fault_fs_->SetFilesystemActive(true); | |
581 | s = dbfull()->Resume(); | |
1e59de90 | 582 | ASSERT_OK(s); |
20effc67 TL |
583 | ASSERT_EQ("val1", Get(Key(1))); |
584 | ASSERT_EQ("val2", Get(Key(2))); | |
585 | ASSERT_OK(Put(Key(3), "val3", wo)); | |
586 | ASSERT_EQ("val3", Get(Key(3))); | |
587 | s = Flush(); | |
588 | ASSERT_OK(s); | |
589 | ASSERT_EQ("val3", Get(Key(3))); | |
590 | ||
591 | Destroy(options); | |
592 | } | |
593 | ||
1e59de90 TL |
594 | // The retryable IO error is injected before we close the table file |
595 | TEST_F(DBErrorHandlingFSTest, FLushWriteNoWALRetryableError3) { | |
20effc67 TL |
596 | std::shared_ptr<ErrorHandlerFSListener> listener( |
597 | new ErrorHandlerFSListener()); | |
598 | Options options = GetDefaultOptions(); | |
599 | options.env = fault_env_.get(); | |
600 | options.create_if_missing = true; | |
601 | options.listeners.emplace_back(listener); | |
602 | options.max_bgerror_resume_count = 0; | |
603 | Status s; | |
604 | ||
605 | listener->EnableAutoRecovery(false); | |
606 | DestroyAndReopen(options); | |
607 | ||
608 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
609 | error_msg.SetRetryable(true); | |
610 | ||
611 | WriteOptions wo = WriteOptions(); | |
612 | wo.disableWAL = true; | |
613 | ||
614 | ASSERT_OK(Put(Key(1), "val1", wo)); | |
615 | SyncPoint::GetInstance()->SetCallBack( | |
616 | "BuildTable:BeforeCloseTableFile", | |
617 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
618 | SyncPoint::GetInstance()->EnableProcessing(); | |
619 | s = Flush(); | |
620 | ASSERT_OK(Put(Key(2), "val2", wo)); | |
621 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
622 | ASSERT_EQ("val2", Get(Key(2))); | |
623 | SyncPoint::GetInstance()->DisableProcessing(); | |
624 | fault_fs_->SetFilesystemActive(true); | |
625 | s = dbfull()->Resume(); | |
1e59de90 | 626 | ASSERT_OK(s); |
20effc67 TL |
627 | ASSERT_EQ("val1", Get(Key(1))); |
628 | ASSERT_EQ("val2", Get(Key(2))); | |
629 | ASSERT_OK(Put(Key(3), "val3", wo)); | |
630 | ASSERT_EQ("val3", Get(Key(3))); | |
631 | s = Flush(); | |
632 | ASSERT_OK(s); | |
633 | ASSERT_EQ("val3", Get(Key(3))); | |
634 | ||
635 | Destroy(options); | |
636 | } | |
637 | ||
638 | TEST_F(DBErrorHandlingFSTest, ManifestWriteError) { | |
639 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
640 | new ErrorHandlerFSListener()); | |
641 | Options options = GetDefaultOptions(); | |
642 | options.env = fault_env_.get(); | |
643 | options.create_if_missing = true; | |
644 | options.listeners.emplace_back(listener); | |
645 | Status s; | |
646 | std::string old_manifest; | |
647 | std::string new_manifest; | |
648 | ||
649 | listener->EnableAutoRecovery(false); | |
650 | DestroyAndReopen(options); | |
651 | old_manifest = GetManifestNameFromLiveFiles(); | |
652 | ||
653 | ASSERT_OK(Put(Key(0), "val")); | |
654 | ASSERT_OK(Flush()); | |
655 | ASSERT_OK(Put(Key(1), "val")); | |
656 | SyncPoint::GetInstance()->SetCallBack( | |
657 | "VersionSet::LogAndApply:WriteManifest", [&](void*) { | |
658 | fault_fs_->SetFilesystemActive(false, | |
659 | IOStatus::NoSpace("Out of space")); | |
660 | }); | |
661 | SyncPoint::GetInstance()->EnableProcessing(); | |
662 | s = Flush(); | |
663 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
664 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
665 | SyncPoint::GetInstance()->DisableProcessing(); | |
666 | fault_fs_->SetFilesystemActive(true); | |
667 | s = dbfull()->Resume(); | |
1e59de90 | 668 | ASSERT_OK(s); |
20effc67 TL |
669 | |
670 | new_manifest = GetManifestNameFromLiveFiles(); | |
671 | ASSERT_NE(new_manifest, old_manifest); | |
672 | ||
673 | Reopen(options); | |
674 | ASSERT_EQ("val", Get(Key(0))); | |
675 | ASSERT_EQ("val", Get(Key(1))); | |
676 | Close(); | |
677 | } | |
678 | ||
679 | TEST_F(DBErrorHandlingFSTest, ManifestWriteRetryableError) { | |
680 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
681 | new ErrorHandlerFSListener()); | |
682 | Options options = GetDefaultOptions(); | |
683 | options.env = fault_env_.get(); | |
684 | options.create_if_missing = true; | |
685 | options.listeners.emplace_back(listener); | |
686 | options.max_bgerror_resume_count = 0; | |
687 | Status s; | |
688 | std::string old_manifest; | |
689 | std::string new_manifest; | |
690 | ||
691 | listener->EnableAutoRecovery(false); | |
692 | DestroyAndReopen(options); | |
693 | old_manifest = GetManifestNameFromLiveFiles(); | |
694 | ||
695 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
696 | error_msg.SetRetryable(true); | |
697 | ||
698 | ASSERT_OK(Put(Key(0), "val")); | |
699 | ASSERT_OK(Flush()); | |
700 | ASSERT_OK(Put(Key(1), "val")); | |
701 | SyncPoint::GetInstance()->SetCallBack( | |
702 | "VersionSet::LogAndApply:WriteManifest", | |
703 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
704 | SyncPoint::GetInstance()->EnableProcessing(); | |
705 | s = Flush(); | |
1e59de90 TL |
706 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); |
707 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
708 | SyncPoint::GetInstance()->DisableProcessing(); | |
709 | fault_fs_->SetFilesystemActive(true); | |
710 | s = dbfull()->Resume(); | |
711 | ASSERT_OK(s); | |
712 | ||
713 | new_manifest = GetManifestNameFromLiveFiles(); | |
714 | ASSERT_NE(new_manifest, old_manifest); | |
715 | ||
716 | Reopen(options); | |
717 | ASSERT_EQ("val", Get(Key(0))); | |
718 | ASSERT_EQ("val", Get(Key(1))); | |
719 | Close(); | |
720 | } | |
721 | ||
722 | TEST_F(DBErrorHandlingFSTest, ManifestWriteFileScopeError) { | |
723 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
724 | new ErrorHandlerFSListener()); | |
725 | Options options = GetDefaultOptions(); | |
726 | options.env = fault_env_.get(); | |
727 | options.create_if_missing = true; | |
728 | options.listeners.emplace_back(listener); | |
729 | options.max_bgerror_resume_count = 0; | |
730 | Status s; | |
731 | std::string old_manifest; | |
732 | std::string new_manifest; | |
733 | ||
734 | listener->EnableAutoRecovery(false); | |
735 | DestroyAndReopen(options); | |
736 | old_manifest = GetManifestNameFromLiveFiles(); | |
737 | ||
738 | IOStatus error_msg = IOStatus::IOError("File Scope Data Loss Error"); | |
739 | error_msg.SetDataLoss(true); | |
740 | error_msg.SetScope( | |
741 | ROCKSDB_NAMESPACE::IOStatus::IOErrorScope::kIOErrorScopeFile); | |
742 | error_msg.SetRetryable(false); | |
743 | ||
744 | ASSERT_OK(Put(Key(0), "val")); | |
745 | ASSERT_OK(Flush()); | |
746 | ASSERT_OK(Put(Key(1), "val")); | |
747 | SyncPoint::GetInstance()->SetCallBack( | |
748 | "VersionSet::LogAndApply:WriteManifest", | |
749 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
750 | SyncPoint::GetInstance()->EnableProcessing(); | |
751 | s = Flush(); | |
752 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
20effc67 TL |
753 | SyncPoint::GetInstance()->ClearAllCallBacks(); |
754 | SyncPoint::GetInstance()->DisableProcessing(); | |
755 | fault_fs_->SetFilesystemActive(true); | |
756 | s = dbfull()->Resume(); | |
1e59de90 TL |
757 | ASSERT_OK(s); |
758 | ||
759 | new_manifest = GetManifestNameFromLiveFiles(); | |
760 | ASSERT_NE(new_manifest, old_manifest); | |
761 | ||
762 | Reopen(options); | |
763 | ASSERT_EQ("val", Get(Key(0))); | |
764 | ASSERT_EQ("val", Get(Key(1))); | |
765 | Close(); | |
766 | } | |
767 | ||
768 | TEST_F(DBErrorHandlingFSTest, ManifestWriteNoWALRetryableError) { | |
769 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
770 | new ErrorHandlerFSListener()); | |
771 | Options options = GetDefaultOptions(); | |
772 | options.env = fault_env_.get(); | |
773 | options.create_if_missing = true; | |
774 | options.listeners.emplace_back(listener); | |
775 | options.max_bgerror_resume_count = 0; | |
776 | Status s; | |
777 | std::string old_manifest; | |
778 | std::string new_manifest; | |
779 | ||
780 | listener->EnableAutoRecovery(false); | |
781 | DestroyAndReopen(options); | |
782 | old_manifest = GetManifestNameFromLiveFiles(); | |
783 | ||
784 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
785 | error_msg.SetRetryable(true); | |
786 | ||
787 | WriteOptions wo = WriteOptions(); | |
788 | wo.disableWAL = true; | |
789 | ASSERT_OK(Put(Key(0), "val", wo)); | |
790 | ASSERT_OK(Flush()); | |
791 | ASSERT_OK(Put(Key(1), "val", wo)); | |
792 | SyncPoint::GetInstance()->SetCallBack( | |
793 | "VersionSet::LogAndApply:WriteManifest", | |
794 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
795 | SyncPoint::GetInstance()->EnableProcessing(); | |
796 | s = Flush(); | |
797 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
798 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
799 | SyncPoint::GetInstance()->DisableProcessing(); | |
800 | fault_fs_->SetFilesystemActive(true); | |
801 | s = dbfull()->Resume(); | |
802 | ASSERT_OK(s); | |
20effc67 TL |
803 | |
804 | new_manifest = GetManifestNameFromLiveFiles(); | |
805 | ASSERT_NE(new_manifest, old_manifest); | |
806 | ||
807 | Reopen(options); | |
808 | ASSERT_EQ("val", Get(Key(0))); | |
809 | ASSERT_EQ("val", Get(Key(1))); | |
810 | Close(); | |
811 | } | |
812 | ||
813 | TEST_F(DBErrorHandlingFSTest, DoubleManifestWriteError) { | |
814 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
815 | new ErrorHandlerFSListener()); | |
816 | Options options = GetDefaultOptions(); | |
817 | options.env = fault_env_.get(); | |
818 | options.create_if_missing = true; | |
819 | options.listeners.emplace_back(listener); | |
820 | Status s; | |
821 | std::string old_manifest; | |
822 | std::string new_manifest; | |
823 | ||
824 | listener->EnableAutoRecovery(false); | |
825 | DestroyAndReopen(options); | |
826 | old_manifest = GetManifestNameFromLiveFiles(); | |
827 | ||
828 | ASSERT_OK(Put(Key(0), "val")); | |
829 | ASSERT_OK(Flush()); | |
830 | ASSERT_OK(Put(Key(1), "val")); | |
831 | SyncPoint::GetInstance()->SetCallBack( | |
832 | "VersionSet::LogAndApply:WriteManifest", [&](void*) { | |
833 | fault_fs_->SetFilesystemActive(false, | |
834 | IOStatus::NoSpace("Out of space")); | |
835 | }); | |
836 | SyncPoint::GetInstance()->EnableProcessing(); | |
837 | s = Flush(); | |
838 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
839 | fault_fs_->SetFilesystemActive(true); | |
840 | ||
841 | // This Resume() will attempt to create a new manifest file and fail again | |
842 | s = dbfull()->Resume(); | |
843 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
844 | fault_fs_->SetFilesystemActive(true); | |
845 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
846 | SyncPoint::GetInstance()->DisableProcessing(); | |
847 | ||
848 | // A successful Resume() will create a new manifest file | |
849 | s = dbfull()->Resume(); | |
1e59de90 | 850 | ASSERT_OK(s); |
20effc67 TL |
851 | |
852 | new_manifest = GetManifestNameFromLiveFiles(); | |
853 | ASSERT_NE(new_manifest, old_manifest); | |
854 | ||
855 | Reopen(options); | |
856 | ASSERT_EQ("val", Get(Key(0))); | |
857 | ASSERT_EQ("val", Get(Key(1))); | |
858 | Close(); | |
859 | } | |
860 | ||
861 | TEST_F(DBErrorHandlingFSTest, CompactionManifestWriteError) { | |
862 | if (mem_env_ != nullptr) { | |
863 | ROCKSDB_GTEST_SKIP("Test requires non-mock environment"); | |
864 | return; | |
865 | } | |
866 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
867 | new ErrorHandlerFSListener()); | |
868 | Options options = GetDefaultOptions(); | |
869 | options.env = fault_env_.get(); | |
870 | options.create_if_missing = true; | |
871 | options.level0_file_num_compaction_trigger = 2; | |
872 | options.listeners.emplace_back(listener); | |
873 | Status s; | |
874 | std::string old_manifest; | |
875 | std::string new_manifest; | |
876 | std::atomic<bool> fail_manifest(false); | |
877 | DestroyAndReopen(options); | |
878 | old_manifest = GetManifestNameFromLiveFiles(); | |
879 | ||
880 | ASSERT_OK(Put(Key(0), "val")); | |
881 | ASSERT_OK(Put(Key(2), "val")); | |
882 | s = Flush(); | |
1e59de90 | 883 | ASSERT_OK(s); |
20effc67 TL |
884 | |
885 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
886 | // Wait for flush of 2nd L0 file before starting compaction | |
887 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
888 | "BackgroundCallCompaction:0"}, | |
889 | // Wait for compaction to detect manifest write error | |
890 | {"BackgroundCallCompaction:1", "CompactionManifestWriteError:0"}, | |
891 | // Make compaction thread wait for error to be cleared | |
892 | {"CompactionManifestWriteError:1", | |
893 | "DBImpl::BackgroundCallCompaction:FoundObsoleteFiles"}, | |
894 | // Wait for DB instance to clear bg_error before calling | |
895 | // TEST_WaitForCompact | |
896 | {"SstFileManagerImpl::ErrorCleared", "CompactionManifestWriteError:2"}}); | |
897 | // trigger manifest write failure in compaction thread | |
898 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
899 | "BackgroundCallCompaction:0", [&](void*) { fail_manifest.store(true); }); | |
900 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
901 | "VersionSet::LogAndApply:WriteManifest", [&](void*) { | |
902 | if (fail_manifest.load()) { | |
903 | fault_fs_->SetFilesystemActive(false, | |
904 | IOStatus::NoSpace("Out of space")); | |
905 | } | |
906 | }); | |
907 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
908 | ||
909 | ASSERT_OK(Put(Key(1), "val")); | |
910 | // This Flush will trigger a compaction, which will fail when appending to | |
911 | // the manifest | |
912 | s = Flush(); | |
1e59de90 | 913 | ASSERT_OK(s); |
20effc67 TL |
914 | |
915 | TEST_SYNC_POINT("CompactionManifestWriteError:0"); | |
916 | // Clear all errors so when the compaction is retried, it will succeed | |
917 | fault_fs_->SetFilesystemActive(true); | |
918 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks(); | |
919 | TEST_SYNC_POINT("CompactionManifestWriteError:1"); | |
920 | TEST_SYNC_POINT("CompactionManifestWriteError:2"); | |
921 | ||
922 | s = dbfull()->TEST_WaitForCompact(); | |
923 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); | |
1e59de90 | 924 | ASSERT_OK(s); |
20effc67 TL |
925 | |
926 | new_manifest = GetManifestNameFromLiveFiles(); | |
927 | ASSERT_NE(new_manifest, old_manifest); | |
928 | Reopen(options); | |
929 | ASSERT_EQ("val", Get(Key(0))); | |
930 | ASSERT_EQ("val", Get(Key(1))); | |
931 | ASSERT_EQ("val", Get(Key(2))); | |
932 | Close(); | |
933 | } | |
934 | ||
935 | TEST_F(DBErrorHandlingFSTest, CompactionManifestWriteRetryableError) { | |
936 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
937 | new ErrorHandlerFSListener()); | |
938 | Options options = GetDefaultOptions(); | |
939 | options.env = fault_env_.get(); | |
940 | options.create_if_missing = true; | |
941 | options.level0_file_num_compaction_trigger = 2; | |
942 | options.listeners.emplace_back(listener); | |
943 | options.max_bgerror_resume_count = 0; | |
944 | Status s; | |
945 | std::string old_manifest; | |
946 | std::string new_manifest; | |
947 | std::atomic<bool> fail_manifest(false); | |
948 | DestroyAndReopen(options); | |
949 | old_manifest = GetManifestNameFromLiveFiles(); | |
950 | ||
951 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
952 | error_msg.SetRetryable(true); | |
953 | ||
954 | ASSERT_OK(Put(Key(0), "val")); | |
955 | ASSERT_OK(Put(Key(2), "val")); | |
956 | s = Flush(); | |
1e59de90 | 957 | ASSERT_OK(s); |
20effc67 TL |
958 | |
959 | listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError)); | |
960 | listener->EnableAutoRecovery(false); | |
961 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
962 | // Wait for flush of 2nd L0 file before starting compaction | |
963 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
964 | "BackgroundCallCompaction:0"}, | |
965 | // Wait for compaction to detect manifest write error | |
966 | {"BackgroundCallCompaction:1", "CompactionManifestWriteError:0"}, | |
967 | // Make compaction thread wait for error to be cleared | |
968 | {"CompactionManifestWriteError:1", | |
969 | "DBImpl::BackgroundCallCompaction:FoundObsoleteFiles"}}); | |
970 | // trigger manifest write failure in compaction thread | |
971 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
972 | "BackgroundCallCompaction:0", [&](void*) { fail_manifest.store(true); }); | |
973 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
974 | "VersionSet::LogAndApply:WriteManifest", [&](void*) { | |
975 | if (fail_manifest.load()) { | |
976 | fault_fs_->SetFilesystemActive(false, error_msg); | |
977 | } | |
978 | }); | |
979 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
980 | ||
981 | ASSERT_OK(Put(Key(1), "val")); | |
982 | s = Flush(); | |
1e59de90 | 983 | ASSERT_OK(s); |
20effc67 TL |
984 | |
985 | TEST_SYNC_POINT("CompactionManifestWriteError:0"); | |
986 | TEST_SYNC_POINT("CompactionManifestWriteError:1"); | |
987 | ||
988 | s = dbfull()->TEST_WaitForCompact(); | |
989 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
990 | ||
991 | fault_fs_->SetFilesystemActive(true); | |
992 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
993 | SyncPoint::GetInstance()->DisableProcessing(); | |
994 | s = dbfull()->Resume(); | |
1e59de90 | 995 | ASSERT_OK(s); |
20effc67 TL |
996 | |
997 | new_manifest = GetManifestNameFromLiveFiles(); | |
998 | ASSERT_NE(new_manifest, old_manifest); | |
999 | ||
1000 | Reopen(options); | |
1001 | ASSERT_EQ("val", Get(Key(0))); | |
1002 | ASSERT_EQ("val", Get(Key(1))); | |
1003 | ASSERT_EQ("val", Get(Key(2))); | |
1004 | Close(); | |
1005 | } | |
1006 | ||
1007 | TEST_F(DBErrorHandlingFSTest, CompactionWriteError) { | |
1008 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
1009 | new ErrorHandlerFSListener()); | |
1010 | Options options = GetDefaultOptions(); | |
1011 | options.env = fault_env_.get(); | |
1012 | options.create_if_missing = true; | |
1013 | options.level0_file_num_compaction_trigger = 2; | |
1014 | options.listeners.emplace_back(listener); | |
1015 | Status s; | |
1016 | DestroyAndReopen(options); | |
1017 | ||
1018 | ASSERT_OK(Put(Key(0), "va;")); | |
1019 | ASSERT_OK(Put(Key(2), "va;")); | |
1020 | s = Flush(); | |
1e59de90 | 1021 | ASSERT_OK(s); |
20effc67 TL |
1022 | |
1023 | listener->OverrideBGError( | |
1024 | Status(Status::NoSpace(), Status::Severity::kHardError)); | |
1025 | listener->EnableAutoRecovery(false); | |
1026 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
1027 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
1028 | "BackgroundCallCompaction:0"}}); | |
1029 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
1030 | "BackgroundCallCompaction:0", [&](void*) { | |
1031 | fault_fs_->SetFilesystemActive(false, | |
1032 | IOStatus::NoSpace("Out of space")); | |
1033 | }); | |
1034 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
1035 | ||
1036 | ASSERT_OK(Put(Key(1), "val")); | |
1037 | s = Flush(); | |
1e59de90 | 1038 | ASSERT_OK(s); |
20effc67 TL |
1039 | |
1040 | s = dbfull()->TEST_WaitForCompact(); | |
1041 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
1042 | ||
1043 | fault_fs_->SetFilesystemActive(true); | |
1044 | s = dbfull()->Resume(); | |
1e59de90 | 1045 | ASSERT_OK(s); |
20effc67 TL |
1046 | Destroy(options); |
1047 | } | |
1048 | ||
1e59de90 | 1049 | TEST_F(DBErrorHandlingFSTest, DISABLED_CompactionWriteRetryableError) { |
20effc67 TL |
1050 | std::shared_ptr<ErrorHandlerFSListener> listener( |
1051 | new ErrorHandlerFSListener()); | |
1052 | Options options = GetDefaultOptions(); | |
1053 | options.env = fault_env_.get(); | |
1054 | options.create_if_missing = true; | |
1055 | options.level0_file_num_compaction_trigger = 2; | |
1056 | options.listeners.emplace_back(listener); | |
1057 | options.max_bgerror_resume_count = 0; | |
1058 | Status s; | |
1059 | DestroyAndReopen(options); | |
1060 | ||
1061 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
1062 | error_msg.SetRetryable(true); | |
1063 | ||
1064 | ASSERT_OK(Put(Key(0), "va;")); | |
1065 | ASSERT_OK(Put(Key(2), "va;")); | |
1066 | s = Flush(); | |
1e59de90 | 1067 | ASSERT_OK(s); |
20effc67 TL |
1068 | |
1069 | listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError)); | |
1070 | listener->EnableAutoRecovery(false); | |
1071 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
1072 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
1073 | "BackgroundCallCompaction:0"}}); | |
1074 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
1075 | "CompactionJob::OpenCompactionOutputFile", | |
1076 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
1e59de90 TL |
1077 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( |
1078 | "DBImpl::BackgroundCompaction:Finish", | |
1079 | [&](void*) { CancelAllBackgroundWork(dbfull()); }); | |
20effc67 TL |
1080 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); |
1081 | ||
1082 | ASSERT_OK(Put(Key(1), "val")); | |
1083 | s = Flush(); | |
1e59de90 | 1084 | ASSERT_OK(s); |
20effc67 | 1085 | |
1e59de90 TL |
1086 | s = dbfull()->TEST_GetBGError(); |
1087 | ASSERT_OK(s); | |
1088 | fault_fs_->SetFilesystemActive(true); | |
1089 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
1090 | SyncPoint::GetInstance()->DisableProcessing(); | |
1091 | s = dbfull()->Resume(); | |
1092 | ASSERT_OK(s); | |
1093 | Destroy(options); | |
1094 | } | |
1095 | ||
1096 | TEST_F(DBErrorHandlingFSTest, DISABLED_CompactionWriteFileScopeError) { | |
1097 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
1098 | new ErrorHandlerFSListener()); | |
1099 | Options options = GetDefaultOptions(); | |
1100 | options.env = fault_env_.get(); | |
1101 | options.create_if_missing = true; | |
1102 | options.level0_file_num_compaction_trigger = 2; | |
1103 | options.listeners.emplace_back(listener); | |
1104 | options.max_bgerror_resume_count = 0; | |
1105 | Status s; | |
1106 | DestroyAndReopen(options); | |
1107 | ||
1108 | IOStatus error_msg = IOStatus::IOError("File Scope Data Loss Error"); | |
1109 | error_msg.SetDataLoss(true); | |
1110 | error_msg.SetScope( | |
1111 | ROCKSDB_NAMESPACE::IOStatus::IOErrorScope::kIOErrorScopeFile); | |
1112 | error_msg.SetRetryable(false); | |
1113 | ||
1114 | ASSERT_OK(Put(Key(0), "va;")); | |
1115 | ASSERT_OK(Put(Key(2), "va;")); | |
1116 | s = Flush(); | |
1117 | ASSERT_OK(s); | |
1118 | ||
1119 | listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError)); | |
1120 | listener->EnableAutoRecovery(false); | |
1121 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
1122 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
1123 | "BackgroundCallCompaction:0"}}); | |
1124 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
1125 | "CompactionJob::OpenCompactionOutputFile", | |
1126 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
1127 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
1128 | "DBImpl::BackgroundCompaction:Finish", | |
1129 | [&](void*) { CancelAllBackgroundWork(dbfull()); }); | |
1130 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
1131 | ||
1132 | ASSERT_OK(Put(Key(1), "val")); | |
1133 | s = Flush(); | |
1134 | ASSERT_OK(s); | |
1135 | ||
1136 | s = dbfull()->TEST_GetBGError(); | |
1137 | ASSERT_OK(s); | |
20effc67 TL |
1138 | |
1139 | fault_fs_->SetFilesystemActive(true); | |
1140 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
1141 | SyncPoint::GetInstance()->DisableProcessing(); | |
1142 | s = dbfull()->Resume(); | |
1e59de90 | 1143 | ASSERT_OK(s); |
20effc67 TL |
1144 | Destroy(options); |
1145 | } | |
1146 | ||
1147 | TEST_F(DBErrorHandlingFSTest, CorruptionError) { | |
1148 | Options options = GetDefaultOptions(); | |
1149 | options.env = fault_env_.get(); | |
1150 | options.create_if_missing = true; | |
1151 | options.level0_file_num_compaction_trigger = 2; | |
1152 | Status s; | |
1153 | DestroyAndReopen(options); | |
1154 | ||
1155 | ASSERT_OK(Put(Key(0), "va;")); | |
1156 | ASSERT_OK(Put(Key(2), "va;")); | |
1157 | s = Flush(); | |
1e59de90 | 1158 | ASSERT_OK(s); |
20effc67 TL |
1159 | |
1160 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
1161 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
1162 | "BackgroundCallCompaction:0"}}); | |
1163 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
1164 | "BackgroundCallCompaction:0", [&](void*) { | |
1165 | fault_fs_->SetFilesystemActive(false, | |
1166 | IOStatus::Corruption("Corruption")); | |
1167 | }); | |
1168 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
1169 | ||
1170 | ASSERT_OK(Put(Key(1), "val")); | |
1171 | s = Flush(); | |
1e59de90 | 1172 | ASSERT_OK(s); |
20effc67 TL |
1173 | |
1174 | s = dbfull()->TEST_WaitForCompact(); | |
1175 | ASSERT_EQ(s.severity(), | |
1176 | ROCKSDB_NAMESPACE::Status::Severity::kUnrecoverableError); | |
1177 | ||
1178 | fault_fs_->SetFilesystemActive(true); | |
1179 | s = dbfull()->Resume(); | |
1e59de90 | 1180 | ASSERT_NOK(s); |
20effc67 TL |
1181 | Destroy(options); |
1182 | } | |
1183 | ||
1184 | TEST_F(DBErrorHandlingFSTest, AutoRecoverFlushError) { | |
1185 | if (mem_env_ != nullptr) { | |
1186 | ROCKSDB_GTEST_SKIP("Test requires non-mock environment"); | |
1187 | return; | |
1188 | } | |
1189 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
1190 | new ErrorHandlerFSListener()); | |
1191 | Options options = GetDefaultOptions(); | |
1192 | options.env = fault_env_.get(); | |
1193 | options.create_if_missing = true; | |
1194 | options.listeners.emplace_back(listener); | |
1e59de90 | 1195 | options.statistics = CreateDBStatistics(); |
20effc67 TL |
1196 | Status s; |
1197 | ||
1198 | listener->EnableAutoRecovery(); | |
1199 | DestroyAndReopen(options); | |
1200 | ||
1201 | ASSERT_OK(Put(Key(0), "val")); | |
1202 | SyncPoint::GetInstance()->SetCallBack("FlushJob::Start", [&](void*) { | |
1203 | fault_fs_->SetFilesystemActive(false, IOStatus::NoSpace("Out of space")); | |
1204 | }); | |
1205 | SyncPoint::GetInstance()->EnableProcessing(); | |
1206 | s = Flush(); | |
1207 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
1208 | SyncPoint::GetInstance()->DisableProcessing(); | |
1209 | fault_fs_->SetFilesystemActive(true); | |
1210 | ASSERT_EQ(listener->WaitForRecovery(5000000), true); | |
1211 | ||
1212 | s = Put(Key(1), "val"); | |
1e59de90 TL |
1213 | ASSERT_OK(s); |
1214 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
1215 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
1216 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
1217 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
1218 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
1219 | ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT)); | |
1220 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
1221 | ERROR_HANDLER_AUTORESUME_COUNT)); | |
1222 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
1223 | ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT)); | |
1224 | ASSERT_EQ(0, options.statistics->getAndResetTickerCount( | |
1225 | ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT)); | |
20effc67 TL |
1226 | |
1227 | Reopen(options); | |
1228 | ASSERT_EQ("val", Get(Key(0))); | |
1229 | ASSERT_EQ("val", Get(Key(1))); | |
1230 | Destroy(options); | |
1231 | } | |
1232 | ||
1233 | TEST_F(DBErrorHandlingFSTest, FailRecoverFlushError) { | |
1234 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
1235 | new ErrorHandlerFSListener()); | |
1236 | Options options = GetDefaultOptions(); | |
1237 | options.env = fault_env_.get(); | |
1238 | options.create_if_missing = true; | |
1239 | options.listeners.emplace_back(listener); | |
1240 | Status s; | |
1241 | ||
1242 | listener->EnableAutoRecovery(); | |
1243 | DestroyAndReopen(options); | |
1244 | ||
1245 | ASSERT_OK(Put(Key(0), "val")); | |
1246 | SyncPoint::GetInstance()->SetCallBack("FlushJob::Start", [&](void*) { | |
1247 | fault_fs_->SetFilesystemActive(false, IOStatus::NoSpace("Out of space")); | |
1248 | }); | |
1249 | SyncPoint::GetInstance()->EnableProcessing(); | |
1250 | s = Flush(); | |
1251 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
1252 | // We should be able to shutdown the database while auto recovery is going | |
1253 | // on in the background | |
1254 | Close(); | |
1e59de90 | 1255 | DestroyDB(dbname_, options).PermitUncheckedError(); |
20effc67 TL |
1256 | } |
1257 | ||
1258 | TEST_F(DBErrorHandlingFSTest, WALWriteError) { | |
1259 | if (mem_env_ != nullptr) { | |
1260 | ROCKSDB_GTEST_SKIP("Test requires non-mock environment"); | |
1261 | return; | |
1262 | } | |
1263 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
1264 | new ErrorHandlerFSListener()); | |
1265 | Options options = GetDefaultOptions(); | |
1266 | options.env = fault_env_.get(); | |
1267 | options.create_if_missing = true; | |
1268 | options.writable_file_max_buffer_size = 32768; | |
1269 | options.listeners.emplace_back(listener); | |
1270 | Status s; | |
1271 | Random rnd(301); | |
1272 | ||
1273 | listener->EnableAutoRecovery(); | |
1274 | DestroyAndReopen(options); | |
1275 | ||
1276 | { | |
1277 | WriteBatch batch; | |
1278 | ||
1279 | for (auto i = 0; i < 100; ++i) { | |
1280 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
1281 | } | |
1282 | ||
1283 | WriteOptions wopts; | |
1284 | wopts.sync = true; | |
1e59de90 | 1285 | ASSERT_OK(dbfull()->Write(wopts, &batch)); |
20effc67 TL |
1286 | }; |
1287 | ||
1288 | { | |
1289 | WriteBatch batch; | |
1290 | int write_error = 0; | |
1291 | ||
1292 | for (auto i = 100; i < 199; ++i) { | |
1293 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
1294 | } | |
1295 | ||
1296 | SyncPoint::GetInstance()->SetCallBack( | |
1297 | "WritableFileWriter::Append:BeforePrepareWrite", [&](void*) { | |
1298 | write_error++; | |
1299 | if (write_error > 2) { | |
1300 | fault_fs_->SetFilesystemActive(false, | |
1301 | IOStatus::NoSpace("Out of space")); | |
1302 | } | |
1303 | }); | |
1304 | SyncPoint::GetInstance()->EnableProcessing(); | |
1305 | WriteOptions wopts; | |
1306 | wopts.sync = true; | |
1307 | s = dbfull()->Write(wopts, &batch); | |
1308 | ASSERT_EQ(s, s.NoSpace()); | |
1309 | } | |
1310 | SyncPoint::GetInstance()->DisableProcessing(); | |
1e59de90 TL |
1311 | // `ClearAllCallBacks()` is needed in addition to `DisableProcessing()` to |
1312 | // drain all callbacks. Otherwise, a pending callback in the background | |
1313 | // could re-disable `fault_fs_` after we enable it below. | |
1314 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
20effc67 TL |
1315 | fault_fs_->SetFilesystemActive(true); |
1316 | ASSERT_EQ(listener->WaitForRecovery(5000000), true); | |
1317 | for (auto i = 0; i < 199; ++i) { | |
1318 | if (i < 100) { | |
1319 | ASSERT_NE(Get(Key(i)), "NOT_FOUND"); | |
1320 | } else { | |
1321 | ASSERT_EQ(Get(Key(i)), "NOT_FOUND"); | |
1322 | } | |
1323 | } | |
1324 | Reopen(options); | |
1325 | for (auto i = 0; i < 199; ++i) { | |
1326 | if (i < 100) { | |
1327 | ASSERT_NE(Get(Key(i)), "NOT_FOUND"); | |
1328 | } else { | |
1329 | ASSERT_EQ(Get(Key(i)), "NOT_FOUND"); | |
1330 | } | |
1331 | } | |
1332 | Close(); | |
1333 | } | |
1334 | ||
1335 | TEST_F(DBErrorHandlingFSTest, WALWriteRetryableError) { | |
1336 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
1337 | new ErrorHandlerFSListener()); | |
1338 | Options options = GetDefaultOptions(); | |
1339 | options.env = fault_env_.get(); | |
1340 | options.create_if_missing = true; | |
1341 | options.writable_file_max_buffer_size = 32768; | |
1342 | options.listeners.emplace_back(listener); | |
1343 | options.paranoid_checks = true; | |
1344 | options.max_bgerror_resume_count = 0; | |
20effc67 TL |
1345 | Random rnd(301); |
1346 | ||
1347 | DestroyAndReopen(options); | |
1348 | ||
1349 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
1350 | error_msg.SetRetryable(true); | |
1351 | ||
1352 | // For the first batch, write is successful, require sync | |
1353 | { | |
1354 | WriteBatch batch; | |
1355 | ||
1356 | for (auto i = 0; i < 100; ++i) { | |
1357 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
1358 | } | |
1359 | ||
1360 | WriteOptions wopts; | |
1361 | wopts.sync = true; | |
1e59de90 | 1362 | ASSERT_OK(dbfull()->Write(wopts, &batch)); |
20effc67 TL |
1363 | }; |
1364 | ||
1365 | // For the second batch, the first 2 file Append are successful, then the | |
1366 | // following Append fails due to file system retryable IOError. | |
1367 | { | |
1368 | WriteBatch batch; | |
1369 | int write_error = 0; | |
1370 | ||
1371 | for (auto i = 100; i < 200; ++i) { | |
1372 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
1373 | } | |
1374 | ||
1375 | SyncPoint::GetInstance()->SetCallBack( | |
1376 | "WritableFileWriter::Append:BeforePrepareWrite", [&](void*) { | |
1377 | write_error++; | |
1378 | if (write_error > 2) { | |
1379 | fault_fs_->SetFilesystemActive(false, error_msg); | |
1380 | } | |
1381 | }); | |
1382 | SyncPoint::GetInstance()->EnableProcessing(); | |
1383 | WriteOptions wopts; | |
1384 | wopts.sync = true; | |
1e59de90 TL |
1385 | Status s = dbfull()->Write(wopts, &batch); |
1386 | ASSERT_TRUE(s.IsIOError()); | |
20effc67 TL |
1387 | } |
1388 | fault_fs_->SetFilesystemActive(true); | |
1389 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
1390 | SyncPoint::GetInstance()->DisableProcessing(); | |
1391 | ||
1392 | // Data in corrupted WAL are not stored | |
1393 | for (auto i = 0; i < 199; ++i) { | |
1394 | if (i < 100) { | |
1395 | ASSERT_NE(Get(Key(i)), "NOT_FOUND"); | |
1396 | } else { | |
1397 | ASSERT_EQ(Get(Key(i)), "NOT_FOUND"); | |
1398 | } | |
1399 | } | |
1400 | ||
1401 | // Resume and write a new batch, should be in the WAL | |
1e59de90 | 1402 | ASSERT_OK(dbfull()->Resume()); |
20effc67 TL |
1403 | { |
1404 | WriteBatch batch; | |
1405 | ||
1406 | for (auto i = 200; i < 300; ++i) { | |
1407 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
1408 | } | |
1409 | ||
1410 | WriteOptions wopts; | |
1411 | wopts.sync = true; | |
1e59de90 | 1412 | ASSERT_OK(dbfull()->Write(wopts, &batch)); |
20effc67 TL |
1413 | }; |
1414 | ||
1415 | Reopen(options); | |
1416 | for (auto i = 0; i < 300; ++i) { | |
1417 | if (i < 100 || i >= 200) { | |
1418 | ASSERT_NE(Get(Key(i)), "NOT_FOUND"); | |
1419 | } else { | |
1420 | ASSERT_EQ(Get(Key(i)), "NOT_FOUND"); | |
1421 | } | |
1422 | } | |
1423 | Close(); | |
1424 | } | |
1425 | ||
1426 | TEST_F(DBErrorHandlingFSTest, MultiCFWALWriteError) { | |
1427 | if (mem_env_ != nullptr) { | |
1428 | ROCKSDB_GTEST_SKIP("Test requires non-mock environment"); | |
1429 | return; | |
1430 | } | |
1431 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
1432 | new ErrorHandlerFSListener()); | |
1433 | Options options = GetDefaultOptions(); | |
1434 | options.env = fault_env_.get(); | |
1435 | options.create_if_missing = true; | |
1436 | options.writable_file_max_buffer_size = 32768; | |
1437 | options.listeners.emplace_back(listener); | |
20effc67 TL |
1438 | Random rnd(301); |
1439 | ||
1440 | listener->EnableAutoRecovery(); | |
1441 | CreateAndReopenWithCF({"one", "two", "three"}, options); | |
1442 | ||
1443 | { | |
1444 | WriteBatch batch; | |
1445 | ||
1446 | for (auto i = 1; i < 4; ++i) { | |
1447 | for (auto j = 0; j < 100; ++j) { | |
1448 | ASSERT_OK(batch.Put(handles_[i], Key(j), rnd.RandomString(1024))); | |
1449 | } | |
1450 | } | |
1451 | ||
1452 | WriteOptions wopts; | |
1453 | wopts.sync = true; | |
1e59de90 | 1454 | ASSERT_OK(dbfull()->Write(wopts, &batch)); |
20effc67 TL |
1455 | }; |
1456 | ||
1457 | { | |
1458 | WriteBatch batch; | |
1459 | int write_error = 0; | |
1460 | ||
1461 | // Write to one CF | |
1462 | for (auto i = 100; i < 199; ++i) { | |
1463 | ASSERT_OK(batch.Put(handles_[2], Key(i), rnd.RandomString(1024))); | |
1464 | } | |
1465 | ||
1466 | SyncPoint::GetInstance()->SetCallBack( | |
1467 | "WritableFileWriter::Append:BeforePrepareWrite", [&](void*) { | |
1468 | write_error++; | |
1469 | if (write_error > 2) { | |
1470 | fault_fs_->SetFilesystemActive(false, | |
1471 | IOStatus::NoSpace("Out of space")); | |
1472 | } | |
1473 | }); | |
1474 | SyncPoint::GetInstance()->EnableProcessing(); | |
1475 | WriteOptions wopts; | |
1476 | wopts.sync = true; | |
1e59de90 TL |
1477 | Status s = dbfull()->Write(wopts, &batch); |
1478 | ASSERT_TRUE(s.IsNoSpace()); | |
20effc67 TL |
1479 | } |
1480 | SyncPoint::GetInstance()->DisableProcessing(); | |
1e59de90 TL |
1481 | // `ClearAllCallBacks()` is needed in addition to `DisableProcessing()` to |
1482 | // drain all callbacks. Otherwise, a pending callback in the background | |
1483 | // could re-disable `fault_fs_` after we enable it below. | |
1484 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
20effc67 TL |
1485 | fault_fs_->SetFilesystemActive(true); |
1486 | ASSERT_EQ(listener->WaitForRecovery(5000000), true); | |
1487 | ||
1488 | for (auto i = 1; i < 4; ++i) { | |
1489 | // Every CF should have been flushed | |
1490 | ASSERT_EQ(NumTableFilesAtLevel(0, i), 1); | |
1491 | } | |
1492 | ||
1493 | for (auto i = 1; i < 4; ++i) { | |
1494 | for (auto j = 0; j < 199; ++j) { | |
1495 | if (j < 100) { | |
1496 | ASSERT_NE(Get(i, Key(j)), "NOT_FOUND"); | |
1497 | } else { | |
1498 | ASSERT_EQ(Get(i, Key(j)), "NOT_FOUND"); | |
1499 | } | |
1500 | } | |
1501 | } | |
1502 | ReopenWithColumnFamilies({"default", "one", "two", "three"}, options); | |
1503 | for (auto i = 1; i < 4; ++i) { | |
1504 | for (auto j = 0; j < 199; ++j) { | |
1505 | if (j < 100) { | |
1506 | ASSERT_NE(Get(i, Key(j)), "NOT_FOUND"); | |
1507 | } else { | |
1508 | ASSERT_EQ(Get(i, Key(j)), "NOT_FOUND"); | |
1509 | } | |
1510 | } | |
1511 | } | |
1512 | Close(); | |
1513 | } | |
1514 | ||
1515 | TEST_F(DBErrorHandlingFSTest, MultiDBCompactionError) { | |
1516 | if (mem_env_ != nullptr) { | |
1517 | ROCKSDB_GTEST_SKIP("Test requires non-mock environment"); | |
1518 | return; | |
1519 | } | |
1520 | FaultInjectionTestEnv* def_env = new FaultInjectionTestEnv(env_); | |
1521 | std::vector<std::unique_ptr<Env>> fault_envs; | |
1522 | std::vector<FaultInjectionTestFS*> fault_fs; | |
1523 | std::vector<Options> options; | |
1524 | std::vector<std::shared_ptr<ErrorHandlerFSListener>> listener; | |
1525 | std::vector<DB*> db; | |
1526 | std::shared_ptr<SstFileManager> sfm(NewSstFileManager(def_env)); | |
1527 | int kNumDbInstances = 3; | |
1528 | Random rnd(301); | |
1529 | ||
1530 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1531 | listener.emplace_back(new ErrorHandlerFSListener()); | |
1532 | options.emplace_back(GetDefaultOptions()); | |
1533 | fault_fs.emplace_back(new FaultInjectionTestFS(env_->GetFileSystem())); | |
1534 | std::shared_ptr<FileSystem> fs(fault_fs.back()); | |
1535 | fault_envs.emplace_back(new CompositeEnvWrapper(def_env, fs)); | |
1536 | options[i].env = fault_envs.back().get(); | |
1537 | options[i].create_if_missing = true; | |
1538 | options[i].level0_file_num_compaction_trigger = 2; | |
1539 | options[i].writable_file_max_buffer_size = 32768; | |
1540 | options[i].listeners.emplace_back(listener[i]); | |
1541 | options[i].sst_file_manager = sfm; | |
1542 | DB* dbptr; | |
1543 | char buf[16]; | |
1544 | ||
1545 | listener[i]->EnableAutoRecovery(); | |
1546 | // Setup for returning error for the 3rd SST, which would be level 1 | |
1547 | listener[i]->InjectFileCreationError(fault_fs[i], 3, | |
1548 | IOStatus::NoSpace("Out of space")); | |
1549 | snprintf(buf, sizeof(buf), "_%d", i); | |
1e59de90 TL |
1550 | ASSERT_OK(DestroyDB(dbname_ + std::string(buf), options[i])); |
1551 | ASSERT_OK(DB::Open(options[i], dbname_ + std::string(buf), &dbptr)); | |
20effc67 TL |
1552 | db.emplace_back(dbptr); |
1553 | } | |
1554 | ||
1555 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1556 | WriteBatch batch; | |
1557 | ||
1558 | for (auto j = 0; j <= 100; ++j) { | |
1559 | ASSERT_OK(batch.Put(Key(j), rnd.RandomString(1024))); | |
1560 | } | |
1561 | ||
1562 | WriteOptions wopts; | |
1563 | wopts.sync = true; | |
1e59de90 TL |
1564 | ASSERT_OK(db[i]->Write(wopts, &batch)); |
1565 | ASSERT_OK(db[i]->Flush(FlushOptions())); | |
20effc67 TL |
1566 | } |
1567 | ||
1568 | def_env->SetFilesystemActive(false, Status::NoSpace("Out of space")); | |
1569 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1570 | WriteBatch batch; | |
1571 | ||
1572 | // Write to one CF | |
1573 | for (auto j = 100; j < 199; ++j) { | |
1574 | ASSERT_OK(batch.Put(Key(j), rnd.RandomString(1024))); | |
1575 | } | |
1576 | ||
1577 | WriteOptions wopts; | |
1578 | wopts.sync = true; | |
1e59de90 TL |
1579 | ASSERT_OK(db[i]->Write(wopts, &batch)); |
1580 | ASSERT_OK(db[i]->Flush(FlushOptions())); | |
20effc67 TL |
1581 | } |
1582 | ||
1583 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1584 | Status s = static_cast<DBImpl*>(db[i])->TEST_WaitForCompact(true); | |
1585 | ASSERT_EQ(s.severity(), Status::Severity::kSoftError); | |
1586 | fault_fs[i]->SetFilesystemActive(true); | |
1587 | } | |
1588 | ||
1589 | def_env->SetFilesystemActive(true); | |
1590 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1591 | std::string prop; | |
1592 | ASSERT_EQ(listener[i]->WaitForRecovery(5000000), true); | |
1e59de90 | 1593 | ASSERT_OK(static_cast<DBImpl*>(db[i])->TEST_WaitForCompact(true)); |
20effc67 | 1594 | EXPECT_TRUE(db[i]->GetProperty( |
1e59de90 | 1595 | "rocksdb.num-files-at-level" + std::to_string(0), &prop)); |
20effc67 TL |
1596 | EXPECT_EQ(atoi(prop.c_str()), 0); |
1597 | EXPECT_TRUE(db[i]->GetProperty( | |
1e59de90 | 1598 | "rocksdb.num-files-at-level" + std::to_string(1), &prop)); |
20effc67 TL |
1599 | EXPECT_EQ(atoi(prop.c_str()), 1); |
1600 | } | |
1601 | ||
1e59de90 TL |
1602 | SstFileManagerImpl* sfmImpl = |
1603 | static_cast_with_check<SstFileManagerImpl>(sfm.get()); | |
1604 | sfmImpl->Close(); | |
1605 | ||
20effc67 TL |
1606 | for (auto i = 0; i < kNumDbInstances; ++i) { |
1607 | char buf[16]; | |
1608 | snprintf(buf, sizeof(buf), "_%d", i); | |
1609 | delete db[i]; | |
1610 | fault_fs[i]->SetFilesystemActive(true); | |
1611 | if (getenv("KEEP_DB")) { | |
1612 | printf("DB is still at %s%s\n", dbname_.c_str(), buf); | |
1613 | } else { | |
1e59de90 | 1614 | ASSERT_OK(DestroyDB(dbname_ + std::string(buf), options[i])); |
20effc67 TL |
1615 | } |
1616 | } | |
1617 | options.clear(); | |
1618 | sfm.reset(); | |
1619 | delete def_env; | |
1620 | } | |
1621 | ||
1622 | TEST_F(DBErrorHandlingFSTest, MultiDBVariousErrors) { | |
1623 | if (mem_env_ != nullptr) { | |
1624 | ROCKSDB_GTEST_SKIP("Test requires non-mock environment"); | |
1625 | return; | |
1626 | } | |
1627 | FaultInjectionTestEnv* def_env = new FaultInjectionTestEnv(env_); | |
1628 | std::vector<std::unique_ptr<Env>> fault_envs; | |
1629 | std::vector<FaultInjectionTestFS*> fault_fs; | |
1630 | std::vector<Options> options; | |
1631 | std::vector<std::shared_ptr<ErrorHandlerFSListener>> listener; | |
1632 | std::vector<DB*> db; | |
1633 | std::shared_ptr<SstFileManager> sfm(NewSstFileManager(def_env)); | |
1634 | int kNumDbInstances = 3; | |
1635 | Random rnd(301); | |
1636 | ||
1637 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1638 | listener.emplace_back(new ErrorHandlerFSListener()); | |
1639 | options.emplace_back(GetDefaultOptions()); | |
1640 | fault_fs.emplace_back(new FaultInjectionTestFS(env_->GetFileSystem())); | |
1641 | std::shared_ptr<FileSystem> fs(fault_fs.back()); | |
1642 | fault_envs.emplace_back(new CompositeEnvWrapper(def_env, fs)); | |
1643 | options[i].env = fault_envs.back().get(); | |
1644 | options[i].create_if_missing = true; | |
1645 | options[i].level0_file_num_compaction_trigger = 2; | |
1646 | options[i].writable_file_max_buffer_size = 32768; | |
1647 | options[i].listeners.emplace_back(listener[i]); | |
1648 | options[i].sst_file_manager = sfm; | |
1649 | DB* dbptr; | |
1650 | char buf[16]; | |
1651 | ||
1652 | listener[i]->EnableAutoRecovery(); | |
1653 | switch (i) { | |
1654 | case 0: | |
1655 | // Setup for returning error for the 3rd SST, which would be level 1 | |
1656 | listener[i]->InjectFileCreationError(fault_fs[i], 3, | |
1657 | IOStatus::NoSpace("Out of space")); | |
1658 | break; | |
1659 | case 1: | |
1660 | // Setup for returning error after the 1st SST, which would result | |
1661 | // in a hard error | |
1662 | listener[i]->InjectFileCreationError(fault_fs[i], 2, | |
1663 | IOStatus::NoSpace("Out of space")); | |
1664 | break; | |
1665 | default: | |
1666 | break; | |
1667 | } | |
1668 | snprintf(buf, sizeof(buf), "_%d", i); | |
1e59de90 TL |
1669 | ASSERT_OK(DestroyDB(dbname_ + std::string(buf), options[i])); |
1670 | ASSERT_OK(DB::Open(options[i], dbname_ + std::string(buf), &dbptr)); | |
20effc67 TL |
1671 | db.emplace_back(dbptr); |
1672 | } | |
1673 | ||
1674 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1675 | WriteBatch batch; | |
1676 | ||
1677 | for (auto j = 0; j <= 100; ++j) { | |
1678 | ASSERT_OK(batch.Put(Key(j), rnd.RandomString(1024))); | |
1679 | } | |
1680 | ||
1681 | WriteOptions wopts; | |
1682 | wopts.sync = true; | |
1e59de90 TL |
1683 | ASSERT_OK(db[i]->Write(wopts, &batch)); |
1684 | ASSERT_OK(db[i]->Flush(FlushOptions())); | |
20effc67 TL |
1685 | } |
1686 | ||
1687 | def_env->SetFilesystemActive(false, Status::NoSpace("Out of space")); | |
1688 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1689 | WriteBatch batch; | |
1690 | ||
1691 | // Write to one CF | |
1692 | for (auto j = 100; j < 199; ++j) { | |
1693 | ASSERT_OK(batch.Put(Key(j), rnd.RandomString(1024))); | |
1694 | } | |
1695 | ||
1696 | WriteOptions wopts; | |
1697 | wopts.sync = true; | |
1e59de90 | 1698 | ASSERT_OK(db[i]->Write(wopts, &batch)); |
20effc67 | 1699 | if (i != 1) { |
1e59de90 | 1700 | ASSERT_OK(db[i]->Flush(FlushOptions())); |
20effc67 | 1701 | } else { |
1e59de90 | 1702 | ASSERT_TRUE(db[i]->Flush(FlushOptions()).IsNoSpace()); |
20effc67 TL |
1703 | } |
1704 | } | |
1705 | ||
1706 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1707 | Status s = static_cast<DBImpl*>(db[i])->TEST_WaitForCompact(true); | |
1708 | switch (i) { | |
1709 | case 0: | |
1710 | ASSERT_EQ(s.severity(), Status::Severity::kSoftError); | |
1711 | break; | |
1712 | case 1: | |
1713 | ASSERT_EQ(s.severity(), Status::Severity::kHardError); | |
1714 | break; | |
1715 | case 2: | |
1e59de90 | 1716 | ASSERT_OK(s); |
20effc67 TL |
1717 | break; |
1718 | } | |
1719 | fault_fs[i]->SetFilesystemActive(true); | |
1720 | } | |
1721 | ||
1722 | def_env->SetFilesystemActive(true); | |
1723 | for (auto i = 0; i < kNumDbInstances; ++i) { | |
1724 | std::string prop; | |
1725 | if (i < 2) { | |
1726 | ASSERT_EQ(listener[i]->WaitForRecovery(5000000), true); | |
1727 | } | |
1728 | if (i == 1) { | |
1e59de90 | 1729 | ASSERT_OK(static_cast<DBImpl*>(db[i])->TEST_WaitForCompact(true)); |
20effc67 TL |
1730 | } |
1731 | EXPECT_TRUE(db[i]->GetProperty( | |
1e59de90 | 1732 | "rocksdb.num-files-at-level" + std::to_string(0), &prop)); |
20effc67 TL |
1733 | EXPECT_EQ(atoi(prop.c_str()), 0); |
1734 | EXPECT_TRUE(db[i]->GetProperty( | |
1e59de90 | 1735 | "rocksdb.num-files-at-level" + std::to_string(1), &prop)); |
20effc67 TL |
1736 | EXPECT_EQ(atoi(prop.c_str()), 1); |
1737 | } | |
1738 | ||
1e59de90 TL |
1739 | SstFileManagerImpl* sfmImpl = |
1740 | static_cast_with_check<SstFileManagerImpl>(sfm.get()); | |
1741 | sfmImpl->Close(); | |
1742 | ||
20effc67 TL |
1743 | for (auto i = 0; i < kNumDbInstances; ++i) { |
1744 | char buf[16]; | |
1745 | snprintf(buf, sizeof(buf), "_%d", i); | |
1746 | fault_fs[i]->SetFilesystemActive(true); | |
1747 | delete db[i]; | |
1748 | if (getenv("KEEP_DB")) { | |
1749 | printf("DB is still at %s%s\n", dbname_.c_str(), buf); | |
1750 | } else { | |
1e59de90 | 1751 | EXPECT_OK(DestroyDB(dbname_ + std::string(buf), options[i])); |
20effc67 TL |
1752 | } |
1753 | } | |
1754 | options.clear(); | |
1755 | delete def_env; | |
1756 | } | |
1757 | ||
1758 | // When Put the KV-pair, the write option is set to disable WAL. | |
1759 | // If retryable error happens in this condition, map the bg error | |
1760 | // to soft error and trigger auto resume. During auto resume, SwitchMemtable | |
1761 | // is disabled to avoid small SST tables. Write can still be applied before | |
1762 | // the bg error is cleaned unless the memtable is full. | |
1e59de90 | 1763 | TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableErrorAutoRecover1) { |
20effc67 TL |
1764 | // Activate the FS before the first resume |
1765 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
1766 | new ErrorHandlerFSListener()); | |
1767 | Options options = GetDefaultOptions(); | |
1768 | options.env = fault_env_.get(); | |
1769 | options.create_if_missing = true; | |
1770 | options.listeners.emplace_back(listener); | |
1771 | options.max_bgerror_resume_count = 2; | |
1772 | options.bgerror_resume_retry_interval = 100000; // 0.1 second | |
1e59de90 | 1773 | options.statistics = CreateDBStatistics(); |
20effc67 TL |
1774 | Status s; |
1775 | ||
1776 | listener->EnableAutoRecovery(false); | |
1777 | DestroyAndReopen(options); | |
1778 | ||
1779 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
1780 | error_msg.SetRetryable(true); | |
1781 | ||
1782 | WriteOptions wo = WriteOptions(); | |
1783 | wo.disableWAL = true; | |
1784 | ASSERT_OK(Put(Key(1), "val1", wo)); | |
1785 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
1786 | {{"RecoverFromRetryableBGIOError:LoopOut", | |
1787 | "FLushWritNoWALRetryableeErrorAutoRecover1:1"}}); | |
1788 | SyncPoint::GetInstance()->SetCallBack( | |
1789 | "BuildTable:BeforeFinishBuildTable", | |
1790 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
1791 | ||
1792 | SyncPoint::GetInstance()->EnableProcessing(); | |
1793 | s = Flush(); | |
1794 | ASSERT_EQ("val1", Get(Key(1))); | |
1795 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
1796 | TEST_SYNC_POINT("FLushWritNoWALRetryableeErrorAutoRecover1:1"); | |
1797 | ASSERT_EQ("val1", Get(Key(1))); | |
1798 | ASSERT_EQ("val1", Get(Key(1))); | |
1799 | SyncPoint::GetInstance()->DisableProcessing(); | |
1800 | fault_fs_->SetFilesystemActive(true); | |
1e59de90 TL |
1801 | ASSERT_EQ(3, options.statistics->getAndResetTickerCount( |
1802 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
1803 | ASSERT_EQ(3, options.statistics->getAndResetTickerCount( | |
1804 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
1805 | ASSERT_EQ(3, options.statistics->getAndResetTickerCount( | |
1806 | ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT)); | |
1807 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
1808 | ERROR_HANDLER_AUTORESUME_COUNT)); | |
1809 | ASSERT_LE(0, options.statistics->getAndResetTickerCount( | |
1810 | ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT)); | |
1811 | ASSERT_LE(0, options.statistics->getAndResetTickerCount( | |
1812 | ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT)); | |
1813 | HistogramData autoresume_retry; | |
1814 | options.statistics->histogramData(ERROR_HANDLER_AUTORESUME_RETRY_COUNT, | |
1815 | &autoresume_retry); | |
1816 | ASSERT_GE(autoresume_retry.max, 0); | |
20effc67 TL |
1817 | ASSERT_OK(Put(Key(2), "val2", wo)); |
1818 | s = Flush(); | |
1819 | // Since auto resume fails, the bg error is not cleand, flush will | |
1820 | // return the bg_error set before. | |
1821 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
1822 | ASSERT_EQ("val2", Get(Key(2))); | |
1823 | ||
1824 | // call auto resume | |
1e59de90 | 1825 | ASSERT_OK(dbfull()->Resume()); |
20effc67 | 1826 | ASSERT_OK(Put(Key(3), "val3", wo)); |
20effc67 | 1827 | // After resume is successful, the flush should be ok. |
1e59de90 | 1828 | ASSERT_OK(Flush()); |
20effc67 TL |
1829 | ASSERT_EQ("val3", Get(Key(3))); |
1830 | Destroy(options); | |
1831 | } | |
1832 | ||
1e59de90 | 1833 | TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableErrorAutoRecover2) { |
20effc67 TL |
1834 | // Activate the FS before the first resume |
1835 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
1836 | new ErrorHandlerFSListener()); | |
1837 | Options options = GetDefaultOptions(); | |
1838 | options.env = fault_env_.get(); | |
1839 | options.create_if_missing = true; | |
1840 | options.listeners.emplace_back(listener); | |
1841 | options.max_bgerror_resume_count = 2; | |
1842 | options.bgerror_resume_retry_interval = 100000; // 0.1 second | |
1e59de90 | 1843 | options.statistics = CreateDBStatistics(); |
20effc67 TL |
1844 | Status s; |
1845 | ||
1846 | listener->EnableAutoRecovery(false); | |
1847 | DestroyAndReopen(options); | |
1848 | ||
1849 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
1850 | error_msg.SetRetryable(true); | |
1851 | ||
1852 | WriteOptions wo = WriteOptions(); | |
1853 | wo.disableWAL = true; | |
1854 | ASSERT_OK(Put(Key(1), "val1", wo)); | |
1855 | SyncPoint::GetInstance()->SetCallBack( | |
1856 | "BuildTable:BeforeFinishBuildTable", | |
1857 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
1858 | ||
1859 | SyncPoint::GetInstance()->EnableProcessing(); | |
1860 | s = Flush(); | |
1861 | ASSERT_EQ("val1", Get(Key(1))); | |
1e59de90 TL |
1862 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); |
1863 | SyncPoint::GetInstance()->DisableProcessing(); | |
1864 | fault_fs_->SetFilesystemActive(true); | |
1865 | ASSERT_EQ(listener->WaitForRecovery(5000000), true); | |
20effc67 | 1866 | ASSERT_EQ("val1", Get(Key(1))); |
1e59de90 TL |
1867 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( |
1868 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
1869 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
1870 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
1871 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
1872 | ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT)); | |
1873 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
1874 | ERROR_HANDLER_AUTORESUME_COUNT)); | |
1875 | ASSERT_LE(0, options.statistics->getAndResetTickerCount( | |
1876 | ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT)); | |
1877 | ASSERT_LE(0, options.statistics->getAndResetTickerCount( | |
1878 | ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT)); | |
1879 | HistogramData autoresume_retry; | |
1880 | options.statistics->histogramData(ERROR_HANDLER_AUTORESUME_RETRY_COUNT, | |
1881 | &autoresume_retry); | |
1882 | ASSERT_GE(autoresume_retry.max, 0); | |
1883 | ASSERT_OK(Put(Key(2), "val2", wo)); | |
20effc67 | 1884 | s = Flush(); |
1e59de90 TL |
1885 | // Since auto resume is successful, the bg error is cleaned, flush will |
1886 | // be successful. | |
1887 | ASSERT_OK(s); | |
20effc67 | 1888 | ASSERT_EQ("val2", Get(Key(2))); |
20effc67 TL |
1889 | Destroy(options); |
1890 | } | |
1891 | ||
1e59de90 TL |
1892 | // Auto resume fromt the flush retryable IO error. Activate the FS before the |
1893 | // first resume. Resume is successful | |
1894 | TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAutoRecover1) { | |
1895 | // Activate the FS before the first resume | |
20effc67 TL |
1896 | std::shared_ptr<ErrorHandlerFSListener> listener( |
1897 | new ErrorHandlerFSListener()); | |
1898 | Options options = GetDefaultOptions(); | |
1899 | options.env = fault_env_.get(); | |
1900 | options.create_if_missing = true; | |
1901 | options.listeners.emplace_back(listener); | |
1902 | options.max_bgerror_resume_count = 2; | |
1e59de90 | 1903 | options.bgerror_resume_retry_interval = 100000; // 0.1 second |
20effc67 TL |
1904 | Status s; |
1905 | ||
1906 | listener->EnableAutoRecovery(false); | |
1907 | DestroyAndReopen(options); | |
1908 | ||
1909 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
1910 | error_msg.SetRetryable(true); | |
20effc67 TL |
1911 | |
1912 | ASSERT_OK(Put(Key(1), "val1")); | |
20effc67 TL |
1913 | SyncPoint::GetInstance()->SetCallBack( |
1914 | "BuildTable:BeforeFinishBuildTable", | |
1915 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
20effc67 TL |
1916 | |
1917 | SyncPoint::GetInstance()->EnableProcessing(); | |
1918 | s = Flush(); | |
1e59de90 | 1919 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); |
20effc67 TL |
1920 | SyncPoint::GetInstance()->DisableProcessing(); |
1921 | fault_fs_->SetFilesystemActive(true); | |
1e59de90 | 1922 | ASSERT_EQ(listener->WaitForRecovery(5000000), true); |
20effc67 | 1923 | |
1e59de90 | 1924 | ASSERT_EQ("val1", Get(Key(1))); |
20effc67 TL |
1925 | Reopen(options); |
1926 | ASSERT_EQ("val1", Get(Key(1))); | |
1927 | ASSERT_OK(Put(Key(2), "val2")); | |
1e59de90 | 1928 | ASSERT_OK(Flush()); |
20effc67 TL |
1929 | ASSERT_EQ("val2", Get(Key(2))); |
1930 | ||
1931 | Destroy(options); | |
1932 | } | |
1933 | ||
1e59de90 TL |
1934 | // Auto resume fromt the flush retryable IO error and set the retry limit count. |
1935 | // Never activate the FS and auto resume should fail at the end | |
1936 | TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAutoRecover2) { | |
1937 | // Fail all the resume and let user to resume | |
20effc67 TL |
1938 | std::shared_ptr<ErrorHandlerFSListener> listener( |
1939 | new ErrorHandlerFSListener()); | |
1940 | Options options = GetDefaultOptions(); | |
1941 | options.env = fault_env_.get(); | |
1942 | options.create_if_missing = true; | |
1943 | options.listeners.emplace_back(listener); | |
1944 | options.max_bgerror_resume_count = 2; | |
1e59de90 | 1945 | options.bgerror_resume_retry_interval = 100000; // 0.1 second |
20effc67 TL |
1946 | Status s; |
1947 | ||
1948 | listener->EnableAutoRecovery(false); | |
1949 | DestroyAndReopen(options); | |
1950 | ||
1951 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
1952 | error_msg.SetRetryable(true); | |
1953 | ||
1954 | ASSERT_OK(Put(Key(1), "val1")); | |
1955 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
1e59de90 TL |
1956 | {{"FLushWritRetryableeErrorAutoRecover2:0", |
1957 | "RecoverFromRetryableBGIOError:BeforeStart"}, | |
1958 | {"RecoverFromRetryableBGIOError:LoopOut", | |
1959 | "FLushWritRetryableeErrorAutoRecover2:1"}}); | |
20effc67 TL |
1960 | SyncPoint::GetInstance()->SetCallBack( |
1961 | "BuildTable:BeforeFinishBuildTable", | |
1962 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
1963 | SyncPoint::GetInstance()->EnableProcessing(); | |
1964 | s = Flush(); | |
1e59de90 TL |
1965 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); |
1966 | TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover2:0"); | |
1967 | TEST_SYNC_POINT("FLushWritRetryableeErrorAutoRecover2:1"); | |
1968 | fault_fs_->SetFilesystemActive(true); | |
20effc67 TL |
1969 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks(); |
1970 | SyncPoint::GetInstance()->DisableProcessing(); | |
20effc67 | 1971 | |
1e59de90 TL |
1972 | ASSERT_EQ("val1", Get(Key(1))); |
1973 | // Auto resume fails due to FS does not recover during resume. User call | |
1974 | // resume manually here. | |
1975 | s = dbfull()->Resume(); | |
1976 | ASSERT_EQ("val1", Get(Key(1))); | |
1977 | ASSERT_OK(s); | |
20effc67 | 1978 | ASSERT_OK(Put(Key(2), "val2")); |
1e59de90 | 1979 | ASSERT_OK(Flush()); |
20effc67 TL |
1980 | ASSERT_EQ("val2", Get(Key(2))); |
1981 | ||
1982 | Destroy(options); | |
1983 | } | |
1984 | ||
1e59de90 TL |
1985 | // Auto resume fromt the flush retryable IO error and set the retry limit count. |
1986 | // Fail the first resume and let the second resume be successful. | |
1987 | TEST_F(DBErrorHandlingFSTest, ManifestWriteRetryableErrorAutoRecover) { | |
1988 | // Fail the first resume and let the second resume be successful | |
20effc67 TL |
1989 | std::shared_ptr<ErrorHandlerFSListener> listener( |
1990 | new ErrorHandlerFSListener()); | |
1991 | Options options = GetDefaultOptions(); | |
1992 | options.env = fault_env_.get(); | |
1993 | options.create_if_missing = true; | |
1994 | options.listeners.emplace_back(listener); | |
1995 | options.max_bgerror_resume_count = 2; | |
1e59de90 | 1996 | options.bgerror_resume_retry_interval = 100000; // 0.1 second |
20effc67 | 1997 | Status s; |
1e59de90 TL |
1998 | std::string old_manifest; |
1999 | std::string new_manifest; | |
20effc67 TL |
2000 | |
2001 | listener->EnableAutoRecovery(false); | |
2002 | DestroyAndReopen(options); | |
1e59de90 | 2003 | old_manifest = GetManifestNameFromLiveFiles(); |
20effc67 TL |
2004 | |
2005 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
2006 | error_msg.SetRetryable(true); | |
2007 | ||
1e59de90 TL |
2008 | ASSERT_OK(Put(Key(0), "val")); |
2009 | ASSERT_OK(Flush()); | |
2010 | ASSERT_OK(Put(Key(1), "val")); | |
20effc67 | 2011 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( |
1e59de90 TL |
2012 | {{"RecoverFromRetryableBGIOError:BeforeStart", |
2013 | "ManifestWriteRetryableErrorAutoRecover:0"}, | |
2014 | {"ManifestWriteRetryableErrorAutoRecover:1", | |
20effc67 | 2015 | "RecoverFromRetryableBGIOError:BeforeWait1"}, |
1e59de90 TL |
2016 | {"RecoverFromRetryableBGIOError:RecoverSuccess", |
2017 | "ManifestWriteRetryableErrorAutoRecover:2"}}); | |
20effc67 | 2018 | SyncPoint::GetInstance()->SetCallBack( |
1e59de90 | 2019 | "VersionSet::LogAndApply:WriteManifest", |
20effc67 TL |
2020 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); |
2021 | SyncPoint::GetInstance()->EnableProcessing(); | |
2022 | s = Flush(); | |
1e59de90 TL |
2023 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); |
2024 | TEST_SYNC_POINT("ManifestWriteRetryableErrorAutoRecover:0"); | |
20effc67 TL |
2025 | fault_fs_->SetFilesystemActive(true); |
2026 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks(); | |
1e59de90 TL |
2027 | TEST_SYNC_POINT("ManifestWriteRetryableErrorAutoRecover:1"); |
2028 | TEST_SYNC_POINT("ManifestWriteRetryableErrorAutoRecover:2"); | |
20effc67 TL |
2029 | SyncPoint::GetInstance()->DisableProcessing(); |
2030 | ||
1e59de90 TL |
2031 | new_manifest = GetManifestNameFromLiveFiles(); |
2032 | ASSERT_NE(new_manifest, old_manifest); | |
20effc67 | 2033 | |
1e59de90 TL |
2034 | Reopen(options); |
2035 | ASSERT_EQ("val", Get(Key(0))); | |
2036 | ASSERT_EQ("val", Get(Key(1))); | |
2037 | Close(); | |
20effc67 TL |
2038 | } |
2039 | ||
1e59de90 | 2040 | TEST_F(DBErrorHandlingFSTest, ManifestWriteNoWALRetryableErrorAutoRecover) { |
20effc67 TL |
2041 | // Fail the first resume and let the second resume be successful |
2042 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2043 | new ErrorHandlerFSListener()); | |
2044 | Options options = GetDefaultOptions(); | |
2045 | options.env = fault_env_.get(); | |
2046 | options.create_if_missing = true; | |
2047 | options.listeners.emplace_back(listener); | |
2048 | options.max_bgerror_resume_count = 2; | |
2049 | options.bgerror_resume_retry_interval = 100000; // 0.1 second | |
2050 | Status s; | |
2051 | std::string old_manifest; | |
2052 | std::string new_manifest; | |
2053 | ||
2054 | listener->EnableAutoRecovery(false); | |
2055 | DestroyAndReopen(options); | |
2056 | old_manifest = GetManifestNameFromLiveFiles(); | |
2057 | ||
2058 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
2059 | error_msg.SetRetryable(true); | |
2060 | ||
1e59de90 TL |
2061 | WriteOptions wo = WriteOptions(); |
2062 | wo.disableWAL = true; | |
2063 | ASSERT_OK(Put(Key(0), "val", wo)); | |
20effc67 | 2064 | ASSERT_OK(Flush()); |
1e59de90 | 2065 | ASSERT_OK(Put(Key(1), "val", wo)); |
20effc67 TL |
2066 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( |
2067 | {{"RecoverFromRetryableBGIOError:BeforeStart", | |
1e59de90 TL |
2068 | "ManifestWriteNoWALRetryableErrorAutoRecover:0"}, |
2069 | {"ManifestWriteNoWALRetryableErrorAutoRecover:1", | |
20effc67 TL |
2070 | "RecoverFromRetryableBGIOError:BeforeWait1"}, |
2071 | {"RecoverFromRetryableBGIOError:RecoverSuccess", | |
1e59de90 | 2072 | "ManifestWriteNoWALRetryableErrorAutoRecover:2"}}); |
20effc67 TL |
2073 | SyncPoint::GetInstance()->SetCallBack( |
2074 | "VersionSet::LogAndApply:WriteManifest", | |
2075 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
2076 | SyncPoint::GetInstance()->EnableProcessing(); | |
2077 | s = Flush(); | |
1e59de90 TL |
2078 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); |
2079 | TEST_SYNC_POINT("ManifestWriteNoWALRetryableErrorAutoRecover:0"); | |
20effc67 TL |
2080 | fault_fs_->SetFilesystemActive(true); |
2081 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks(); | |
1e59de90 TL |
2082 | TEST_SYNC_POINT("ManifestWriteNoWALRetryableErrorAutoRecover:1"); |
2083 | TEST_SYNC_POINT("ManifestWriteNoWALRetryableErrorAutoRecover:2"); | |
20effc67 TL |
2084 | SyncPoint::GetInstance()->DisableProcessing(); |
2085 | ||
2086 | new_manifest = GetManifestNameFromLiveFiles(); | |
2087 | ASSERT_NE(new_manifest, old_manifest); | |
2088 | ||
2089 | Reopen(options); | |
2090 | ASSERT_EQ("val", Get(Key(0))); | |
2091 | ASSERT_EQ("val", Get(Key(1))); | |
2092 | Close(); | |
2093 | } | |
2094 | ||
2095 | TEST_F(DBErrorHandlingFSTest, | |
2096 | CompactionManifestWriteRetryableErrorAutoRecover) { | |
2097 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2098 | new ErrorHandlerFSListener()); | |
2099 | Options options = GetDefaultOptions(); | |
2100 | options.env = fault_env_.get(); | |
2101 | options.create_if_missing = true; | |
2102 | options.level0_file_num_compaction_trigger = 2; | |
2103 | options.listeners.emplace_back(listener); | |
2104 | options.max_bgerror_resume_count = 2; | |
2105 | options.bgerror_resume_retry_interval = 100000; // 0.1 second | |
2106 | Status s; | |
2107 | std::string old_manifest; | |
2108 | std::string new_manifest; | |
2109 | std::atomic<bool> fail_manifest(false); | |
2110 | DestroyAndReopen(options); | |
2111 | old_manifest = GetManifestNameFromLiveFiles(); | |
2112 | ||
2113 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
2114 | error_msg.SetRetryable(true); | |
2115 | ||
2116 | ASSERT_OK(Put(Key(0), "val")); | |
2117 | ASSERT_OK(Put(Key(2), "val")); | |
1e59de90 | 2118 | ASSERT_OK(Flush()); |
20effc67 TL |
2119 | |
2120 | listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError)); | |
2121 | listener->EnableAutoRecovery(false); | |
2122 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
2123 | // Wait for flush of 2nd L0 file before starting compaction | |
2124 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
2125 | "BackgroundCallCompaction:0"}, | |
2126 | // Wait for compaction to detect manifest write error | |
2127 | {"BackgroundCallCompaction:1", "CompactionManifestWriteErrorAR:0"}, | |
2128 | // Make compaction thread wait for error to be cleared | |
2129 | {"CompactionManifestWriteErrorAR:1", | |
2130 | "DBImpl::BackgroundCallCompaction:FoundObsoleteFiles"}, | |
2131 | {"CompactionManifestWriteErrorAR:2", | |
2132 | "RecoverFromRetryableBGIOError:BeforeStart"}, | |
2133 | // Fail the first resume, before the wait in resume | |
2134 | {"RecoverFromRetryableBGIOError:BeforeResume0", | |
2135 | "CompactionManifestWriteErrorAR:3"}, | |
2136 | // Activate the FS before the second resume | |
2137 | {"CompactionManifestWriteErrorAR:4", | |
2138 | "RecoverFromRetryableBGIOError:BeforeResume1"}, | |
2139 | // Wait the auto resume be sucessful | |
2140 | {"RecoverFromRetryableBGIOError:RecoverSuccess", | |
2141 | "CompactionManifestWriteErrorAR:5"}}); | |
2142 | // trigger manifest write failure in compaction thread | |
2143 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
2144 | "BackgroundCallCompaction:0", [&](void*) { fail_manifest.store(true); }); | |
2145 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
2146 | "VersionSet::LogAndApply:WriteManifest", [&](void*) { | |
2147 | if (fail_manifest.load()) { | |
2148 | fault_fs_->SetFilesystemActive(false, error_msg); | |
2149 | } | |
2150 | }); | |
2151 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
2152 | ||
2153 | ASSERT_OK(Put(Key(1), "val")); | |
2154 | s = Flush(); | |
1e59de90 | 2155 | ASSERT_OK(s); |
20effc67 TL |
2156 | |
2157 | TEST_SYNC_POINT("CompactionManifestWriteErrorAR:0"); | |
2158 | TEST_SYNC_POINT("CompactionManifestWriteErrorAR:1"); | |
2159 | ||
2160 | s = dbfull()->TEST_WaitForCompact(); | |
2161 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
2162 | TEST_SYNC_POINT("CompactionManifestWriteErrorAR:2"); | |
2163 | TEST_SYNC_POINT("CompactionManifestWriteErrorAR:3"); | |
2164 | fault_fs_->SetFilesystemActive(true); | |
2165 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
2166 | TEST_SYNC_POINT("CompactionManifestWriteErrorAR:4"); | |
2167 | TEST_SYNC_POINT("CompactionManifestWriteErrorAR:5"); | |
2168 | SyncPoint::GetInstance()->DisableProcessing(); | |
2169 | ||
2170 | new_manifest = GetManifestNameFromLiveFiles(); | |
2171 | ASSERT_NE(new_manifest, old_manifest); | |
2172 | ||
2173 | Reopen(options); | |
2174 | ASSERT_EQ("val", Get(Key(0))); | |
2175 | ASSERT_EQ("val", Get(Key(1))); | |
2176 | ASSERT_EQ("val", Get(Key(2))); | |
2177 | Close(); | |
2178 | } | |
2179 | ||
2180 | TEST_F(DBErrorHandlingFSTest, CompactionWriteRetryableErrorAutoRecover) { | |
2181 | // In this test, in the first round of compaction, the FS is set to error. | |
2182 | // So the first compaction fails due to retryable IO error and it is mapped | |
2183 | // to soft error. Then, compaction is rescheduled, in the second round of | |
2184 | // compaction, the FS is set to active and compaction is successful, so | |
2185 | // the test will hit the CompactionJob::FinishCompactionOutputFile1 sync | |
2186 | // point. | |
2187 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2188 | new ErrorHandlerFSListener()); | |
2189 | Options options = GetDefaultOptions(); | |
2190 | options.env = fault_env_.get(); | |
2191 | options.create_if_missing = true; | |
2192 | options.level0_file_num_compaction_trigger = 2; | |
2193 | options.listeners.emplace_back(listener); | |
2194 | Status s; | |
2195 | std::atomic<bool> fail_first(false); | |
2196 | std::atomic<bool> fail_second(true); | |
2197 | DestroyAndReopen(options); | |
2198 | ||
2199 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
2200 | error_msg.SetRetryable(true); | |
2201 | ||
2202 | ASSERT_OK(Put(Key(0), "va;")); | |
2203 | ASSERT_OK(Put(Key(2), "va;")); | |
2204 | s = Flush(); | |
1e59de90 | 2205 | ASSERT_OK(s); |
20effc67 TL |
2206 | |
2207 | listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError)); | |
2208 | listener->EnableAutoRecovery(false); | |
2209 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
2210 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
2211 | "BackgroundCallCompaction:0"}, | |
2212 | {"CompactionJob::FinishCompactionOutputFile1", | |
2213 | "CompactionWriteRetryableErrorAutoRecover0"}}); | |
2214 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
2215 | "DBImpl::BackgroundCompaction:Start", | |
2216 | [&](void*) { fault_fs_->SetFilesystemActive(true); }); | |
2217 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
2218 | "BackgroundCallCompaction:0", [&](void*) { fail_first.store(true); }); | |
2219 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
2220 | "CompactionJob::OpenCompactionOutputFile", [&](void*) { | |
2221 | if (fail_first.load() && fail_second.load()) { | |
2222 | fault_fs_->SetFilesystemActive(false, error_msg); | |
2223 | fail_second.store(false); | |
2224 | } | |
2225 | }); | |
2226 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
2227 | ||
2228 | ASSERT_OK(Put(Key(1), "val")); | |
2229 | s = Flush(); | |
1e59de90 | 2230 | ASSERT_OK(s); |
20effc67 TL |
2231 | |
2232 | s = dbfull()->TEST_WaitForCompact(); | |
1e59de90 | 2233 | ASSERT_OK(s); |
20effc67 TL |
2234 | TEST_SYNC_POINT("CompactionWriteRetryableErrorAutoRecover0"); |
2235 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
2236 | SyncPoint::GetInstance()->DisableProcessing(); | |
2237 | Destroy(options); | |
2238 | } | |
2239 | ||
2240 | TEST_F(DBErrorHandlingFSTest, WALWriteRetryableErrorAutoRecover1) { | |
2241 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2242 | new ErrorHandlerFSListener()); | |
2243 | Options options = GetDefaultOptions(); | |
2244 | options.env = fault_env_.get(); | |
2245 | options.create_if_missing = true; | |
2246 | options.writable_file_max_buffer_size = 32768; | |
2247 | options.listeners.emplace_back(listener); | |
2248 | options.paranoid_checks = true; | |
2249 | options.max_bgerror_resume_count = 2; | |
2250 | options.bgerror_resume_retry_interval = 100000; // 0.1 second | |
2251 | Status s; | |
2252 | Random rnd(301); | |
2253 | ||
2254 | DestroyAndReopen(options); | |
2255 | ||
2256 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
2257 | error_msg.SetRetryable(true); | |
2258 | ||
2259 | // For the first batch, write is successful, require sync | |
2260 | { | |
2261 | WriteBatch batch; | |
2262 | ||
2263 | for (auto i = 0; i < 100; ++i) { | |
2264 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
2265 | } | |
2266 | ||
2267 | WriteOptions wopts; | |
2268 | wopts.sync = true; | |
1e59de90 | 2269 | ASSERT_OK(dbfull()->Write(wopts, &batch)); |
20effc67 TL |
2270 | }; |
2271 | ||
2272 | // For the second batch, the first 2 file Append are successful, then the | |
2273 | // following Append fails due to file system retryable IOError. | |
2274 | { | |
2275 | WriteBatch batch; | |
2276 | int write_error = 0; | |
2277 | ||
2278 | for (auto i = 100; i < 200; ++i) { | |
2279 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
2280 | } | |
2281 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
1e59de90 TL |
2282 | {{"WALWriteErrorDone", "RecoverFromRetryableBGIOError:BeforeStart"}, |
2283 | {"RecoverFromRetryableBGIOError:BeforeResume0", "WALWriteError1:0"}, | |
20effc67 TL |
2284 | {"WALWriteError1:1", "RecoverFromRetryableBGIOError:BeforeResume1"}, |
2285 | {"RecoverFromRetryableBGIOError:RecoverSuccess", "WALWriteError1:2"}}); | |
2286 | ||
2287 | SyncPoint::GetInstance()->SetCallBack( | |
2288 | "WritableFileWriter::Append:BeforePrepareWrite", [&](void*) { | |
2289 | write_error++; | |
2290 | if (write_error > 2) { | |
2291 | fault_fs_->SetFilesystemActive(false, error_msg); | |
2292 | } | |
2293 | }); | |
2294 | SyncPoint::GetInstance()->EnableProcessing(); | |
2295 | WriteOptions wopts; | |
2296 | wopts.sync = true; | |
2297 | s = dbfull()->Write(wopts, &batch); | |
2298 | ASSERT_EQ(true, s.IsIOError()); | |
1e59de90 | 2299 | TEST_SYNC_POINT("WALWriteErrorDone"); |
20effc67 TL |
2300 | |
2301 | TEST_SYNC_POINT("WALWriteError1:0"); | |
2302 | fault_fs_->SetFilesystemActive(true); | |
2303 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
2304 | TEST_SYNC_POINT("WALWriteError1:1"); | |
2305 | TEST_SYNC_POINT("WALWriteError1:2"); | |
2306 | } | |
2307 | SyncPoint::GetInstance()->DisableProcessing(); | |
2308 | ||
2309 | // Data in corrupted WAL are not stored | |
2310 | for (auto i = 0; i < 199; ++i) { | |
2311 | if (i < 100) { | |
2312 | ASSERT_NE(Get(Key(i)), "NOT_FOUND"); | |
2313 | } else { | |
2314 | ASSERT_EQ(Get(Key(i)), "NOT_FOUND"); | |
2315 | } | |
2316 | } | |
2317 | ||
2318 | // Resume and write a new batch, should be in the WAL | |
2319 | { | |
2320 | WriteBatch batch; | |
2321 | ||
2322 | for (auto i = 200; i < 300; ++i) { | |
2323 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
2324 | } | |
2325 | ||
2326 | WriteOptions wopts; | |
2327 | wopts.sync = true; | |
1e59de90 | 2328 | ASSERT_OK(dbfull()->Write(wopts, &batch)); |
20effc67 TL |
2329 | }; |
2330 | ||
2331 | Reopen(options); | |
2332 | for (auto i = 0; i < 300; ++i) { | |
2333 | if (i < 100 || i >= 200) { | |
2334 | ASSERT_NE(Get(Key(i)), "NOT_FOUND"); | |
2335 | } else { | |
2336 | ASSERT_EQ(Get(Key(i)), "NOT_FOUND"); | |
2337 | } | |
2338 | } | |
2339 | Close(); | |
2340 | } | |
2341 | ||
2342 | TEST_F(DBErrorHandlingFSTest, WALWriteRetryableErrorAutoRecover2) { | |
2343 | // Fail the first recover and try second time. | |
2344 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2345 | new ErrorHandlerFSListener()); | |
2346 | Options options = GetDefaultOptions(); | |
2347 | options.env = fault_env_.get(); | |
2348 | options.create_if_missing = true; | |
2349 | options.writable_file_max_buffer_size = 32768; | |
2350 | options.listeners.emplace_back(listener); | |
2351 | options.paranoid_checks = true; | |
2352 | options.max_bgerror_resume_count = 2; | |
2353 | options.bgerror_resume_retry_interval = 100000; // 0.1 second | |
2354 | Status s; | |
2355 | Random rnd(301); | |
2356 | ||
2357 | DestroyAndReopen(options); | |
2358 | ||
2359 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
2360 | error_msg.SetRetryable(true); | |
2361 | ||
2362 | // For the first batch, write is successful, require sync | |
2363 | { | |
2364 | WriteBatch batch; | |
2365 | ||
2366 | for (auto i = 0; i < 100; ++i) { | |
2367 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
2368 | } | |
2369 | ||
2370 | WriteOptions wopts; | |
2371 | wopts.sync = true; | |
1e59de90 | 2372 | ASSERT_OK(dbfull()->Write(wopts, &batch)); |
20effc67 TL |
2373 | }; |
2374 | ||
2375 | // For the second batch, the first 2 file Append are successful, then the | |
2376 | // following Append fails due to file system retryable IOError. | |
2377 | { | |
2378 | WriteBatch batch; | |
2379 | int write_error = 0; | |
2380 | ||
2381 | for (auto i = 100; i < 200; ++i) { | |
2382 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
2383 | } | |
2384 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
2385 | {{"RecoverFromRetryableBGIOError:BeforeWait0", "WALWriteError2:0"}, | |
2386 | {"WALWriteError2:1", "RecoverFromRetryableBGIOError:BeforeWait1"}, | |
2387 | {"RecoverFromRetryableBGIOError:RecoverSuccess", "WALWriteError2:2"}}); | |
2388 | ||
2389 | SyncPoint::GetInstance()->SetCallBack( | |
2390 | "WritableFileWriter::Append:BeforePrepareWrite", [&](void*) { | |
2391 | write_error++; | |
2392 | if (write_error > 2) { | |
2393 | fault_fs_->SetFilesystemActive(false, error_msg); | |
2394 | } | |
2395 | }); | |
2396 | SyncPoint::GetInstance()->EnableProcessing(); | |
2397 | WriteOptions wopts; | |
2398 | wopts.sync = true; | |
2399 | s = dbfull()->Write(wopts, &batch); | |
2400 | ASSERT_EQ(true, s.IsIOError()); | |
2401 | ||
2402 | TEST_SYNC_POINT("WALWriteError2:0"); | |
2403 | fault_fs_->SetFilesystemActive(true); | |
2404 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
2405 | TEST_SYNC_POINT("WALWriteError2:1"); | |
2406 | TEST_SYNC_POINT("WALWriteError2:2"); | |
2407 | } | |
2408 | SyncPoint::GetInstance()->DisableProcessing(); | |
2409 | ||
2410 | // Data in corrupted WAL are not stored | |
2411 | for (auto i = 0; i < 199; ++i) { | |
2412 | if (i < 100) { | |
2413 | ASSERT_NE(Get(Key(i)), "NOT_FOUND"); | |
2414 | } else { | |
2415 | ASSERT_EQ(Get(Key(i)), "NOT_FOUND"); | |
2416 | } | |
2417 | } | |
2418 | ||
2419 | // Resume and write a new batch, should be in the WAL | |
2420 | { | |
2421 | WriteBatch batch; | |
2422 | ||
2423 | for (auto i = 200; i < 300; ++i) { | |
2424 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
2425 | } | |
2426 | ||
2427 | WriteOptions wopts; | |
2428 | wopts.sync = true; | |
1e59de90 | 2429 | ASSERT_OK(dbfull()->Write(wopts, &batch)); |
20effc67 TL |
2430 | }; |
2431 | ||
2432 | Reopen(options); | |
2433 | for (auto i = 0; i < 300; ++i) { | |
2434 | if (i < 100 || i >= 200) { | |
2435 | ASSERT_NE(Get(Key(i)), "NOT_FOUND"); | |
2436 | } else { | |
2437 | ASSERT_EQ(Get(Key(i)), "NOT_FOUND"); | |
2438 | } | |
2439 | } | |
2440 | Close(); | |
2441 | } | |
2442 | ||
1e59de90 TL |
2443 | // Fail auto resume from a flush retryable error and verify that |
2444 | // OnErrorRecoveryEnd listener callback is called | |
2445 | TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAbortRecovery) { | |
2446 | // Activate the FS before the first resume | |
2447 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2448 | new ErrorHandlerFSListener()); | |
2449 | Options options = GetDefaultOptions(); | |
2450 | options.env = fault_env_.get(); | |
2451 | options.create_if_missing = true; | |
2452 | options.listeners.emplace_back(listener); | |
2453 | options.max_bgerror_resume_count = 2; | |
2454 | options.bgerror_resume_retry_interval = 100000; // 0.1 second | |
2455 | Status s; | |
2456 | ||
2457 | listener->EnableAutoRecovery(false); | |
2458 | DestroyAndReopen(options); | |
2459 | ||
2460 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
2461 | error_msg.SetRetryable(true); | |
2462 | ||
2463 | ASSERT_OK(Put(Key(1), "val1")); | |
2464 | SyncPoint::GetInstance()->SetCallBack( | |
2465 | "BuildTable:BeforeFinishBuildTable", | |
2466 | [&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); }); | |
2467 | ||
2468 | SyncPoint::GetInstance()->EnableProcessing(); | |
2469 | s = Flush(); | |
2470 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
2471 | ASSERT_EQ(listener->WaitForRecovery(5000000), true); | |
2472 | ASSERT_EQ(listener->new_bg_error(), Status::Aborted()); | |
2473 | SyncPoint::GetInstance()->DisableProcessing(); | |
2474 | fault_fs_->SetFilesystemActive(true); | |
2475 | ||
2476 | Destroy(options); | |
2477 | } | |
2478 | ||
2479 | TEST_F(DBErrorHandlingFSTest, FlushReadError) { | |
2480 | std::shared_ptr<ErrorHandlerFSListener> listener = | |
2481 | std::make_shared<ErrorHandlerFSListener>(); | |
2482 | Options options = GetDefaultOptions(); | |
2483 | options.env = fault_env_.get(); | |
2484 | options.create_if_missing = true; | |
2485 | options.listeners.emplace_back(listener); | |
2486 | options.statistics = CreateDBStatistics(); | |
2487 | Status s; | |
2488 | ||
2489 | listener->EnableAutoRecovery(false); | |
2490 | DestroyAndReopen(options); | |
2491 | ||
2492 | ASSERT_OK(Put(Key(0), "val")); | |
2493 | SyncPoint::GetInstance()->SetCallBack( | |
2494 | "BuildTable:BeforeOutputValidation", [&](void*) { | |
2495 | IOStatus st = IOStatus::IOError(); | |
2496 | st.SetRetryable(true); | |
2497 | st.SetScope(IOStatus::IOErrorScope::kIOErrorScopeFile); | |
2498 | fault_fs_->SetFilesystemActive(false, st); | |
2499 | }); | |
2500 | SyncPoint::GetInstance()->SetCallBack( | |
2501 | "BuildTable:BeforeDeleteFile", | |
2502 | [&](void*) { fault_fs_->SetFilesystemActive(true, IOStatus::OK()); }); | |
2503 | SyncPoint::GetInstance()->EnableProcessing(); | |
2504 | s = Flush(); | |
2505 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
2506 | SyncPoint::GetInstance()->DisableProcessing(); | |
2507 | fault_fs_->SetFilesystemActive(true); | |
2508 | ASSERT_EQ(listener->WaitForRecovery(5000000), true); | |
2509 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
2510 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
2511 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
2512 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
2513 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
2514 | ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT)); | |
2515 | ASSERT_LE(1, options.statistics->getAndResetTickerCount( | |
2516 | ERROR_HANDLER_AUTORESUME_COUNT)); | |
2517 | ASSERT_LE(0, options.statistics->getAndResetTickerCount( | |
2518 | ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT)); | |
2519 | s = dbfull()->TEST_GetBGError(); | |
2520 | ASSERT_OK(s); | |
2521 | ||
2522 | Reopen(GetDefaultOptions()); | |
2523 | ASSERT_EQ("val", Get(Key(0))); | |
2524 | } | |
2525 | ||
2526 | TEST_F(DBErrorHandlingFSTest, AtomicFlushReadError) { | |
2527 | std::shared_ptr<ErrorHandlerFSListener> listener = | |
2528 | std::make_shared<ErrorHandlerFSListener>(); | |
2529 | Options options = GetDefaultOptions(); | |
2530 | options.env = fault_env_.get(); | |
2531 | options.create_if_missing = true; | |
2532 | options.listeners.emplace_back(listener); | |
2533 | options.statistics = CreateDBStatistics(); | |
2534 | Status s; | |
2535 | ||
2536 | listener->EnableAutoRecovery(false); | |
2537 | options.atomic_flush = true; | |
2538 | CreateAndReopenWithCF({"pikachu"}, options); | |
2539 | ||
2540 | ASSERT_OK(Put(0, Key(0), "val")); | |
2541 | ASSERT_OK(Put(1, Key(0), "val")); | |
2542 | SyncPoint::GetInstance()->SetCallBack( | |
2543 | "BuildTable:BeforeOutputValidation", [&](void*) { | |
2544 | IOStatus st = IOStatus::IOError(); | |
2545 | st.SetRetryable(true); | |
2546 | st.SetScope(IOStatus::IOErrorScope::kIOErrorScopeFile); | |
2547 | fault_fs_->SetFilesystemActive(false, st); | |
2548 | }); | |
2549 | SyncPoint::GetInstance()->SetCallBack( | |
2550 | "BuildTable:BeforeDeleteFile", | |
2551 | [&](void*) { fault_fs_->SetFilesystemActive(true, IOStatus::OK()); }); | |
2552 | SyncPoint::GetInstance()->EnableProcessing(); | |
2553 | s = Flush({0, 1}); | |
2554 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError); | |
2555 | SyncPoint::GetInstance()->DisableProcessing(); | |
2556 | fault_fs_->SetFilesystemActive(true); | |
2557 | ASSERT_EQ(listener->WaitForRecovery(5000000), true); | |
2558 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
2559 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
2560 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
2561 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
2562 | ASSERT_EQ(1, options.statistics->getAndResetTickerCount( | |
2563 | ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT)); | |
2564 | ASSERT_LE(1, options.statistics->getAndResetTickerCount( | |
2565 | ERROR_HANDLER_AUTORESUME_COUNT)); | |
2566 | ASSERT_LE(0, options.statistics->getAndResetTickerCount( | |
2567 | ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT)); | |
2568 | s = dbfull()->TEST_GetBGError(); | |
2569 | ASSERT_OK(s); | |
2570 | ||
2571 | TryReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, | |
2572 | GetDefaultOptions()); | |
2573 | ASSERT_EQ("val", Get(Key(0))); | |
2574 | } | |
2575 | ||
2576 | TEST_F(DBErrorHandlingFSTest, AtomicFlushNoSpaceError) { | |
2577 | std::shared_ptr<ErrorHandlerFSListener> listener = | |
2578 | std::make_shared<ErrorHandlerFSListener>(); | |
2579 | Options options = GetDefaultOptions(); | |
2580 | options.env = fault_env_.get(); | |
2581 | options.create_if_missing = true; | |
2582 | options.listeners.emplace_back(listener); | |
2583 | options.statistics = CreateDBStatistics(); | |
2584 | Status s; | |
2585 | ||
2586 | listener->EnableAutoRecovery(true); | |
2587 | options.atomic_flush = true; | |
2588 | CreateAndReopenWithCF({"pikachu"}, options); | |
2589 | ||
2590 | ASSERT_OK(Put(0, Key(0), "val")); | |
2591 | ASSERT_OK(Put(1, Key(0), "val")); | |
2592 | SyncPoint::GetInstance()->SetCallBack("BuildTable:create_file", [&](void*) { | |
2593 | IOStatus st = IOStatus::NoSpace(); | |
2594 | fault_fs_->SetFilesystemActive(false, st); | |
2595 | }); | |
2596 | SyncPoint::GetInstance()->SetCallBack( | |
2597 | "BuildTable:BeforeDeleteFile", | |
2598 | [&](void*) { fault_fs_->SetFilesystemActive(true, IOStatus::OK()); }); | |
2599 | SyncPoint::GetInstance()->EnableProcessing(); | |
2600 | s = Flush({0, 1}); | |
2601 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError); | |
2602 | SyncPoint::GetInstance()->DisableProcessing(); | |
2603 | fault_fs_->SetFilesystemActive(true); | |
2604 | ASSERT_EQ(listener->WaitForRecovery(5000000), true); | |
2605 | ASSERT_LE(1, options.statistics->getAndResetTickerCount( | |
2606 | ERROR_HANDLER_BG_ERROR_COUNT)); | |
2607 | ASSERT_LE(1, options.statistics->getAndResetTickerCount( | |
2608 | ERROR_HANDLER_BG_IO_ERROR_COUNT)); | |
2609 | s = dbfull()->TEST_GetBGError(); | |
2610 | ASSERT_OK(s); | |
2611 | ||
2612 | TryReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, | |
2613 | GetDefaultOptions()); | |
2614 | ASSERT_EQ("val", Get(Key(0))); | |
2615 | } | |
2616 | ||
2617 | TEST_F(DBErrorHandlingFSTest, CompactionReadRetryableErrorAutoRecover) { | |
2618 | // In this test, in the first round of compaction, the FS is set to error. | |
2619 | // So the first compaction fails due to retryable IO error and it is mapped | |
2620 | // to soft error. Then, compaction is rescheduled, in the second round of | |
2621 | // compaction, the FS is set to active and compaction is successful, so | |
2622 | // the test will hit the CompactionJob::FinishCompactionOutputFile1 sync | |
2623 | // point. | |
2624 | std::shared_ptr<ErrorHandlerFSListener> listener = | |
2625 | std::make_shared<ErrorHandlerFSListener>(); | |
2626 | Options options = GetDefaultOptions(); | |
2627 | options.env = fault_env_.get(); | |
2628 | options.create_if_missing = true; | |
2629 | options.level0_file_num_compaction_trigger = 2; | |
2630 | options.listeners.emplace_back(listener); | |
2631 | BlockBasedTableOptions table_options; | |
2632 | table_options.no_block_cache = true; | |
2633 | options.table_factory.reset(NewBlockBasedTableFactory(table_options)); | |
2634 | Status s; | |
2635 | std::atomic<bool> fail_first(false); | |
2636 | std::atomic<bool> fail_second(true); | |
2637 | Random rnd(301); | |
2638 | DestroyAndReopen(options); | |
2639 | ||
2640 | IOStatus error_msg = IOStatus::IOError("Retryable IO Error"); | |
2641 | error_msg.SetRetryable(true); | |
2642 | ||
2643 | for (int i = 0; i < 100; ++i) { | |
2644 | ASSERT_OK(Put(Key(i), rnd.RandomString(1024))); | |
2645 | } | |
2646 | s = Flush(); | |
2647 | ASSERT_OK(s); | |
2648 | ||
2649 | listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError)); | |
2650 | listener->EnableAutoRecovery(false); | |
2651 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
2652 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
2653 | "BackgroundCallCompaction:0"}, | |
2654 | {"CompactionJob::FinishCompactionOutputFile1", | |
2655 | "CompactionWriteRetryableErrorAutoRecover0"}}); | |
2656 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
2657 | "DBImpl::BackgroundCompaction:Start", | |
2658 | [&](void*) { fault_fs_->SetFilesystemActive(true); }); | |
2659 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
2660 | "BackgroundCallCompaction:0", [&](void*) { fail_first.store(true); }); | |
2661 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
2662 | "CompactionJob::Run():PausingManualCompaction:2", [&](void*) { | |
2663 | if (fail_first.load() && fail_second.load()) { | |
2664 | fault_fs_->SetFilesystemActive(false, error_msg); | |
2665 | fail_second.store(false); | |
2666 | } | |
2667 | }); | |
2668 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
2669 | ||
2670 | ASSERT_OK(Put(Key(1), "val")); | |
2671 | s = Flush(); | |
2672 | ASSERT_OK(s); | |
2673 | ||
2674 | s = dbfull()->TEST_WaitForCompact(); | |
2675 | ASSERT_OK(s); | |
2676 | TEST_SYNC_POINT("CompactionWriteRetryableErrorAutoRecover0"); | |
2677 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
2678 | SyncPoint::GetInstance()->DisableProcessing(); | |
2679 | ||
2680 | Reopen(GetDefaultOptions()); | |
2681 | } | |
2682 | ||
20effc67 TL |
2683 | class DBErrorHandlingFencingTest : public DBErrorHandlingFSTest, |
2684 | public testing::WithParamInterface<bool> {}; | |
2685 | ||
2686 | TEST_P(DBErrorHandlingFencingTest, FLushWriteFenced) { | |
2687 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2688 | new ErrorHandlerFSListener()); | |
2689 | Options options = GetDefaultOptions(); | |
2690 | options.env = fault_env_.get(); | |
2691 | options.create_if_missing = true; | |
2692 | options.listeners.emplace_back(listener); | |
2693 | options.paranoid_checks = GetParam(); | |
2694 | Status s; | |
2695 | ||
2696 | listener->EnableAutoRecovery(true); | |
2697 | DestroyAndReopen(options); | |
2698 | ||
2699 | ASSERT_OK(Put(Key(0), "val")); | |
2700 | SyncPoint::GetInstance()->SetCallBack("FlushJob::Start", [&](void*) { | |
2701 | fault_fs_->SetFilesystemActive(false, IOStatus::IOFenced("IO fenced")); | |
2702 | }); | |
2703 | SyncPoint::GetInstance()->EnableProcessing(); | |
2704 | s = Flush(); | |
2705 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError); | |
2706 | ASSERT_TRUE(s.IsIOFenced()); | |
2707 | SyncPoint::GetInstance()->DisableProcessing(); | |
2708 | fault_fs_->SetFilesystemActive(true); | |
2709 | s = dbfull()->Resume(); | |
2710 | ASSERT_TRUE(s.IsIOFenced()); | |
2711 | Destroy(options); | |
2712 | } | |
2713 | ||
2714 | TEST_P(DBErrorHandlingFencingTest, ManifestWriteFenced) { | |
2715 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2716 | new ErrorHandlerFSListener()); | |
2717 | Options options = GetDefaultOptions(); | |
2718 | options.env = fault_env_.get(); | |
2719 | options.create_if_missing = true; | |
2720 | options.listeners.emplace_back(listener); | |
2721 | options.paranoid_checks = GetParam(); | |
2722 | Status s; | |
2723 | std::string old_manifest; | |
2724 | std::string new_manifest; | |
2725 | ||
2726 | listener->EnableAutoRecovery(true); | |
2727 | DestroyAndReopen(options); | |
2728 | old_manifest = GetManifestNameFromLiveFiles(); | |
2729 | ||
2730 | ASSERT_OK(Put(Key(0), "val")); | |
1e59de90 | 2731 | ASSERT_OK(Flush()); |
20effc67 TL |
2732 | ASSERT_OK(Put(Key(1), "val")); |
2733 | SyncPoint::GetInstance()->SetCallBack( | |
2734 | "VersionSet::LogAndApply:WriteManifest", [&](void*) { | |
2735 | fault_fs_->SetFilesystemActive(false, IOStatus::IOFenced("IO fenced")); | |
2736 | }); | |
2737 | SyncPoint::GetInstance()->EnableProcessing(); | |
2738 | s = Flush(); | |
2739 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError); | |
2740 | ASSERT_TRUE(s.IsIOFenced()); | |
2741 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
2742 | SyncPoint::GetInstance()->DisableProcessing(); | |
2743 | fault_fs_->SetFilesystemActive(true); | |
2744 | s = dbfull()->Resume(); | |
2745 | ASSERT_TRUE(s.IsIOFenced()); | |
2746 | Close(); | |
2747 | } | |
2748 | ||
2749 | TEST_P(DBErrorHandlingFencingTest, CompactionWriteFenced) { | |
2750 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2751 | new ErrorHandlerFSListener()); | |
2752 | Options options = GetDefaultOptions(); | |
2753 | options.env = fault_env_.get(); | |
2754 | options.create_if_missing = true; | |
2755 | options.level0_file_num_compaction_trigger = 2; | |
2756 | options.listeners.emplace_back(listener); | |
2757 | options.paranoid_checks = GetParam(); | |
2758 | Status s; | |
2759 | DestroyAndReopen(options); | |
2760 | ||
2761 | ASSERT_OK(Put(Key(0), "va;")); | |
2762 | ASSERT_OK(Put(Key(2), "va;")); | |
2763 | s = Flush(); | |
1e59de90 | 2764 | ASSERT_OK(s); |
20effc67 TL |
2765 | |
2766 | listener->EnableAutoRecovery(true); | |
2767 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency( | |
2768 | {{"DBImpl::FlushMemTable:FlushMemTableFinished", | |
2769 | "BackgroundCallCompaction:0"}}); | |
2770 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
2771 | "BackgroundCallCompaction:0", [&](void*) { | |
2772 | fault_fs_->SetFilesystemActive(false, IOStatus::IOFenced("IO fenced")); | |
2773 | }); | |
2774 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
2775 | ||
2776 | ASSERT_OK(Put(Key(1), "val")); | |
2777 | s = Flush(); | |
1e59de90 | 2778 | ASSERT_OK(s); |
20effc67 TL |
2779 | |
2780 | s = dbfull()->TEST_WaitForCompact(); | |
2781 | ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError); | |
2782 | ASSERT_TRUE(s.IsIOFenced()); | |
2783 | ||
2784 | fault_fs_->SetFilesystemActive(true); | |
2785 | s = dbfull()->Resume(); | |
2786 | ASSERT_TRUE(s.IsIOFenced()); | |
2787 | Destroy(options); | |
2788 | } | |
2789 | ||
2790 | TEST_P(DBErrorHandlingFencingTest, WALWriteFenced) { | |
2791 | std::shared_ptr<ErrorHandlerFSListener> listener( | |
2792 | new ErrorHandlerFSListener()); | |
2793 | Options options = GetDefaultOptions(); | |
2794 | options.env = fault_env_.get(); | |
2795 | options.create_if_missing = true; | |
2796 | options.writable_file_max_buffer_size = 32768; | |
2797 | options.listeners.emplace_back(listener); | |
2798 | options.paranoid_checks = GetParam(); | |
2799 | Status s; | |
2800 | Random rnd(301); | |
2801 | ||
2802 | listener->EnableAutoRecovery(true); | |
2803 | DestroyAndReopen(options); | |
2804 | ||
2805 | { | |
2806 | WriteBatch batch; | |
2807 | ||
2808 | for (auto i = 0; i < 100; ++i) { | |
2809 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
2810 | } | |
2811 | ||
2812 | WriteOptions wopts; | |
2813 | wopts.sync = true; | |
1e59de90 | 2814 | ASSERT_OK(dbfull()->Write(wopts, &batch)); |
20effc67 TL |
2815 | }; |
2816 | ||
2817 | { | |
2818 | WriteBatch batch; | |
2819 | int write_error = 0; | |
2820 | ||
2821 | for (auto i = 100; i < 199; ++i) { | |
2822 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
2823 | } | |
2824 | ||
2825 | SyncPoint::GetInstance()->SetCallBack( | |
2826 | "WritableFileWriter::Append:BeforePrepareWrite", [&](void*) { | |
2827 | write_error++; | |
2828 | if (write_error > 2) { | |
2829 | fault_fs_->SetFilesystemActive(false, | |
2830 | IOStatus::IOFenced("IO fenced")); | |
2831 | } | |
2832 | }); | |
2833 | SyncPoint::GetInstance()->EnableProcessing(); | |
2834 | WriteOptions wopts; | |
2835 | wopts.sync = true; | |
2836 | s = dbfull()->Write(wopts, &batch); | |
2837 | ASSERT_TRUE(s.IsIOFenced()); | |
2838 | } | |
2839 | SyncPoint::GetInstance()->DisableProcessing(); | |
2840 | fault_fs_->SetFilesystemActive(true); | |
2841 | { | |
2842 | WriteBatch batch; | |
2843 | ||
2844 | for (auto i = 0; i < 100; ++i) { | |
2845 | ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024))); | |
2846 | } | |
2847 | ||
2848 | WriteOptions wopts; | |
2849 | wopts.sync = true; | |
2850 | s = dbfull()->Write(wopts, &batch); | |
2851 | ASSERT_TRUE(s.IsIOFenced()); | |
2852 | } | |
2853 | Close(); | |
2854 | } | |
2855 | ||
2856 | INSTANTIATE_TEST_CASE_P(DBErrorHandlingFSTest, DBErrorHandlingFencingTest, | |
2857 | ::testing::Bool()); | |
2858 | ||
2859 | } // namespace ROCKSDB_NAMESPACE | |
2860 | ||
2861 | int main(int argc, char** argv) { | |
2862 | ROCKSDB_NAMESPACE::port::InstallStackTraceHandler(); | |
2863 | ::testing::InitGoogleTest(&argc, argv); | |
2864 | return RUN_ALL_TESTS(); | |
2865 | } | |
2866 | ||
2867 | #else | |
2868 | #include <stdio.h> | |
2869 | ||
2870 | int main(int /*argc*/, char** /*argv*/) { | |
2871 | fprintf(stderr, "SKIPPED as Cuckoo table is not supported in ROCKSDB_LITE\n"); | |
2872 | return 0; | |
2873 | } | |
2874 | ||
2875 | #endif // ROCKSDB_LITE |