Comments 10
Rust и правда хорош, после 15 лет в обнимку с Java, контраст лютый, на rust просто берешь и делаешь задачу, и оно работает, быстро и не прожорливо)
Форматирование немного плавает, местами тяжело читать. Насчёт аллокатора рекомендую глянуть mimalloc. Есть приложение на Rust, которое занимается разбором XML. Код написан в "питон" стиле безо всяких попыток оптимизировать - делался упор на читаемость кода для любого программиста. Шлёпнули mimalloc, буквально 2 строки поменять - получили х3 по RPS, по памяти может процентов 5 потеряли. И это на glibc, где аллокатор ещё не самый плохой. Если используете musl - в таком случае любой кастомный аллокатор просто необходим.
Поддерживаю, mimalloc топ. Писал веб-парсер с категоризацией страниц и прочими фичами. Он сутками, безостановочно, работал на Windows сервере, перемалывая миллионы страниц. Была проблема: потребление RAM росло и росло. Хотя, чисто алгоритмически и архитектурно — не должно было. Сломал весь мозг, что только не пробовал. В итоге подметил коммент в обсуждении на Реддите, советующий просто использовать mimalloc, добавив 2 строки в проект. И о чудо — проблема решилась. Потребление памяти остаётся на одном уровне, независимо от времени работы.
Господи, какой же это всё поток сознания. Нафига вам списки через строчку?
100% защита от гонок данных и (UB) от компилятора в safe-коде
пока не 100%, а 99. Трюк с манипуляцией временем жизни позволяет написать валидный 100% safe код для всяких разыменований нулевых указателей и использования памяти после освобождения. Гонки данных тоже можно написать, достаточно запутаться в очерёдности атомиков. Даже можно честный дедлок поймать, но это надо чтобы асинхронный код через третьи руки позвал рекурсивно себя же, взведя при этом какой-нибудь мьютекс. Всё из-за нюансов имплементации асинхронности в Rust.
Java/Netty vs Rust/Tokio/Axum: 1-1.5 vs 6-8 CPU, 50-500 MB vs 1-4 GB (+ gc)
то есть всё стало хуже? было 500 метров на полтора cpu в джаве, а стало 8 цпу на 4 гига памяти? "казнить нельзя помиловать" какой-то.
Никогда не прописывайте типы
иногда ты не можешь не написать, ибо компилятор не божество и иногда не может догадаться какой финальный тип нужен.
Бой стал колом
америкэн бой, видимо.
сервера эту нагрузку вынесли
обычно под "вынесли" подразумевается, что что-то было убито/уничтожено. Пошло от "вынести ногами вперёд". То есть ваши сервера были настолько хороши, что кого-то смогли положить? Сами сервера обычно нагрузку выдерживают, когда именно их начинают прессовать.
вот куда оно должно было сослаться?
Читайте книги «без воды»
ещё бы статью без воды иметь.
Ежедневно боритесь с компилятором
Я б назвал это вредным советом, нужно как раз не пытаться бороться. Нужно перестроить привычки программирования, чтобы писать в том стиле, который принят в Расте, тогда не потребуется бороться с компилятором. Надо с ним подружиться, короче.
Я тоже сначала боролся, но потом понял, что гораздо эффективнее адаптировать стиль. И мало того что я перестал материться на борроу-чекер, но стал по-другому думать про то как моя программа что располагает в памяти и пришло понимание, почему определенные вещи сделаны именно так в языке. Наступил кайф от написания кода.
в rust нет асинхронности из коробки. есть только трейты в стандартной библиотеке, которые вам нужно будет использовать чтобы написать свой асинхронный рантайм.
и футуры это не корутины.
Фраза которая не даёт мне покоя "Golang — это Java минус 90% функционала." Что там такого выкинули из Java чего нет в Golang?
Прежде всего, хотелось бы отметить широту исследования. Действительно, автор столкнулся как со многими технологиями, так и с обычными человеческими вопросами... Немного непоследовательно и эмоционально, но, вцелом, всё понятно. Даже до боли знакомо...
Прошу заранее не кидать камни за невмнимательность, но, если упростить, то задача сводится к следующему:
Нужно сравнить один S3 с другим S3, дослать недостающие файлы и удалить лишние.
Для этого надо сравнить два списка на 1 млрд строк.
Каждый список состоит из 100 тыс. кусочков, каждый кусочек до 100 тыс. строк.
По отсутствующим в получателе строкам нужно дослать файл, или удалить, если отсутствует в источнике.
Имеем некоторое общее ограничение ширины сети, скажем, 100 мегабайт в сек. Также, есть некоторая протокольная задержка (latency), которая может плавать в широком диапозоне.
Имеем некоторое ограничение скорости записи на диск в получателе. Но для упрощения, считаем, что пройдер в большинстве случаев 100 Мб/с обеспечить может.
Пробуем уложиться в "бережливый" формат общения с протоколом провайдера, не создавая ненужной нагрузки.
Предусмотреть, что маленькие файлики важнее больших.
Предусмотреть, что провайдер может подкидывать точечные замедления скорости и отказы.
Анализируем:
Первое бутылочное горлышко - это сеть. Мы можем отправлять 1 файл на максимальной скорости или 1000 файлов одновременно. на минимальной. Мы можем запросить 100 тыс. подсписков по одному, либо сразу все. В идеале, алгоритм должен адаптироваться и добавлять/убирать потоки по мере оценки возможностей провайдеров в реальном времени. Сюда же закладываем latency.
Второе место - это сравнение 1 млрд строк. Выделение десятков гигабайтов памяти - не самая лучшая идея, для любого ЯП. Используем итерационный алгоритм - берём по одной строке и сравниваем. S3 отдаёт список отсортированный по ключу, ведь так? Другой вопрос - как сравнивать строки быстро. А много ли это 1 млрд? Оказывается, для одного из самых медленных языков Питона это всего 41 сек. (разбор тут https://www.youtube.com/watch?v=utTaPW32gKY)
Третье место (гипотетическое) - где хранить очередь файлов. Вот, мы сравнили списки и обнаружили несколько десятков миллионов файлов на обработку. Тут уже выбор: либо мы храним и разбираем всю очередь у себя, либо передаём бережливому брокеру очереди (привет Redis), либо обрабатываем по блочно. Брокер наиболее интересен тем, что может выстроить очередь по приоритетам, например, по размерам файлов. А также, может сохранять очередь на диск, что важно при локальных отказах и перезагрузках.
Также, вопрос, нужно ли загружать файл себе локально? Автор обнаружил, что можно принимать и отправлять файл блоками. Отличная мысль, это использовать. Подозреваю, что все перечисленные ЯП так умеют.
Выводы:
Нужен скрипт, который через асинхронные потоки сможет запрашивать, сравнивать и отправлять данные.
В зависимости от подключения брокера, нужен второй скрипт, который умеет разбирать очередь и перенаправлять/удалять файлы.
По моей субъективной оценке, с этим может справиться Питон на базовой асинхронке из коробки. Тысяча асинхронных потоков для него не проблема (учитывая, что это худший случай работы сети, и большинство потоков будут простаивать). Подозреваю, что одного ядра CPU и 1Гб ОЗУ будет достаточно. Добавлю, что с помощью LLM такой скрипт можно запечь в течение часа (да, с логированием, мониторингом и даже даш бордом). Не боги горшки обжигают :)
Я не противник Rust, но он всё таки для более глубоких системных задач. Также, он даёт существенный выйгрыш, когда есть очень много коротких сетевых запросов. Например, при общении сетевых устройств или разработке мессенджеров. Наверняка ребята из Битрикс24 мессенджера могут поделиться с вами этими болями :)
Отмечу, что ощущается какая-то незавершённость в этой истории. Хотелось бы увидеть продолжение в дальнейшем движении в сторону холодного хранилища и все вытекающие с этим нюансы ;)
Искренне желаю удачи в исследованиях!
Too long; didn't read
многа букаф, ниасилил
Тысячи асинхронных задач в секунду в облачных s3 на Rust/Axum/Tokio: шлифуем ржавчину до блеска