От чего зависит объём внутреннего буфера itertools.tee при разной скорости потребителей одного итератора?
Объём буфера itertools.tee зависит от максимального отставания одного потребителя от другого: элементы, уже полученные быстрым потребителем, но ещё не запрошенные медленным, временно сохраняются. Если один потребитель полностью опередит другой, буфер может вырасти почти до размера всего потока.
Итераторы позволяют обрабатывать последовательности потоково, без предварительного хранения всех элементов. Однако один одноразовый итератор нельзя независимо пройти двумя потребителями: каждый вызов next() продвигает общий источник.
itertools.tee решает задачу логического дублирования потока. Он предоставляет несколько итераторов, сохраняющих элементы источника до тех пор, пока их не получат все созданные потребители.
Пусть один потребитель читает данные быстро, а другой временно остановился. Источник уже нельзя повторно запросить для медленного потребителя, поэтому ранее прочитанные элементы приходится удерживать во внутреннем буфере.
Следовательно, tee не создаёт независимые копии всех данных заранее, но и не сохраняет постоянное потребление памяти. Риск зависит от разницы скоростей: чем дольше один потребитель отстаёт, тем больше растёт буфер.
itertools.tee возвращает несколько связанных итераторов. При первом запросе элемента у источника результат помещается во внутреннюю очередь; потребитель, который запросил его первым, получает значение сразу, а остальные позднее забирают сохранённое значение.
После того как все потребители получили конкретный элемент, он больше не нужен и может быть удалён из буфера. Поэтому память определяется не размером всего источника, а текущим разрывом между самым быстрым и самым медленным потребителем.
tee не делает источник повторно итерируемым и не запускает его заново для каждого потребителя. Все производные итераторы используют общий источник, поэтому внешний источник должен корректно поддерживать последовательные вызовы next().
Обычно не следует одновременно изменять или перемешивать производные итераторы из разных потоков: tee не является универсальным механизмом синхронизации и не устраняет проблемы потокобезопасности. Если потребители читают данные с сильно различающейся скоростью, явная очередь с ограничением размера может быть более контролируемой альтернативой.
Сервис получает поток записей из сети и должен передать его валидатору и журналу аудита. Вариант с преобразованием потока в список прост, но требует памяти под весь объём данных и лишает обработку потокового характера.
Вариант с двумя независимыми запросами к источнику может быть невозможен или приведёт к повторному чтению данных. Вариант с itertools.tee не требует немедленной материализации всего потока, но при остановке валидатора буфер будет расти со скоростью чтения журнала.
Если задержки потребителей кратковременны и контролируемы, выбирают tee: он сохраняет потоковую обработку и существенно уменьшает пиковое потребление памяти. Если отставание может быть большим, лучше использовать ограниченную очередь, сбрасывать необязательные записи или архитектурно разделить доставку данных, чтобы рост памяти имел явный предел.
Нет. Элемент можно удалить только после того, как его получили все производные итераторы, которым он предназначен. Если хотя бы один потребитель отстаёт, соответствующие значения должны оставаться доступными для него.
tee независимое выполнение исходного генератора?Нет. Исходный итератор выполняется один раз, а его уже полученные результаты распределяются через внутренний буфер. Поэтому побочные эффекты источника происходят при продвижении общего источника, а не отдельно для каждого потребителя.
tee экономнее преобразования источника в список?Нет. При близких скоростях потребителей буфер может быть небольшим, и tee действительно сохраняет потоковость. При большом или неограниченном отставании одного потребителя он способен удерживать почти весь уже прочитанный поток, поэтому по памяти может приблизиться к полной материализации, сохраняя при этом дополнительные накладные расходы на координацию потребителей.