perf(extensions): apply backpressure to the JSON-RPC stdio stream (#893) - #979
Merged
Merged
Conversation
…oses #893) JsonRpcStdioClient consumed a driver's stdout eagerly, and every received line was queued as a closure on the handling chain, so a plugin streaming NDJSON faster than it was decoded grew the backlog without bound. - The client counts lines and bytes waiting to be handled and pauses stdout above 256 lines or 16 MiB, resuming at half of either limit; the OS pipe then throttles the plugin instead of our heap. - boundedUtf8LineSplitter now forwards pause/resume to the byte source. Before, pausing the consumer only made the controller buffer decoded lines while the source kept being read. - Fix a related loss: on stdout close, pending requests were failed immediately even when their replies were still queued for (isolate) decoding. They are now failed only after the backlog has been handled.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Описание изменений
JsonRpcStdioClient. Клиент считает строки и байты, ожидающие обработки, и ставит чтение stdout драйвера на паузу выше 256 строк или 16 МиБ, а возобновляет при половине любого лимита (maxBufferedLines,maxBufferedBytes, значения по умолчанию вjson_rpc_payload_limits.dart). Драйвер тогда упирается в буфер ОС-пайпа, а не в память клиента. Одна строка больше лимита всё равно принимается (доmaxLineBytes): чтение просто ждёт её обработки.boundedUtf8LineSplitterпробрасывал паузу не дальше контроллера. Раньшеpause()подписчика лишь заставлял контроллер копить декодированные строки, а источник байтов продолжал читаться. ТеперьonPause/onResumeуправляют исходной подпиской.Plugin stdout closedсразу, даже если ответы уже пришли и ждали декодирования (крупные строки декодируются в изоляте). Плагин, который отвечает и сразу завершается, терял последние ответы. Теперь остаток запросов падает только после того, как очередь обработана. Это выходит за текст issue, но напрямую связано с очередью строк.bufferedLineCountиisReadPausedдля диагностики и тестов.Замер (300 строк по 300 КБ, плагин в том же процессе, изолят-декодирование включено)
Очередь необработанных строк ограничена (300 → 55), но прирост RSS в этом замере не изменился: генератор-плагин работает в том же процессе и сам порождает мусор (jsonEncode / utf8.encode 300 КБ на строку), а RSS не отражает мгновенный размер живой кучи. Поэтому экономию памяти цифрами я показать не смог. В реальном сценарии плагин отдельный процесс, и его данные остаются в пайпе ОС, а не в нашей куче, так что ограничение очереди должно снижать пик, но это не измерено. Замер с реальным драйвером не делал. Время на прогон слегка вырастает (5,6 против 6,4 и 9,1 с при шуме), пауза чуть простаивает конвейер.
Тесты
json_rpc_stdio_client_test(6 новых): пауза источника пробрасывается сплиттером; стоп/старт при накоплении очереди и возобновление после; быстрый источник не уводит очередь за лимит; лимит по байтам; ответы, стоявшие в очереди при закрытии stdout, доставляются. Весьtest/core/extensionsиtest/features/extensions(155 тестов) прогнал локально, проходят.Не входит
Смена транспорта (shared memory / сокеты), как в issue.
Связанные Issue
Closes #893