nav-img
Evolution

Склейка и построение файла с разницей снапшотов

Операции склейки (merge) и построения разницы (diff) для файлов со снапшотами

Инкрементальный снапшот представляет собой в некотором смысле разницу более старого и более нового состояния тома. Не стоит рассматривать эту разницу, как аналог patch-файла, получаемого с помощью утилит типа diff, так как инкрементальный снапшот не хранит данные, которые были перезаписаны или помечены как неиспользуемые. Соответственно, если есть файл с более старым (или базовым) снапшотом и инкрементальный снапшот между этим старым и более новым снапшотом тома, то можно узнать содержимое более нового снапшота. Такая операция называется — merge или склейка снапшотов.

old_snap merge inc_snap -> new_snap

При этом old_snap и new_snap могут быть как полными, так и инкрементальными снапшотами, но они всегда будут одинакового типа.

Логика merge:

  1. Для каждого блока, про который в инкрементальном снапшоте есть запись об изменении содержимого, в результирующий снапшот попадает именно это изменение (запись данных или пометка в качестве неиспользуемого).

  2. Если в инкрементальном снапшоте про некоторый блок нет записи, то в результат попадает содержимое, соответствующее базовому снапшоту.

  3. Если в базовом снапшоте, который может быть только инкрементальным, нет записи про текущий блок, то в результирующем снапшоте не создается никакой записи про этот блок.

Например, склейка снапшотов из раздела Форматы образа тома будет схематично выглядеть, как показано на рисунке ниже.

Пример структуры склейки инкрементального снапшота volume

Операцию склейки снапшотов можно осуществить с несколькими инкрементальными снапшотами по цепочке:

old_snap merge inc_snap_1 merge inc_snap_2 ... merge inc_snap_N

Что технически представляет из себя следующее:

(((old_snap merge inc_snap_1) merge inc_snap_2) ... merge inc_snap_N)
old_snap merge (inc_snap_1 merge (inc_snap_2 ... ( merge inc_snap_N)))

И тот, и другой порядки склейки инкрементальных снапшотов с базовым дают один и тот же результат. Рекомендуется использовать второй вариант. Технически это означает следующее: при склейке выстраивается ряд снапшотов, в самом верху которого находится базовый снапшот, а ниже — инкрементальные снапшоты, начиная от 1 до N. Дальше в результирующий снапшот попадает самая нижняя запись из ряда, являющаяся записью о внесенных изменениях.

Алгоритм работы операции merge для нескольких снапшотов в sbdctl:

  1. Обработка каждого блока тома:

    Для каждого блока данных в томе выполняется следующая последовательность действий:

    • Проверка инкрементальных снапшотов, начиная с самого последнего. Если в снапшоте обнаружены сведения о блоке (или данные, или пометка о неиспользовании):

      • Данные копируются в результирующий снапшот.

      • Переход к обработке следующего блока.

      Если сведения отсутствуют:

      • Переход к проверке предыдущего инкрементального снапшота.

    • Проверка базового снапшота. Если в инкрементальных снапшотах блок помечен как неизменный:

      • Поиск сведений о блоке в базовом снапшоте.

      • При обнаружении - копирование данных в результирующий снапшот.

      • Переход к следующему блоку.

  2. Обработка отсутствующих данных:

    Если информация о блоке отсутствует во всех проверенных снапшотах:

    • В результирующий снапшот ничего не записывается.

    • Выполняется переход к обработке следующего блока.

Поскольку запись в каждом из SBD файлов может охватывать несколько блоков, то за каждый шаг этот алгоритм может обрабатывать более одного блока.

Работа алгоритма склейки в случае нескольких инкрементальных снапшотов

Аналогичным образом можно ввести операцию вычисления разницы между двумя однотипными файлами снапшотов — diff:

old_snap diff new_snap -> inc_snap

inc_snap — это инкрементальный снапшот, такой что результат склейки old_snap и inc_snap равен new_snap. Эта разница всегда существует для двух полных снапшотов, но для инкрементальных снапшотов она существует не всегда.

Логика diff для полных снапшотов:

  1. Если данные, соответствующие некоторому блоку, различаются для двух снапшотов, то в результирующий снапшот записываются данные из более нового снапшота (правого).

  2. Если данные совпадают, то для этого блока не пишется ничего.

Работа алгоритма построения разницы для полных снапшотов

Логика diff для инкрементальных снапшотов:

  1. Если в более старом снапшоте есть какие-или данные, свидетельствующие об изменении некоторого блока, а в более новом (правом), никаких записей нет (т.е. предполагается, что блок не изменялся), то в таком случае diff нельзя построить однозначно.

  2. Если в более новом снапшоте есть какие-или данные, свидетельствующие об изменении некоторого блока, а в более старом никаких записей нет, то в результирующий снапшот попадают данные из более нового снапшота (правого).

  3. Если данные, соответствующие некоторому блоку, различаются для двух снапшотов, то в результирующий снапшот записываются данные из более нового снапшота (правого).

  4. Если данные про изменение блока или совпадают, или отсутствуют и в одном, и в другом снапшотах, то в инкрементальный снапшот не пишется никакой информации про этот блок.

