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:
parent
c555329948
commit
2fa58b1026
@ -28,8 +28,10 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
|||||||
|
|
||||||
guard let currentWindow else {
|
guard let currentWindow else {
|
||||||
if let candidatePageMap {
|
if let candidatePageMap {
|
||||||
|
RDEPUBBackgroundTrace.log("Reconciliation", "evaluateTakeover: no currentWindow, fullReplace candidateChapters=\(candidatePageMap.totalChapters) candidatePages=\(candidatePageMap.totalPages)")
|
||||||
return .fullReplace(candidatePageMap)
|
return .fullReplace(candidatePageMap)
|
||||||
}
|
}
|
||||||
|
RDEPUBBackgroundTrace.log("Reconciliation", "evaluateTakeover: no currentWindow, no candidate")
|
||||||
return .keepCurrentWindow
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +56,7 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
|||||||
if coverageRatio < 0.8 {
|
if coverageRatio < 0.8 {
|
||||||
RDEPUBBackgroundTrace.log(
|
RDEPUBBackgroundTrace.log(
|
||||||
"Reconciliation",
|
"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
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
@ -72,7 +74,7 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
|||||||
if !hasPrev || !hasNext {
|
if !hasPrev || !hasNext {
|
||||||
RDEPUBBackgroundTrace.log(
|
RDEPUBBackgroundTrace.log(
|
||||||
"Reconciliation",
|
"Reconciliation",
|
||||||
"blocked: missing adjacent chapter coverage"
|
"evaluateTakeover: blocked by missing adjacent coverage (segment) currentSpine=\(currentSpineIndex) hasPrev=\(hasPrev) hasNext=\(hasNext)"
|
||||||
)
|
)
|
||||||
return .keepCurrentWindow
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
@ -85,7 +87,7 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
|||||||
if candidateSegment.renderSignature != currentRenderSignature {
|
if candidateSegment.renderSignature != currentRenderSignature {
|
||||||
RDEPUBBackgroundTrace.log(
|
RDEPUBBackgroundTrace.log(
|
||||||
"Reconciliation",
|
"Reconciliation",
|
||||||
"blocked: render signature mismatch"
|
"evaluateTakeover: blocked by render signature mismatch"
|
||||||
)
|
)
|
||||||
return .keepCurrentWindow
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
@ -109,6 +111,7 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RDEPUBBackgroundTrace.log("Reconciliation", "evaluateTakeover: no candidate, keepCurrentWindow")
|
||||||
return .keepCurrentWindow
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,6 +126,10 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
|||||||
|
|
||||||
if let currentSpineIndex {
|
if let currentSpineIndex {
|
||||||
if !candidateIndices.contains(currentSpineIndex) {
|
if !candidateIndices.contains(currentSpineIndex) {
|
||||||
|
RDEPUBBackgroundTrace.log(
|
||||||
|
"Reconciliation",
|
||||||
|
"evaluateSegmentTakeover: keepCurrentWindow — currentSpine=\(currentSpineIndex) not in candidate"
|
||||||
|
)
|
||||||
return .keepCurrentWindow
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,6 +139,10 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
|||||||
let hasNext = candidateIndices.contains(currentSpineIndex + 1) ||
|
let hasNext = candidateIndices.contains(currentSpineIndex + 1) ||
|
||||||
currentSpineIndex == lastBuildableSpineIndex
|
currentSpineIndex == lastBuildableSpineIndex
|
||||||
if !hasPrev || !hasNext {
|
if !hasPrev || !hasNext {
|
||||||
|
RDEPUBBackgroundTrace.log(
|
||||||
|
"Reconciliation",
|
||||||
|
"evaluateSegmentTakeover: keepCurrentWindow — missing adjacent spine=\(currentSpineIndex) hasPrev=\(hasPrev) hasNext=\(hasNext)"
|
||||||
|
)
|
||||||
return .keepCurrentWindow
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,9 +175,14 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
|||||||
lastBuildableSpineIndex: Int
|
lastBuildableSpineIndex: Int
|
||||||
) -> RDEPUBPageMapTakeoverDecision {
|
) -> RDEPUBPageMapTakeoverDecision {
|
||||||
let candidateIndices = Set(candidatePageMap.entries.map { $0.spineIndex })
|
let candidateIndices = Set(candidatePageMap.entries.map { $0.spineIndex })
|
||||||
|
let currentEntries = currentWindow.entries.count
|
||||||
|
|
||||||
if let currentSpineIndex {
|
if let currentSpineIndex {
|
||||||
if !candidateIndices.contains(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
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,15 +192,27 @@ final class RDEPUBPageMapReconciliationCoordinator {
|
|||||||
let hasNext = candidateIndices.contains(currentSpineIndex + 1) ||
|
let hasNext = candidateIndices.contains(currentSpineIndex + 1) ||
|
||||||
currentSpineIndex == lastBuildableSpineIndex
|
currentSpineIndex == lastBuildableSpineIndex
|
||||||
if !hasPrev || !hasNext {
|
if !hasPrev || !hasNext {
|
||||||
|
RDEPUBBackgroundTrace.log(
|
||||||
|
"Reconciliation",
|
||||||
|
"evaluateFullPageMapTakeover: keepCurrentWindow — missing adjacent spine=\(currentSpineIndex) hasPrev=\(hasPrev) hasNext=\(hasNext) lastBuildable=\(lastBuildableSpineIndex)"
|
||||||
|
)
|
||||||
return .keepCurrentWindow
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let isComplete = candidateIndices.count >= currentWindow.entries.count
|
let isComplete = candidateIndices.count >= currentEntries
|
||||||
if isComplete {
|
if isComplete {
|
||||||
|
RDEPUBBackgroundTrace.log(
|
||||||
|
"Reconciliation",
|
||||||
|
"evaluateFullPageMapTakeover: fullReplace — candidateChapters=\(candidateIndices.count) currentChapters=\(currentEntries) candidatePages=\(candidatePageMap.totalPages) currentPages=\(currentWindow.totalPages)"
|
||||||
|
)
|
||||||
return .fullReplace(candidatePageMap)
|
return .fullReplace(candidatePageMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RDEPUBBackgroundTrace.log(
|
||||||
|
"Reconciliation",
|
||||||
|
"evaluateFullPageMapTakeover: keepCurrentWindow — candidateChapters=\(candidateIndices.count) < currentChapters=\(currentEntries)"
|
||||||
|
)
|
||||||
return .keepCurrentWindow
|
return .keepCurrentWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -226,7 +226,8 @@ final class RDEPUBPresentationRuntime {
|
|||||||
|
|
||||||
switch decision {
|
switch decision {
|
||||||
case .keepCurrentWindow:
|
case .keepCurrentWindow:
|
||||||
RDEPUBBackgroundTrace.log("Reconciliation", "decision: keepCurrentWindow")
|
RDEPUBBackgroundTrace.log("Reconciliation", "decision: keepCurrentWindow — pending update removed to prevent indefinite retry")
|
||||||
|
removePendingPageMapUpdate(at: index)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case .fullReplace(let newPageMap):
|
case .fullReplace(let newPageMap):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user