Context
PlatformWalletManager.emitCoreWalletDiagnostics(for:) (#4580) runs the SwiftData half of the export on the persistence serial queue and holds it for the whole duration. Every Rust persister and SPV callback enters that queue through serialQueue.sync, and so does any main-thread persistence access, so they all block until the export returns.
Under CoreDiagnosticRowLimits the export materializes the full PersistentTxo and PersistentTransaction tables cross-wallet — including transactionData blobs — because the exact #4438 classification needs them: an output absent from this wallet may be wrong_wallet rather than missing_txo, and only the whole table can say which. A plain fetch limit is the wrong tool, since a truncated table silently collapses the two.
#4580 bounds this by counting first: above the ceilings (100k TXO rows / 10k transaction rows table-wide) the export narrows to the wallet's own rows, declines the exact audit, and reports audit_incomplete=true, reason=tables_too_large_for_exact_audit. That is honest, but it means the wallets most likely to be under support investigation — heavily mixed CoinJoin wallets — get no exact audit at all.
Ask
A paged / streaming variant of the exact audit that lifts the ceilings without losing the classification:
- Iterate
PersistentTransaction in batches (ModelContext.enumerate(_:batchSize:) or an offset/limit loop under an autoreleasepool per batch) so at most one batch of transactionData is resident.
- Resolve decoded outputs against the TXO table by a batched
#Predicate on the outpoints of the current batch rather than a whole-table Dictionary(grouping:), so wrong_wallet vs missing_txo still comes from the real cross-wallet row set.
- Yield the serial queue between batches (
serialQueue.async per batch, or an explicit drain point) so persister/SPV callbacks interleave instead of waiting for the whole pass.
- Keep
representativeTxo's deterministic duplicate resolution and the existing anomaly vocabulary, so the log format does not change.
When this lands, CoreDiagnosticRowLimits can either be raised substantially or retired, and the tables_too_large_for_exact_audit decline path becomes dead.
Not in scope
Freezing the remaining V1/V2 model shapes in DashSchemaFrozenModels.swift — that is the fix for the v4.2.0-dev.1 open failure, which is a separate problem.
Context
PlatformWalletManager.emitCoreWalletDiagnostics(for:)(#4580) runs the SwiftData half of the export on the persistence serial queue and holds it for the whole duration. Every Rust persister and SPV callback enters that queue throughserialQueue.sync, and so does any main-thread persistence access, so they all block until the export returns.Under
CoreDiagnosticRowLimitsthe export materializes the fullPersistentTxoandPersistentTransactiontables cross-wallet — includingtransactionDatablobs — because the exact #4438 classification needs them: an output absent from this wallet may bewrong_walletrather thanmissing_txo, and only the whole table can say which. A plain fetch limit is the wrong tool, since a truncated table silently collapses the two.#4580 bounds this by counting first: above the ceilings (100k TXO rows / 10k transaction rows table-wide) the export narrows to the wallet's own rows, declines the exact audit, and reports
audit_incomplete=true, reason=tables_too_large_for_exact_audit. That is honest, but it means the wallets most likely to be under support investigation — heavily mixed CoinJoin wallets — get no exact audit at all.Ask
A paged / streaming variant of the exact audit that lifts the ceilings without losing the classification:
PersistentTransactionin batches (ModelContext.enumerate(_:batchSize:)or an offset/limit loop under anautoreleasepoolper batch) so at most one batch oftransactionDatais resident.#Predicateon the outpoints of the current batch rather than a whole-tableDictionary(grouping:), sowrong_walletvsmissing_txostill comes from the real cross-wallet row set.serialQueue.asyncper batch, or an explicit drain point) so persister/SPV callbacks interleave instead of waiting for the whole pass.representativeTxo's deterministic duplicate resolution and the existing anomaly vocabulary, so the log format does not change.When this lands,
CoreDiagnosticRowLimitscan either be raised substantially or retired, and thetables_too_large_for_exact_auditdecline path becomes dead.Not in scope
Freezing the remaining V1/V2 model shapes in
DashSchemaFrozenModels.swift— that is the fix for the v4.2.0-dev.1 open failure, which is a separate problem.