-
Notifications
You must be signed in to change notification settings - Fork 5k
Feat/336/wal-recover #35200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.3.6
Are you sure you want to change the base?
Feat/336/wal-recover #35200
Changes from all commits
50b5164
4eeeb7b
8be4d82
1f23a8a
8d8b4f5
dbf6f82
281ce7b
224b376
f77e404
4fb8017
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -166,7 +166,7 @@ int32_t walInit(stopDnodeFn stopDnode); | |||||||||||
| void walCleanUp(); | ||||||||||||
|
|
||||||||||||
| // handle open and ctl | ||||||||||||
| SWal *walOpen(const char *path, SWalCfg *pCfg); | ||||||||||||
| SWal *walOpen(const char *path, SWalCfg *pCfg, int32_t replica); | ||||||||||||
|
||||||||||||
| SWal *walOpen(const char *path, SWalCfg *pCfg, int32_t replica); | |
| SWal *walOpen(const char *path, SWalCfg *pCfg, int32_t replica); | |
| static inline SWal *walOpenDefaultReplica(const char *path, SWalCfg *pCfg) { return walOpen(path, pCfg, 0); } | |
| #define WAL_OPEN_SELECT(_1, _2, _3, NAME, ...) NAME | |
| #define walOpen(...) WAL_OPEN_SELECT(__VA_ARGS__, walOpen, walOpenDefaultReplica)(__VA_ARGS__) |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4090,3 +4090,8 @@ bool syncNodeCanChange(SSyncNode* pSyncNode) { | |||||||||||
| return true; | ||||||||||||
| } | ||||||||||||
| #endif | ||||||||||||
|
|
||||||||||||
| int32_t syncNotifyWalTruncated(int32_t vgId, int64_t truncatedVer) { | ||||||||||||
| sInfo("vgId:%d, notified sync module: WAL truncated to ver:%" PRId64, vgId, truncatedVer); | ||||||||||||
| return TSDB_CODE_SUCCESS; | ||||||||||||
| } | ||||||||||||
|
Comment on lines
+4094
to
+4097
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The implementation of
Comment on lines
+4093
to
+4097
|
||||||||||||
| int32_t syncNotifyWalTruncated(int32_t vgId, int64_t truncatedVer) { | |
| sInfo("vgId:%d, notified sync module: WAL truncated to ver:%" PRId64, vgId, truncatedVer); | |
| return TSDB_CODE_SUCCESS; | |
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -431,7 +431,143 @@ static int32_t walRenameCorruptedDir(SWal* pWal) { | |||||||||||||||||||||||||
| TAOS_RETURN(code); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| static int32_t walLogEntriesComplete(SWal* pWal) { | ||||||||||||||||||||||||||
| static int32_t walTruncateCorruptedFiles(SWal* pWal, int32_t fileIdx, int32_t replica, int64_t lastValidVer) { | ||||||||||||||||||||||||||
| int32_t code = TSDB_CODE_SUCCESS; | ||||||||||||||||||||||||||
| int32_t lino = 0; | ||||||||||||||||||||||||||
| bool shouldRecover = false; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (replica == 3) { | ||||||||||||||||||||||||||
| shouldRecover = true; | ||||||||||||||||||||||||||
| SWalFileInfo* pFileInfo = taosArrayGet(pWal->fileInfoSet, fileIdx); | ||||||||||||||||||||||||||
| wInfo("vgId:%d, WAL corrupted at ver:%" PRId64 ", auto-recovery enabled for replica=3", | ||||||||||||||||||||||||||
| pWal->cfg.vgId, pFileInfo->firstVer); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| shouldRecover = (tsWalRecoveryPolicy == 1); | ||||||||||||||||||||||||||
| if (shouldRecover) { | ||||||||||||||||||||||||||
| SWalFileInfo* pFileInfo = taosArrayGet(pWal->fileInfoSet, fileIdx); | ||||||||||||||||||||||||||
| wWarn("vgId:%d, WAL corrupted at ver:%" PRId64 ", force recovery enabled by walRecoveryPolicy=1", | ||||||||||||||||||||||||||
| pWal->cfg.vgId, pFileInfo->firstVer); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| SWalFileInfo* pFileInfo = taosArrayGet(pWal->fileInfoSet, fileIdx); | ||||||||||||||||||||||||||
| wError("vgId:%d, WAL corrupted at ver:%" PRId64 ", refusing to start to prevent data loss", | ||||||||||||||||||||||||||
| pWal->cfg.vgId, pFileInfo->firstVer); | ||||||||||||||||||||||||||
| wError("vgId:%d, corrupted WAL files are preserved for manual inspection", pWal->cfg.vgId); | ||||||||||||||||||||||||||
| wError("vgId:%d, to force recovery with data loss, set 'walRecoveryPolicy 1' in taos.cfg and restart", | ||||||||||||||||||||||||||
| pWal->cfg.vgId); | ||||||||||||||||||||||||||
|
Comment on lines
+439
to
+456
|
||||||||||||||||||||||||||
| TAOS_RETURN(TSDB_CODE_WAL_FILE_CORRUPTED); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (!shouldRecover) { | ||||||||||||||||||||||||||
| TAOS_RETURN(TSDB_CODE_WAL_FILE_CORRUPTED); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| wInfo("vgId:%d, truncating WAL at corrupted file index %d, lastValidVer:%" PRId64, pWal->cfg.vgId, fileIdx, lastValidVer); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Truncate current corrupted file if there's a valid version | ||||||||||||||||||||||||||
| if (fileIdx < taosArrayGetSize(pWal->fileInfoSet) && lastValidVer >= 0) { | ||||||||||||||||||||||||||
| SWalFileInfo* pFileInfo = taosArrayGet(pWal->fileInfoSet, fileIdx); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Only truncate if this file has valid entries before corruption | ||||||||||||||||||||||||||
| if (lastValidVer >= pFileInfo->firstVer) { | ||||||||||||||||||||||||||
| char logName[WAL_FILE_LEN]; | ||||||||||||||||||||||||||
| char idxName[WAL_FILE_LEN]; | ||||||||||||||||||||||||||
| walBuildLogName(pWal, pFileInfo->firstVer, logName); | ||||||||||||||||||||||||||
| walBuildIdxName(pWal, pFileInfo->firstVer, idxName); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Read the idx file to find the offset of lastValidVer + 1 | ||||||||||||||||||||||||||
| TdFilePtr pIdxFile = taosOpenFile(idxName, TD_FILE_READ); | ||||||||||||||||||||||||||
| if (pIdxFile != NULL) { | ||||||||||||||||||||||||||
| int64_t idxEntries = lastValidVer - pFileInfo->firstVer + 1; | ||||||||||||||||||||||||||
| int64_t idxFileSize = idxEntries * sizeof(SWalIdxEntry); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Truncate idx file | ||||||||||||||||||||||||||
| (void)taosCloseFile(&pIdxFile); | ||||||||||||||||||||||||||
| pIdxFile = taosOpenFile(idxName, TD_FILE_READ | TD_FILE_WRITE); | ||||||||||||||||||||||||||
| if (pIdxFile != NULL) { | ||||||||||||||||||||||||||
| if (taosFtruncateFile(pIdxFile, idxFileSize) == 0) { | ||||||||||||||||||||||||||
| wInfo("vgId:%d, truncated idx file %s to size:%" PRId64, pWal->cfg.vgId, idxName, idxFileSize); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| wWarn("vgId:%d, failed to truncate idx file %s", pWal->cfg.vgId, idxName); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| (void)taosCloseFile(&pIdxFile); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Read the last valid entry's offset from idx, then read log entry to get size | ||||||||||||||||||||||||||
| pIdxFile = taosOpenFile(idxName, TD_FILE_READ); | ||||||||||||||||||||||||||
| if (pIdxFile != NULL && idxEntries > 0) { | ||||||||||||||||||||||||||
| SWalIdxEntry idxEntry; | ||||||||||||||||||||||||||
| int64_t readPos = (idxEntries - 1) * sizeof(SWalIdxEntry); | ||||||||||||||||||||||||||
| if (taosLSeekFile(pIdxFile, readPos, SEEK_SET) >= 0) { | ||||||||||||||||||||||||||
| if (taosReadFile(pIdxFile, &idxEntry, sizeof(SWalIdxEntry)) == sizeof(SWalIdxEntry)) { | ||||||||||||||||||||||||||
| // Read log entry header to get the size | ||||||||||||||||||||||||||
| TdFilePtr pLogFile = taosOpenFile(logName, TD_FILE_READ | TD_FILE_WRITE); | ||||||||||||||||||||||||||
| if (pLogFile != NULL) { | ||||||||||||||||||||||||||
| SWalCkHead ckHead; | ||||||||||||||||||||||||||
| if (taosLSeekFile(pLogFile, idxEntry.offset, SEEK_SET) >= 0) { | ||||||||||||||||||||||||||
| if (taosReadFile(pLogFile, &ckHead, sizeof(SWalCkHead)) == sizeof(SWalCkHead)) { | ||||||||||||||||||||||||||
| int32_t cryptedBodyLen = ckHead.head.bodyLen; | ||||||||||||||||||||||||||
| if (pWal->cfg.encryptAlgorithm == DND_CA_SM4) { | ||||||||||||||||||||||||||
| cryptedBodyLen = ENCRYPTED_LEN(cryptedBodyLen); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| int64_t logTruncateOffset = idxEntry.offset + sizeof(SWalCkHead) + cryptedBodyLen; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Truncate log file | ||||||||||||||||||||||||||
| if (taosFtruncateFile(pLogFile, logTruncateOffset) == 0) { | ||||||||||||||||||||||||||
| wInfo("vgId:%d, truncated log file %s to offset:%" PRId64, pWal->cfg.vgId, logName, logTruncateOffset); | ||||||||||||||||||||||||||
| pFileInfo->lastVer = lastValidVer; | ||||||||||||||||||||||||||
| pFileInfo->fileSize = logTruncateOffset; | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| wWarn("vgId:%d, failed to truncate log file %s", pWal->cfg.vgId, logName); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| (void)taosCloseFile(&pLogFile); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| (void)taosCloseFile(&pIdxFile); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Don't delete this file, move to next | ||||||||||||||||||||||||||
| fileIdx++; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Delete all files from fileIdx onwards | ||||||||||||||||||||||||||
| for (int32_t i = fileIdx; i < taosArrayGetSize(pWal->fileInfoSet); i++) { | ||||||||||||||||||||||||||
| SWalFileInfo* pDelFileInfo = taosArrayGet(pWal->fileInfoSet, i); | ||||||||||||||||||||||||||
| char delLogName[WAL_FILE_LEN]; | ||||||||||||||||||||||||||
| char delIdxName[WAL_FILE_LEN]; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| walBuildLogName(pWal, pDelFileInfo->firstVer, delLogName); | ||||||||||||||||||||||||||
| walBuildIdxName(pWal, pDelFileInfo->firstVer, delIdxName); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (taosRemoveFile(delLogName) != 0) { | ||||||||||||||||||||||||||
| wWarn("vgId:%d, failed to remove corrupted log file %s", pWal->cfg.vgId, delLogName); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| wInfo("vgId:%d, removed corrupted log file %s", pWal->cfg.vgId, delLogName); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (taosRemoveFile(delIdxName) != 0) { | ||||||||||||||||||||||||||
| wWarn("vgId:%d, failed to remove corrupted idx file %s", pWal->cfg.vgId, delIdxName); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| wInfo("vgId:%d, removed corrupted idx file %s", pWal->cfg.vgId, delIdxName); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Remove deleted files from fileInfoSet | ||||||||||||||||||||||||||
| if (fileIdx < taosArrayGetSize(pWal->fileInfoSet)) { | ||||||||||||||||||||||||||
| taosArrayRemoveBatch(pWal->fileInfoSet, fileIdx, taosArrayGetSize(pWal->fileInfoSet) - fileIdx, NULL); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| wInfo("vgId:%d, WAL truncated successfully", pWal->cfg.vgId); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| TAOS_RETURN(TSDB_CODE_SUCCESS); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| static int32_t walLogEntriesComplete(SWal* pWal, int32_t replica) { | ||||||||||||||||||||||||||
| int32_t sz = taosArrayGetSize(pWal->fileInfoSet); | ||||||||||||||||||||||||||
| bool complete = true; | ||||||||||||||||||||||||||
| int32_t fileIdx = -1; | ||||||||||||||||||||||||||
|
|
@@ -451,13 +587,10 @@ static int32_t walLogEntriesComplete(SWal* pWal) { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (!complete) { | ||||||||||||||||||||||||||
| wError("vgId:%d, WAL log entries incomplete in range [%" PRId64 ", %" PRId64 "], index:%" PRId64 | ||||||||||||||||||||||||||
| ", snaphot index:%" PRId64, | ||||||||||||||||||||||||||
| pWal->cfg.vgId, pWal->vers.firstVer, pWal->vers.lastVer, index, pWal->vers.snapshotVer); | ||||||||||||||||||||||||||
| if (tsWalDeleteOnCorruption) { | ||||||||||||||||||||||||||
| TAOS_RETURN(walRenameCorruptedDir(pWal)); | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| TAOS_RETURN(TSDB_CODE_WAL_LOG_INCOMPLETE); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| ", snaphot index:%" PRId64 ", fileIdx:%d", | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
| ", snaphot index:%" PRId64 ", fileIdx:%d", | |
| ", snapshot index:%" PRId64 ", fileIdx:%d", |
Copilot
AI
Apr 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
walTruncated and truncatedVer are declared but never used, which is dead code and can trigger -Wunused-variable warnings in some build configurations. Either remove them or use them for the intended truncation notification/metadata tracking (e.g., record truncatedVer and propagate it to the sync module).
| bool walTruncated = false; | |
| int64_t truncatedVer = -1; |
Copilot
AI
Apr 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
walLogEntriesComplete() can now mutate pWal->fileInfoSet by truncating WAL files, but walCheckAndRepairMeta() doesn't recompute pWal->vers/totSize or persist meta after this call. If truncation happens here, pWal->vers.lastVer (and the on-disk meta) can remain stale/inconsistent with the new file set. Consider having walLogEntriesComplete() report whether truncation occurred (or return a distinct code), then re-run version alignment + walSaveMeta() (and adjust totSize) when truncation happens.
| actualFileNum = taosArrayGetSize(pWal->fileInfoSet); | |
| if (actualFileNum > 0) { | |
| pWal->vers.firstVer = ((SWalFileInfo*)taosArrayGet(pWal->fileInfoSet, 0))->firstVer; | |
| pWal->vers.lastVer = ((SWalFileInfo*)taosArrayGetLast(pWal->fileInfoSet))->lastVer; | |
| } else { | |
| pWal->vers.firstVer = -1; | |
| pWal->vers.lastVer = -1; | |
| } | |
| walAlignVersions(pWal); | |
| TAOS_CHECK_EXIT(walSaveMeta(pWal)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Header comment says
walRecoveryPolicy"only affects single replica", but the new logic checks it for allreplica != 3cases (so it would also affect replica=2 if that configuration exists). Please align the comment (and possibly the config semantics) with actual behavior to avoid misconfiguration.