Термин S3 compatible storage звучит так, будто все решения взаимозаменяемы: есть бакет, ключ, PUT/GET, подписи — значит «как S3». На практике у админов и DevOps это быстро превращается в вопросы: почему вырос latency, почему внезапно подорожал трафик, почему не сработал lifecycle, почему multipart upload зависает, а ETag не совпадает.
Совместимость на уровне API не гарантирует одинаковые характеристики. Провайдер может поддерживать только часть эндпоинтов, по-своему трактовать нюансы консистентности, иметь свои лимиты на запросы и префиксы, отличающиеся SLA и биллинг (особенно по исходящему трафику — egress cost).
Базовая модель object storage и где «вылезают» различия
Объектное хранилище — это плоское пространство ключей внутри бакета. «Папки» — лишь префиксы. Запросы обычно ходят по HTTPS, данные отдаются целиком или диапазонами (Range GET). Для больших объектов применяется multipart upload.
В реальной эксплуатации различия чаще проявляются в пяти зонах:
- Latency: задержка на операции HEAD/GET маленьких объектов, задержка на LIST по префиксу, задержка до первого байта.
- Consistency: когда после PUT/DELETE объект гарантированно виден всем клиентам (и насколько предсказуемо ведёт себя LIST).
- Storage classes: наличие классов, задержка «подъёма» из холодных классов, минимальный срок хранения, стоимость чтения.
- Egress cost: стоимость исходящего трафика и правила, когда он считается «исходящим» (внутри зоны, между регионами, между сервисами).
- Multipart upload: лимиты, параллелизм, особенности ETag/контрольных сумм, корректность abort и сбор «хвостов» незавершённых загрузок.
Latency: что именно мерить и почему среднее мало что говорит
Когда говорят «у нас высокая latency», важно уточнить: какая операция и при каком паттерне ключей, размеров и параллелизма. Для object storage типично, что GET маленьких объектов и HEAD чувствительны к RTT и производительности фронтендов, LIST по «горячему» префиксу деградирует сильнее, чем GET, а большие файлы чаще ограничены пропускной способностью и количеством потоков, а не задержкой одного запроса.
Минимальный набор метрик для сравнения
- Косвенный RTT до endpoint: иногда ICMP недоступен, поэтому ориентируйтесь на время установки TCP/TLS и повторное использование соединений.
- TTFB (time to first byte) для GET маленького объекта.
- Полное время GET для 1/10/100 МБ с разным числом параллельных запросов.
- Latency на LIST для префиксов с 1k/10k/100k ключей (включая пагинацию).
- Ошибки и таймауты: доля 5xx/429 и корреляция с пиками нагрузки.
Практический совет: не сравнивайте только среднее. Смотрите p95/p99. Для бэкапов критична стабильность long-running upload, а для origin под CDN важнее p95 на HEAD/GET маленьких объектов.
Тестовый сценарий, близкий к «боевому»
Если вы храните статику сайта — тестируйте 10–100 тысяч объектов по 5–200 КБ и интенсивный HEAD/GET. Если это бэкапы — тестируйте несколько объектов по 50–500 ГБ и многопоточную загрузку, включая прерывания и продолжение.
Отдельно проверьте поведение при keep-alive (повторное использование соединений) и при принудительном отключении HTTP/2 на клиенте: встречаются реализации, где HTTP/2 даёт неожиданные хвосты задержек.