Работа алгоритма построения разницы для инкрементальных снапшотов

Посколько снапшоты одного тома могут иметь разную длину (из-за использования команды storage volume grow), то алгоритмом склейки допускается, что снапшоты могут иметь разную длину, но она всегда должна возрастать, начиная от базового снапшота к последнему инкрементальному. При этом размер результирующего снапшота определяется размером последнего инкрементального снапшота. При этом все снапшоты в стопке слияния расширяются до результирующей длины, при этом полный снапшот заполняется записью о неиспользуемом пространстве в конце, а у инкрементальных снапшотов соответствующий диапазон помечается как не изменявшийся. Аналогичным образом происходит расширение старого снапшота в алгоритме построения разницы. Снапшоты разной длины поддерживаются, начиная с версии 1.3.

merge или склейка нескольких снапшотов с помощью sbdctl

Для построения склейки нескольких снапшотов в формате sbd с помощью указанного выше алгоритма используется операция sbdctl merge. При этом базовый снапшот может быть как инкрементальным, так и полным, а патчи — только инкрементальными.

Операция sbdctl merge:

sbdctl merge <options> <base snapshot> [ <delta snapshot> ... ]

Обязательные параметры:

  • --base file или -b file или первый свободный аргумент после списка параметров — задает имя файла, содержащего базовый снапшот в формате sbd.

  • --delta file или -d file или второй и последующие свободные аргументы — задает список имен файлов, содержащих дельты — инкрементальные снапшоты в формате sbd. Допустимо использовать несколько таких опций. Можно не использовать эту опцию вообще — при этом получается слияние базового снапшота с пустым списком дельт — т.е. по сути копирование базового снапшота в выходной файл.

  • --file-to file или -o file — имя выходного файла с результирующим снапшотом.

  • или же: --stdout — выводить результирующий снапшот на стандартный вывод (поддерживается начиная с версии 1.3)

  • или же: --upload volume_id или -u volume_id — для выгрузки результата на целевой вольюм. На данный момент подобная выгрузка работает медленно, рекомендуется использовать sbdctl import или комбинацию вида sbdctl merge --stdout ... | sbdctl import --stdin ... — в следующих релизах эта опция перестанет поддерживаться.

Нельзя указывать значение одной и той же опции через именованный параметр и через свободный аргумент:

# ошибка: base определено дважды!
sbdctl merge --file-to merged.sbd --delta delta.sbd --base base.sbd base.sbd
# правильно:
sbdctl merge --file-to merged.sbd --delta delta.sbd --base base.sbd
# тоже правильно:
sbdctl merge -o merged.sbd base.sbd delta.sbd

Необязательные параметры:

  • --order-deltas — позволяет автоматически задать цепочку дельт в соответствии с указанными в заголовках файлов версиями снапшотов. По умолчанию порядок дельт соответствует их порядку в командной строке, что не всегда удобно. При использовании этой опции необходимо, чтобы дельты формировали цепочку версий без перекрыващихся версий (например, если база — снапшот версии 1, а дельты — это инкременты между 1 и 2 и 2 и 3 снапшотами, то такая цепочка валидна. Если же база — это снапшот версии 2, а дельты — это инкременты между 2 и 3 и 2 и 4 снапшотами, то такая цепочка не валидна, так как версии дельт перекрываются).

  • --snapshot-name arg — позволяет указать понятное имя для результирующего снапшота (строка до 255 байт длиной).

  • --overwrite — позволяет утилите записать данные результирующего снапшота, перезаписывая уже существующий файл. Без указания этой опции утилита будет сообщать об ошибке при попытке перезаписи созданного ранее файла. Эту опцию необходимо использовать с осторожностью, так как в случае ошибки программы файл может быть удален полностью.

  • --file-buffer size — позволяет выбрать размер буфера в памяти для чтения и записи с файловой системы. Значение по умолчанию — 8M (8 мегабайт). Возможные значения — от 8k до 128M (8 килобайт и 128 мегабайт соответственно). Слишком маленькое значение этого параметра может привести к замедлению записи на локальный диск. Для этой опции можно использовать размерные суффиксы.

  • --show-stats, --stats-json и --format-json — на данный момент (версия 1.3.x и более ранние) эти опции не работают, т.к. sbdctl merge не собирает статистику выполнения. В следующих релизах эта опция будет включать вывод статистики выполнения на стандартный поток ошибок (stderr).

Также поддерживаются все указанные параметры, управляющие логированием и параметры, управляющие поведением клиентской библиотеки сетевого блочного устройства (они нужны при использовании режима upload). Oни являются опциональными и имеют такую же семантику, как и в других операциях.

Примеры использования

