| 2279 | } |
| 2280 | |
| 2281 | ACTOR static Future<Void> recover(DWALPager* self) { |
| 2282 | ASSERT(!self->recoverFuture.isValid()); |
| 2283 | |
| 2284 | state bool exists = false; |
| 2285 | |
| 2286 | if (!self->memoryOnly) { |
| 2287 | int64_t flags = IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_UNBUFFERED | IAsyncFile::OPEN_READWRITE | |
| 2288 | IAsyncFile::OPEN_LOCK; |
| 2289 | exists = fileExists(self->filename); |
| 2290 | if (!exists) { |
| 2291 | flags |= IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE | IAsyncFile::OPEN_CREATE; |
| 2292 | } |
| 2293 | wait(store(self->pageFile, IAsyncFileSystem::filesystem()->open(self->filename, flags, 0644))); |
| 2294 | } |
| 2295 | |
| 2296 | // Header page is always treated as having a page size of smallestPhysicalBlock |
| 2297 | self->setPageSize(smallestPhysicalBlock); |
| 2298 | |
| 2299 | state int64_t fileSize = 0; |
| 2300 | if (exists) { |
| 2301 | wait(store(fileSize, self->pageFile->size())); |
| 2302 | } |
| 2303 | |
| 2304 | self->fileExtension = Void(); |
| 2305 | |
| 2306 | debug_printf( |
| 2307 | "DWALPager(%s) recover exists=%d fileSize=%" PRId64 "\n", self->filename.c_str(), exists, fileSize); |
| 2308 | // TODO: If the file exists but appears to never have been successfully committed is this an error or |
| 2309 | // should recovery proceed with a new pager instance? |
| 2310 | |
| 2311 | // If there are at least 2 pages then try to recover the existing file |
| 2312 | if (exists && fileSize >= (self->smallestPhysicalBlock * 2)) { |
| 2313 | debug_printf("DWALPager(%s) recovering using existing file\n", self->filename.c_str()); |
| 2314 | |
| 2315 | state bool recoveredBackupHeader = false; |
| 2316 | |
| 2317 | // Try to read primary header |
| 2318 | try { |
| 2319 | wait(store(self->headerPage, self->readHeaderPage(primaryHeaderPageID))); |
| 2320 | } catch (Error& e) { |
| 2321 | debug_printf("DWALPager(%s) Primary header read failed with %s\n", self->filename.c_str(), e.what()); |
| 2322 | |
| 2323 | // Errors that can be caused by a corrupted and unsync'd write to the header page can be ignored and the |
| 2324 | // committed, sync'd backup header will be tried. Notably, page_header_wrong_page_id is not included in |
| 2325 | // this list because it means the header checksum passed but this page data is not meant to be at this |
| 2326 | // location, which is a very serious error so recovery should not continue. |
| 2327 | bool tryBackupHeader = |
| 2328 | (e.code() == error_code_page_header_version_not_supported || |
| 2329 | e.code() == error_code_page_encoding_not_supported || |
| 2330 | e.code() == error_code_page_decoding_failed || e.code() == error_code_page_header_checksum_failed); |
| 2331 | |
| 2332 | TraceEvent(SevWarn, "RedwoodRecoveryErrorPrimaryHeaderFailed") |
| 2333 | .errorUnsuppressed(e) |
| 2334 | .detail("Filename", self->filename) |
| 2335 | .detail("PageID", primaryHeaderPageID) |
| 2336 | .detail("TryingBackupHeader", tryBackupHeader); |
| 2337 | |
| 2338 | // Don't throw if trying backup header below |
no test coverage detected