Summary
PlatformWalletPersistenceHandler (swift-sdk) applies a wallet changeset row by row: markUtxoSpent, upsertTransaction, upsertUtxo each run their own FetchDescriptor (107 fetch sites in the file). On an imported wallet with ~2000 transactions one block-event batch (235–382 folded events) took 184–358 s to commit on the iPhone 17 simulator (Debug app build), and every store round queues behind it on FFIPersister::store's round_lock.
Measured 2026-09-07 from Library/Logs/SwiftDashSDK/<launch>/swift/run.log (persistence_changeset_started 11:56:06 → committed 12:02:04) with sample showing 56% of the commit inside markUtxoSpent (SwiftData/CoreData fetch + relationship resolution). PersistentTxo.outpoint and PersistentTransaction.txid are already @Attribute(.unique), so this is per-fetch overhead, not a missing index.
Proposal
Batch the lookups per changeset: collect the spent outpoints / txids / UTXO keys for an account, fetch them with one #Predicate contains query each into dictionaries, then apply in memory. Keep the per-row autoreleasepool discipline. Profile one commit before/after with sample on the simulator.
Impact
While a commit runs, anything that stores under the wallet-manager guard (registration, send_payment, contact ingest, DashPay payment reconcile) holds the lock for the commit's duration — the iOS freeze class fixed only partially by the lock-scope PR. A wallet import that starts during such a commit still waits for it (the registration store round is serialized behind round_lock).
Related
Summary
PlatformWalletPersistenceHandler(swift-sdk) applies a wallet changeset row by row:markUtxoSpent,upsertTransaction,upsertUtxoeach run their ownFetchDescriptor(107 fetch sites in the file). On an imported wallet with ~2000 transactions one block-event batch (235–382 folded events) took 184–358 s to commit on the iPhone 17 simulator (Debug app build), and everystoreround queues behind it onFFIPersister::store'sround_lock.Measured 2026-09-07 from
Library/Logs/SwiftDashSDK/<launch>/swift/run.log(persistence_changeset_started11:56:06 →committed12:02:04) withsampleshowing 56% of the commit insidemarkUtxoSpent(SwiftData/CoreData fetch + relationship resolution).PersistentTxo.outpointandPersistentTransaction.txidare already@Attribute(.unique), so this is per-fetch overhead, not a missing index.Proposal
Batch the lookups per changeset: collect the spent outpoints / txids / UTXO keys for an account, fetch them with one
#Predicatecontainsquery each into dictionaries, then apply in memory. Keep the per-rowautoreleasepooldiscipline. Profile one commit before/after withsampleon the simulator.Impact
While a commit runs, anything that stores under the wallet-manager guard (registration,
send_payment, contact ingest, DashPay payment reconcile) holds the lock for the commit's duration — the iOS freeze class fixed only partially by the lock-scope PR. A wallet import that starts during such a commit still waits for it (the registration store round is serialized behindround_lock).Related