From 2fa58b1026095fd8719b53705cc2bb395d7956cb Mon Sep 17 00:00:00 2001 From: shenlei Date: Thu, 25 Jun 2026 17:33:16 +0800 Subject: [PATCH] =?UTF-8?q?diag:=20=E6=80=BB=E9=A1=B5=E6=95=B0=E6=9C=AA?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=97=AE=E9=A2=98=20=E2=80=94=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0Reconciliation=E8=AF=8A=E6=96=AD=E6=97=A5=E5=BF=97=20+?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8DkeepCurrentWindow=E5=90=8Epending=E4=B8=8D?= =?UTF-8?q?=E7=A7=BB=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. RDEPUBPageMapReconciliationCoordinator: 在evaluateTakeover、 evaluateSegmentTakeover、evaluateFullPageMapTakeover的每个返回点 增加RDEPUBBackgroundTrace日志,记录关键决策参数: - currentSpineIndex、candidateChapters/pages、currentChapters/pages - hasPrev/hasNext、lastBuildableSpineIndex - jumpSession protected coverage ratio 用于确认fullMap被拒绝的具体原因 2. RDEPUBPresentationRuntime: .keepCurrentWindow分支增加 removePendingPageMapUpdate(at: index),防止被拒绝的 reconcileFullMap update无限期留在pending队列中不被消费。 之前return false不移除,导致: - 后续commitPendingPageMapUpdateIfNeeded重试时条件不变 仍然keepCurrentWindow,形成无效循环 - 如果用户不再翻页,总页数永远不更新 Co-Authored-By: Claude --- ...EPUBPageMapReconciliationCoordinator.swift | 36 ++++++++++++++++--- .../RDEPUBPresentationRuntime.swift | 3 +- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPageMapReconciliationCoordinator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPageMapReconciliationCoordinator.swift index fc57fc6..99a3430 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPageMapReconciliationCoordinator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPageMapReconciliationCoordinator.swift @@ -28,8 +28,10 @@ final class RDEPUBPageMapReconciliationCoordinator { guard let currentWindow else { if let candidatePageMap { + RDEPUBBackgroundTrace.log("Reconciliation", "evaluateTakeover: no currentWindow, fullReplace candidateChapters=\(candidatePageMap.totalChapters) candidatePages=\(candidatePageMap.totalPages)") return .fullReplace(candidatePageMap) } + RDEPUBBackgroundTrace.log("Reconciliation", "evaluateTakeover: no currentWindow, no candidate") return .keepCurrentWindow } @@ -54,7 +56,7 @@ final class RDEPUBPageMapReconciliationCoordinator { if coverageRatio < 0.8 { RDEPUBBackgroundTrace.log( "Reconciliation", - "blocked: candidate doesn't cover protected area (coverage=\(coverageRatio))" + "evaluateTakeover: blocked by jumpSession protection coverage=\(String(format: "%.2f", coverageRatio)) currentSpine=\(currentSpineIndex) protected=\(protectedIndices)" ) return .keepCurrentWindow } @@ -72,7 +74,7 @@ final class RDEPUBPageMapReconciliationCoordinator { if !hasPrev || !hasNext { RDEPUBBackgroundTrace.log( "Reconciliation", - "blocked: missing adjacent chapter coverage" + "evaluateTakeover: blocked by missing adjacent coverage (segment) currentSpine=\(currentSpineIndex) hasPrev=\(hasPrev) hasNext=\(hasNext)" ) return .keepCurrentWindow } @@ -85,7 +87,7 @@ final class RDEPUBPageMapReconciliationCoordinator { if candidateSegment.renderSignature != currentRenderSignature { RDEPUBBackgroundTrace.log( "Reconciliation", - "blocked: render signature mismatch" + "evaluateTakeover: blocked by render signature mismatch" ) return .keepCurrentWindow } @@ -109,6 +111,7 @@ final class RDEPUBPageMapReconciliationCoordinator { ) } + RDEPUBBackgroundTrace.log("Reconciliation", "evaluateTakeover: no candidate, keepCurrentWindow") return .keepCurrentWindow } @@ -123,6 +126,10 @@ final class RDEPUBPageMapReconciliationCoordinator { if let currentSpineIndex { if !candidateIndices.contains(currentSpineIndex) { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateSegmentTakeover: keepCurrentWindow — currentSpine=\(currentSpineIndex) not in candidate" + ) return .keepCurrentWindow } } @@ -132,6 +139,10 @@ final class RDEPUBPageMapReconciliationCoordinator { let hasNext = candidateIndices.contains(currentSpineIndex + 1) || currentSpineIndex == lastBuildableSpineIndex if !hasPrev || !hasNext { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateSegmentTakeover: keepCurrentWindow — missing adjacent spine=\(currentSpineIndex) hasPrev=\(hasPrev) hasNext=\(hasNext)" + ) return .keepCurrentWindow } } @@ -164,9 +175,14 @@ final class RDEPUBPageMapReconciliationCoordinator { lastBuildableSpineIndex: Int ) -> RDEPUBPageMapTakeoverDecision { let candidateIndices = Set(candidatePageMap.entries.map { $0.spineIndex }) + let currentEntries = currentWindow.entries.count if let currentSpineIndex { if !candidateIndices.contains(currentSpineIndex) { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateFullPageMapTakeover: keepCurrentWindow — currentSpine=\(currentSpineIndex) not in candidate (candidateChapters=\(candidateIndices.count) currentChapters=\(currentEntries) candidatePages=\(candidatePageMap.totalPages) currentPages=\(currentWindow.totalPages))" + ) return .keepCurrentWindow } } @@ -176,15 +192,27 @@ final class RDEPUBPageMapReconciliationCoordinator { let hasNext = candidateIndices.contains(currentSpineIndex + 1) || currentSpineIndex == lastBuildableSpineIndex if !hasPrev || !hasNext { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateFullPageMapTakeover: keepCurrentWindow — missing adjacent spine=\(currentSpineIndex) hasPrev=\(hasPrev) hasNext=\(hasNext) lastBuildable=\(lastBuildableSpineIndex)" + ) return .keepCurrentWindow } } - let isComplete = candidateIndices.count >= currentWindow.entries.count + let isComplete = candidateIndices.count >= currentEntries if isComplete { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateFullPageMapTakeover: fullReplace — candidateChapters=\(candidateIndices.count) currentChapters=\(currentEntries) candidatePages=\(candidatePageMap.totalPages) currentPages=\(currentWindow.totalPages)" + ) return .fullReplace(candidatePageMap) } + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateFullPageMapTakeover: keepCurrentWindow — candidateChapters=\(candidateIndices.count) < currentChapters=\(currentEntries)" + ) return .keepCurrentWindow } diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift index 433116c..c68f61e 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift @@ -226,7 +226,8 @@ final class RDEPUBPresentationRuntime { switch decision { case .keepCurrentWindow: - RDEPUBBackgroundTrace.log("Reconciliation", "decision: keepCurrentWindow") + RDEPUBBackgroundTrace.log("Reconciliation", "decision: keepCurrentWindow — pending update removed to prevent indefinite retry") + removePendingPageMapUpdate(at: index) return false case .fullReplace(let newPageMap):