Как уже упоминалось, объектные хранилища, как правило, не поддерживают обновления на месте или добавления данных. Чтобы обновить объект, мы записываем новый объект под тем же ключом. Когда специалисты по инженерии данных используют обновления в своих процессах, они должны учитывать модель согласованности (consistency model) объектного хранилища, с которым работают. Объектные хранилища могут быть либо согласованными в конечном счёте (eventually consistent), либо строго согласованными (strongly consistent). Например, до недавнего времени S3 был согласованным в конечном счёте: после записи новой версии объекта под тем же ключом хранилище могло иногда возвращать старую версию объекта. Часть «в конечном счёте» в термине «согласованность в конечном счёте» означает, что через некоторое время кластер хранения достигает состояния, при котором возвращается только последняя версия объекта. Это отличается от модели строгой согласованности, которую мы ожидаем от локальных дисков, подключённых к серверу: чтение после записи всегда возвращает самые свежие данные.
В некоторых случаях может быть желательно обеспечить строгую согласованность для объектного хранилища, и для этого используются стандартные методы. Один из подходов — добавить строго согласованную базу данных (например, PostgreSQL) в систему. Теперь запись объекта становится двухэтапным процессом:
- Записать объект.
- Записать возвращённые метаданные для версии объекта в строго согласованную базу данных.
Метаданные версии (например, хэш объекта или временная метка) могут однозначно идентифицировать версию объекта в сочетании с ключом объекта. Чтобы прочитать объект, читатель выполняет следующие шаги:
- Получить последние метаданные объекта из строго согласованной базы данных.
- Запросить метаданные объекта с использованием ключа объекта. Прочитать данные объекта, если они совпадают с метаданными, полученными из согласованной базы данных.
- Если метаданные объекта не совпадают, повторить шаг 2, пока не будет возвращена последняя версия объекта.
Практическая реализация имеет исключения и крайние случаи, которые нужно учитывать, например, когда объект перезаписывается в процессе выполнения запроса. Эти шаги могут быть скрыты за API, чтобы читатель объекта воспринимал объектное хранилище как строго согласованное, хотя это увеличивает задержку доступа к объекту.
Управление версиями объектов тесно связано с согласованностью объектов. Когда мы перезаписываем объект под существующим ключом в объектном хранилище, мы, по сути, создаём совершенно новый объект, обновляем ссылки с существующего ключа на новый объект и удаляем ссылки на старый объект. Обновление всех ссылок в кластере занимает время, что может приводить к чтению устаревших данных. В конечном итоге сборщик мусора кластера хранения освобождает пространство, занятое устаревшими данными, и перераспределяет ёмкость дисков для новых объектов.
Если включено управление версиями объектов, мы добавляем дополнительные метаданные, указывающие версию объекта. Хотя ссылка по умолчанию обновляется, чтобы указывать на новый объект, мы сохраняем указатели на предыдущие версии. Мы также ведём список версий, чтобы клиенты могли получить список всех версий объекта и выбрать конкретную версию для чтения. Поскольку старые версии объекта всё ещё имеют ссылки, они не удаляются сборщиком мусора.
Если мы ссылаемся на объект с указанием версии, проблема согласованности в некоторых системах объектного хранения исчезает: ключ и метаданные версии вместе образуют уникальную ссылку на конкретный неизменяемый объект данных. При использовании этой пары мы всегда получим один и тот же объект, при условии что он не был удалён. Проблема согласованности всё ещё существует, когда клиент запрашивает «версию по умолчанию» или «последнюю версию» объекта.
Основной издержкой, которую инженерам необходимо учитывать при использовании управления версиями объектов, является стоимость хранения. Исторические версии объектов обычно имеют те же затраты на хранение, что и текущие версии. Стоимость хранения версий объектов может быть как незначительной, так и катастрофически высокой, в зависимости от различных факторов. Размер данных и частота обновлений играют важную роль: большее количество версий объектов может привести к значительному увеличению объёма данных.
Важно помнить, что речь идёт о «грубой» реализации управления версиями объектов. Системы объектного хранения обычно хранят полные данные объекта для каждой версии, а не дифференциальные снимки.
Инженеры также могут использовать политики жизненного цикла хранения (storage lifecycle policies). Эти политики позволяют автоматически удалять старые версии объектов при выполнении определённых условий (например, когда версия объекта достигает определённого возраста или существует много более новых версий). Облачные провайдеры также предлагают различные уровни архивного хранения по значительно сниженным ценам, и процесс архивирования можно управлять с помощью политик жизненного цикла.