Version du document 29/05/2026 · Contexte : 1300 ble_token_renew d’un seul invité pendant 4 heures d’absence du propriétaire en ligne, ce qui a lancé la recherche d’autres sources de trafic HTTP et WebSocket superflu.
ble_token_renew in pending_host_msgs (un UNIQUE INDEX sur host_id, dedup_key). La migration est appliquée.GuestConn.requestBleTokenRenew — au plus une fois toutes les 60 s par bleId.onOpen ; elle attend une session stable d’au moins 60 s, ce qui protège de la tempête connexion–coupure–connexion.Files: HostService.kt:1424-1432, Vault.kt:213
private val pendingKeysRunnable = object : Runnable {
override fun run() {
if (Prefs.isHostRegistered && Prefs.getPendingHostKeys().isNotEmpty()) {
Thread { syncPendingHostKeys() }.start()
}
pendingKeysHandler.postDelayed(this, 60_000L)
}
}
syncPendingHostKeys prend TOUTES les clés hôte en attente et appelle Api.keyCreatepour chacune. Si l’appel HTTP échoue, la clé reste en attente et 60 s plus tard le cycle recommence.
В Prefs stocker par localId :
attempt_at_<localId> — l’horodatage de la dernière tentativeattempt_count_<localId> — le compteur de tentativesÀ chaque passage de syncPendingHostKeys:
val waitMs = (30_000L * (1L shl attemptCount)).coerceAtMost(30 * 60_000L)
if (now - attemptAt < waitMs) return@forEach // skip
// otherwise try
val ok = try { Api.keyCreate(...); true } catch (e) { false }
if (ok) { clear attempt_at/count } else { attempt_count++; attempt_at = now }
Progression: 30 s → 1 min → 2 min → 4 min → 8 min → 16 min → 30 min (plafond). En cas d’échec permanent, cela fait une requête toutes les 30 minutes au lieu d’une par minute. Une réduction par trente.
Il en va de même pour Vault.retryPendingBundles() — le même schéma.
snap_url est l’adresse externe d’une caméra, d’un NVR ou d’une source HTTP. Le client s’y rend directement via Coil ou ExoPlayer ; notre serveur ne fait que transmettre le data_cipher chiffré contenant l’adresse dans le lot. Aucune image ne passe par nous.
Si un propriétaire règle snap_int=1 pour 10 invités, son NVR reçoit 10 requêtes par seconde. C’est une charge pour son NVR, pas pour notre serveur.
Facultatif : borner le curseur de l’interface à trois secondes. C’est une question d’interface, pas d’infrastructure.
Déjà bridé par les correctifs de la 6.61 :
Avec dix objets BLE, un utilisateur envoie dix messages WebSocket d’affilée et le serveur exécute dix branches case 'ble_token_renew' à la suite. Une bagatelle.
File: GuestConn.kt — 7+ endroits.
AppState.overridesVersion = MutableState<Int>, un signal Compose signifiant les données ont changé, relis remember . Il est utilisé dans ActionCard:
val ovrVersion = AppState.overridesVersion.value
val (overrideLabel, avatarPath) = remember(action.key, ovrVersion) {
Prefs.getOverride(action.key) // SharedPreferences IO
}
val snapCfg = remember(action.key, ovrVersion) {
if (action.source == ActionSource.HOST_OWN) Prefs.getSnapshot(action.numberId)
else Prefs.getIncomingSnapshot(...)
}
val tpl = remember(action.key, ovrVersion) { loadTpl() } // more IO
Chaque incrément force chaque carte à relire la substitution, l’avatar, l’instantané et le modèle depuis SharedPreferences. Un utilisateur avec 20 cartes, cela fait 80 lectures de SharedPreferences par incrément.
| Line | Context | Nécessaire ? |
|---|---|---|
| L193 | après réingestion d’un lot (pas de K_obj) | yes |
| L355 | après parseNumbers dans numbers_update | redundant quand les numéros n’ont pas changé |
| L411 | après le chargement d’un avatar | yes |
| L558, L595 | après parseNumbers dans les autres gestionnaires | redundant quand les données sont identiques |
Avec un WebSocket instable, chaque guest_ok → le gestionnaire réanalyse les numéros → incrémente overridesVersion → 80 lectures de SharedPreferences × 20 cartes × la fréquence de guest_ok.
C’est du processeur local et des E/S, pas une charge directe du serveur, mais :
Avant d’incrémenter, comparer la nouvelle liste JSON de numbers avec la précédente, par hachage ou en comparant la liste des GuestNumberInfo). Identiques → ne pas incrémenter.
private var lastNumbersHash: Int = 0
private fun maybeUpdateNumbers(arr: JSONArray?) {
val parsed = parseNumbers(arr)
val newHash = parsed.hashCode() // or a hash of the JSON serialisation
if (newHash == lastNumbersHash) return // nothing changed
lastNumbersHash = newHash
numbers = parsed
publish()
AppState.overridesVersion.value = AppState.overridesVersion.value + 1
}
numbers_update arrive régulièrement mais change rarement — un propriétaire n’édite pas ses objets toutes les minutes. L’optimisation supprime environ 90 % des vagues de redessin superflues.