From 605f353f4b9d4c508d117d8da31a122036f47e75 Mon Sep 17 00:00:00 2001 From: iris <2+iris@noreply.localhost> Date: Mon, 7 Sep 2026 20:57:46 -0400 Subject: [PATCH] An app's devlog poll forwards at most 500 lines at a time A ring holds thousands, and two things routinely hand this the whole of it at once: the first poll after the tab opens, and every restart of the app being read, since an in-memory ring starts again at zero. Unbounded that is one request, one append and one redraw of the whole log per second -- measured on the emulator as an ANR in this app while the app it was reading crash-looped. The cursor advances by what was sent, so a backlog drains over a few polls and nothing is lost. Co-Authored-By: Claude Fable 5.1 --- .../main/kotlin/com/example/devupdater/DevLog.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/androidApp/src/main/kotlin/com/example/devupdater/DevLog.kt b/app/androidApp/src/main/kotlin/com/example/devupdater/DevLog.kt index 727fd78..c4839af 100644 --- a/app/androidApp/src/main/kotlin/com/example/devupdater/DevLog.kt +++ b/app/androidApp/src/main/kotlin/com/example/devupdater/DevLog.kt @@ -193,7 +193,7 @@ fun forwardDevLog( val status = devLogStatus(resolver, authority) ?: return DevLogForward(null, 0) val stored = devLogCursor(context, key, component) val since = if (status.newestSeq >= 0 && status.newestSeq + 1 < stored) 0 else stored - val lines = devLogLines(resolver, authority, since) + val lines = devLogLines(resolver, authority, since).take(MAX_LINES_PER_POLL) if (lines.isEmpty()) { // Still worth writing back, so a reset is not re-decided every // second while an app that restarted says nothing. @@ -215,6 +215,18 @@ fun forwardDevLog( return DevLogForward(status, lines.size) } +/** + * How much one poll will forward, leaving the rest for the next one. + * + * An app's ring can hold thousands of lines, and two things routinely hand this the whole of it at + * once: the first poll after the tab opens, and every restart of the app being read, since an + * in-memory ring starts again at zero. Unbounded, that is one request, one append and one redraw of + * the whole log per second — measured on the emulator as an ANR in this app while the app it was + * reading crash-looped. The cursor advances by what was actually sent, so nothing is lost; a + * backlog simply drains over a few polls. + */ +private const val MAX_LINES_PER_POLL = 500 + /** What one poll found: the ring as a whole, and how many lines it forwarded. */ data class DevLogForward(val status: DevLogStatus?, val sent: Int)