diag: 总页数未更新问题 — 增加Reconciliation诊断日志 + 修复keepCurrentWindow后pending不移除

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 <noreply@anthropic.com>
This commit is contained in:
shenlei 2026-06-25 17:33:16 +08:00
parent c555329948
commit 2fa58b1026
2 changed files with 34 additions and 5 deletions

View File

@ -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
}

View File

@ -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):