# слияние в указанном порядке: delta-3.sbd поверх delta-2.sbd поверх delta-1.sbd поверх базы base.sbd и запись в merged.sbd
sbdctl merge --file-to merged.sbd --base base.sbd --delta delta-1.sbd --delta delta-2.sbd --delta delta-3.sbd
sbdctl merge -o merged.sbd -b base.sbd -d delta-1.sbd -d delta-2.sbd -d delta-3.sbd
sbdctl merge -o merged.sbd base.sbd delta-1.sbd delta-2.sbd delta-3.sbd
# слияние c автоупорядочиванием дельт: delta-3.sbd, delta-2.sbd и delta-1.sbd поверх базы base.sbd, и запись в merged.sbd
# в этом случае дельты можно указывать в произвольном порядке
sbdctl merge --order-deltas --file-to merged.sbd --base base.sbd --delta delta-1.sbd --delta delta-2.sbd --delta delta-3.sbd
sbdctl merge --order-deltas -o merged.sbd -b base.sbd -d delta-3.sbd -d delta-2.sbd -d delta-1.sbd
sbdctl merge --order-deltas -o merged.sbd base.sbd delta-2.sbd delta-1.sbd delta-3.sbd

diff или вычисление разницы двух снапшотов с помощью sbdctl

Для построения разницы двух снапшотов в формате sbd с помощью указанного выше алгоритма используется операция sbdctl diff. При этом возможно вычислять «разницу» (diff) двух снапшотов одного типа (двух полных снапшотов или двух инкрементальных), в противном случае возникнет ошибка.

Операция sbdctl diff :

sbdctl diff <options> <left snapshot> <right snapshot> <diff snapshot>

Обязательные параметры:

  • --left file или -a file или первый свободный аргумент после списка параметров — задает имя файла, содержащего более старый (или левый) снапшот в формате sbd.

  • --right file или -b file или второй свободный аргумент — задает имя файла, содержащего более новый (или правый) снапшот в формате sbd.

  • --file-to file или -o file или третий свободный аргумент — имя выходного файла с инкрементальным снапшотом — разницей между левым и правым снапшотами.

  • или же: --stdout — выводить результирующий инкрементальный снапшот на стандартный вывод (поддерживается начиная с версии 1.3)

Нельзя указывать значение одной и той же опции через именованный параметр и через свободный аргумент:

# ошибка: left определено дважды!
sbdctl diff --left left.sbd right.sbd delta.sbd
# правильно:
sbdctl diff --left left.sbd --right right.sbd --file-to delta.sbd
sbdctl diff left.sbd right.sbd delta.sbd

Необязательные параметры:

  • --incremental или -I — включить поддержку инкрементальных снапшотов в качестве входных. Поскольку разница для двух инкрементальных снапшотов может быть вычислена не всегда, то ее поддержка включается отдельной опцией командной строки.

  • --snapshot-name arg — позволяет указать понятное имя для результирующего снапшота (строка до 255 байт длиной).

  • --overwrite — позволяет утилите записать данные результирующего снапшота, перезаписывая уже существующий файл. Без указания этой опции утилита будет сообщать об ошибке при попытке перезаписи созданного ранее файла. Эту опцию необходимо использовать с осторожностью, так как в случае ошибки программы файл может быть удален полностью.

  • --file-buffer size — позволяет выбрать размер буфера в памяти для чтения и записи с файловой системы. Значение по умолчанию — 16M (16 мегабайт). Возможные значения — от 8k до 128M (8 килобайт и 128 мегабайт соответственно). Слишком маленькое значение этого параметра может привести к замедлению записи на локальный диск. Для этой опции можно использовать размерные суффиксы.

  • --show-stats, --stats-json и --format-json — на данный момент (версия 1.3.x и более ранние) эти опции не работают, т.к. sbdctl diff не собирает статистику выполнения. В следующих релизах эта опция будет включать вывод статистики выполнения на стандартный поток ошибок (stderr).

Также поддерживаются все указанные параметры, управляющие логированием. Oни являются опциональными и имеют такую же семантику, как и в других операциях.

Примеры использования:

# вычисление инкрементального снапшота --- разницы между двумя полными снапшотами snap1.sbd и snap2.sbd
sbdctl diff snap1.sbd snap2.sbd delta.sbd
# то же самое с использованием длинных опций и поддержкой инкрементальных снапшотов
sbdctl diff --left snap1.sbd --right snap2.sbd --file-to delta.sbd --incremental
# то же самое с использованием коротких опций и перезаписью
sbdctl diff -a snap1.sbd -b snap2.sbd -o delta.sbd -I --overwrite
# посмотреть краткую сводку отличий между двумя снапшотами (начиная с версии 1.3):
sbdctl diff -a snap1.sbd -b snap2.sbd --stdout | sbdctl view --stdin

Входные файлы не обязаны быть снапшотами одного тома и правый файл не обязательно должен иметь более новую версию снапшота по сравнению с левым. Но если утилита обнаружит такое расхождение, она выдаст предупреждение.