Consistency: почему «объект загружен, но его нет»
Consistency — это гарантия видимости изменений. У разных S3-совместимых реализаций картина отличается: от строгой консистентности в пределах кластера до «почти строгой», но с задержками на листингах и метаданных.
Какие операции проверять в PoC
- Read-after-write: сразу после PUT объект читается корректно?
- Overwrite: перезапись того же ключа видна без «провалов» и микса версий?
- Delete: после DELETE объект исчезает в GET/HEAD и в LIST?
- LIST consistency: новый объект быстро появляется в LIST по префиксу?
Почему это важно: многие приложения используют LIST как источник истины (обработчики очередей, «папки» с заданиями, сборщики логов). Если LIST запаздывает, вы получите пропуски или дубли. Если overwrite нестрогий — можно поймать старый объект при чтении сразу после обновления.
Если вы часто упираетесь именно в эти эффекты, полезно держать под рукой набор архитектурных приёмов для eventual-consistency: паттерны работы с консистентностью в S3-совместимых хранилищах.
Паттерны, которые уменьшают риск проблем
- Использовать уникальные ключи (контент-адресация по хэшу или префикс по timestamp), избегая overwrite в горячем пути.
- Хранить индекс и метаданные отдельно (БД/кэш) и не полагаться на LIST как на «каталог».
- Для важных пайплайнов закладывать ретраи и идемпотентность обработчиков.
Storage classes: «дешевле хранить» почти всегда означает «дороже доставать»
Storage classes — это компромисс между ценой хранения, доступностью и задержкой доступа. У разных провайдеров классы могут называться похоже, но иметь разные свойства.
- Минимальный срок хранения (например, 30/60/90 дней) и штрафы за раннее удаление.
- Время восстановления и отдельная стоимость retrieval для «холодных» классов.
- Платные операции (GET/PUT/LIST/HEAD) и их тарификация.
Для админа важен не только прайс «за ГБ в месяц», а TCO по вашему профилю: сколько вы читаете, сколько листите, сколько раз делаете HEAD, какой процент данных реально «холодный».
Lifecycle и совместимость
Lifecycle-правила (переезд между классами, удаление старых объектов) могут поддерживаться частично. Где-то нет переходов между классами, где-то нет фильтров по тегам, где-то правила применяются пакетно и с задержкой.
Проверяйте это отдельным PoC: загружаете тестовые объекты с тегами, ставите правило на быстрый переход и смотрите, что реально происходит (и как быстро меняются метаданные, стоимость операций и доступность).
Egress cost: главный источник «неожиданных» счетов
Egress cost — это плата за исходящий трафик из хранилища. Сюрпризы обычно приходят не от очевидной раздачи файлов пользователям, а от инфраструктурных сценариев.
- Репликация или синхронизация между регионами или между разными провайдерами.
- Проверки целостности «через скачивание» вместо серверной проверки по checksum/ETag.
- ETL и сборщики, которые перечитывают большие объёмы вместо инкрементальных выборок.
- CDN или прокси, который кеширует не так, как ожидается, и постоянно ходит на origin.
Что уточнять в тарифах до миграции
- Стоимость исходящего трафика «в интернет».
- Стоимость трафика внутри региона или зоны (если применимо).
- Стоимость трафика до вычислений провайдера (VM, контейнеры) в той же площадке: бывает как бесплатной, так и платной.
- Отдельная тарификация запросов (GET/PUT/LIST) и минимальный биллинг (округления по ГБ).
Если вы отдаёте много статики, иногда выгоднее архитектурно отделить «хранилище как источник» от «раздачи пользователям»: кэширование, агрессивные заголовки, версионирование объектов, чтобы уменьшить промахи кеша.
Multipart upload: лимиты, параллелизм, ETag и уборка хвостов
Multipart upload — критичная часть для бэкапов, медиатеки, логов и артефактов CI. Формально API одинаковый, но нюансы реализаций разные.
Ключевые параметры, которые стоит проверить
- Минимальный и максимальный размер part, максимальное число частей.
- Параллелизм: как хранилище реагирует на 10–50–200 одновременных part upload.
- Стабильность долгих загрузок: что происходит при разрыве соединения, как долго живёт upload session.
- Abort и сбор мусора: удаляются ли незавершённые части автоматически и как быстро.
Отдельная «мина» — ETag. Во многих реализациях ETag для multipart — это не MD5 всего объекта, а производное от MD5 частей с суффиксом вида -N (число частей). Некоторые клиенты и инструменты ошибочно считают ETag «контрольной суммой» и строят на этом верификацию.
Надёжнее опираться на явные checksum-механизмы клиента (если доступны) или хранить отдельный хэш в метаданных или sidecar-объекте.
Практика: как не устроить себе утечку денег и места
- Настройте регулярный аудит незавершённых multipart uploads и политику авто-очистки.
- Выбирайте размер part так, чтобы он был достаточно крупным (меньше частей и нагрузки на метаданные), но не гигантским (чтобы ретраи не стоили слишком дорого по времени).
- Закладывайте экспоненциальный backoff на 429/5xx и ограничение параллелизма на клиенте.

Функциональные отличия, о которых забывают
Помимо KPI, в сравнении часто всплывают «мелочи», которые ломают миграцию или усложняют эксплуатацию:
- Versioning и правила удаления версий: поддерживается ли полноценно и как тарифицируется.
- Object Lock и immutability: важно для бэкапов и соответствия требованиям, но есть не везде.
- События и уведомления, политики доступа, ограничения по IP: поддержка может быть частичной.
- Лимиты: на бакеты, на ключи в префиксе, на частоту запросов, на размер метаданных, на длину ключа.
- Совместимость подписи: SigV4 обычно ожидаем, но встречаются нюансы с регионом и заголовками.
Чек-лист выбора: коротко, но по делу
- Какая рабочая нагрузка: мелкие объекты с частыми HEAD/GET или большие объекты и редкие чтения?
- Какие SLO: p95 latency на GET/HEAD, скорость выгрузки больших объектов, допустимые задержки LIST?
- Нужны ли storage classes и lifecycle (и какие сценарии переходов и удаления)?
- Сколько egress в месяц и куда именно (интернет, другая площадка, вычисления рядом)?
- Нужна ли строгая consistency для LIST/overwrite или можно обойтись архитектурными паттернами?
- Какие клиенты или инструменты будут использоваться и какие требования к multipart upload и checksum?
Как провести PoC: план на 1–2 дня
Чтобы не спорить «на глаз», сделайте короткий PoC на двух-трёх кандидатах и сравнивайте одинаковые сценарии.
- Сгенерируйте набор данных: 100k маленьких объектов и 3–5 больших файлов.
- Прогоните PUT, GET, HEAD, LIST с пагинацией, параллельные multipart upload и abort незавершённых загрузок.
- Соберите p50/p95/p99 по каждой операции и отдельно посчитайте стоимость: хранение, запросы, egress.
- Проверьте lifecycle на тестовых объектах и задержки применения правил.
После такого сравнения s3 compatible storage перестаёт быть маркетинговым ярлыком и превращается в инженерную матрицу выбора: где лучше latency, где предсказуемее consistency, где подходят storage classes, и где egress cost не уничтожает экономику проекта.


