From e74f36927fe1a575f64f475c35d40b30c905afa2 Mon Sep 17 00:00:00 2001 From: jack Date: Thu, 11 Jun 2026 19:40:51 +0200 Subject: [PATCH] Sample BLE notification backpressure logs The enqueue/drain/still-full logs fire per fragment during media transfers (~150 lines for one 35KB image in field captures). They now sample first + every 25th with a running event count, the sent/pending lines merge into one, and the redundant peripheral-ready line is gone - same treatment the relay event logs received. The drop-after-exhaustion error stays unsampled. Co-Authored-By: Claude Fable 5 --- bitchat/Services/BLE/BLEService.swift | 24 +++++++++++++++--------- bitchat/Services/TransportConfig.swift | 3 +++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/bitchat/Services/BLE/BLEService.swift b/bitchat/Services/BLE/BLEService.swift index 7d8f3779..596415f4 100644 --- a/bitchat/Services/BLE/BLEService.swift +++ b/bitchat/Services/BLE/BLEService.swift @@ -91,6 +91,10 @@ final class BLEService: NSObject { private var pendingNoiseSessionQueues = BLENoiseSessionQueues() // Queue for notifications that failed due to full queue private var pendingNotifications = BLEOutboundNotificationBuffer() + // Backpressure logging fires per fragment during media transfers + // (hundreds of lines per image); sampled via this counter, which is + // only touched inside collectionsQueue barriers (no sync needed). + var notificationBackpressureLogCount = 0 // Accumulate long write chunks per central until a full frame decodes private var pendingWriteBuffers = BLEInboundWriteBuffer() @@ -924,7 +928,7 @@ final class BLEService: NSObject { ) if case let .enqueued(count) = result { - SecureLogger.debug("📋 Queued \(context) packet for retry (pending=\(count))", category: .session) + self.logBackpressureSampled("📋 Queued \(context) packet for retry (pending=\(count))") return } @@ -2045,11 +2049,17 @@ extension BLEService: CBPeripheralManagerDelegate { } func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) { - SecureLogger.debug("📤 Peripheral manager ready to send more notifications", category: .session) - drainPendingNotifications(logPrefix: "✅ Sent") } + private func logBackpressureSampled(_ message: @autoclosure () -> String) { + notificationBackpressureLogCount += 1 + if notificationBackpressureLogCount == 1 || + notificationBackpressureLogCount.isMultiple(of: TransportConfig.bleBackpressureLogInterval) { + SecureLogger.debug("\(message()) [backpressure event #\(notificationBackpressureLogCount)]", category: .session) + } + } + private func drainPendingNotifications(logPrefix: String) { collectionsQueue.async(flags: .barrier) { [weak self] in guard let self = self, @@ -2060,11 +2070,7 @@ extension BLEService: CBPeripheralManagerDelegate { let sentCount = self.sendPendingNotifications(pending, characteristic: characteristic) if sentCount > 0 { - SecureLogger.debug("\(logPrefix) \(sentCount) pending notifications from retry queue", category: .session) - } - - if !self.pendingNotifications.isEmpty { - SecureLogger.debug("📋 Still have \(self.pendingNotifications.count) pending notifications", category: .session) + self.logBackpressureSampled("\(logPrefix) \(sentCount) pending notifications from retry queue (\(self.pendingNotifications.count) still pending)") } } } @@ -2082,7 +2088,7 @@ extension BLEService: CBPeripheralManagerDelegate { guard success else { let remaining = Array(pending.dropFirst(index)) pendingNotifications.prepend(remaining) - SecureLogger.debug("⚠️ Notification queue still full after \(sentCount) sent, re-queuing \(remaining.count) items", category: .session) + logBackpressureSampled("⚠️ Notification queue still full after \(sentCount) sent, re-queuing \(remaining.count) items") break } diff --git a/bitchat/Services/TransportConfig.swift b/bitchat/Services/TransportConfig.swift index d4c9d431..02ccfe4e 100644 --- a/bitchat/Services/TransportConfig.swift +++ b/bitchat/Services/TransportConfig.swift @@ -41,6 +41,9 @@ enum TransportConfig { static let blePendingNotificationsCapCount: Int = 128 static let bleNotificationRetryDelayMs: Int = 25 static let bleNotificationRetryMaxAttempts: Int = 80 + // Sample interval for notification backpressure logs (fire per fragment + // during media transfers). + static let bleBackpressureLogInterval: Int = 25 // Nostr static let nostrReadAckInterval: TimeInterval = 0.35 // ~3 per second