Compare commits
2 Commits
e732cbea2d
...
4f7c8752e0
Author | SHA1 | Date |
---|---|---|
Vitaliy Filippov | 4f7c8752e0 | |
Vitaliy Filippov | 9def199981 |
|
@ -80,67 +80,71 @@ All other client-side components are based on the client library:
|
||||||
- **vitastor-disk** — утилита для разметки дисков под Vitastor OSD. С её помощью можно
|
- **vitastor-disk** — утилита для разметки дисков под Vitastor OSD. С её помощью можно
|
||||||
создавать, удалять, менять размеры или перемещать разделы OSD.
|
создавать, удалять, менять размеры или перемещать разделы OSD.
|
||||||
|
|
||||||
## Общий процесс записи и чтения
|
## Overall read/write process
|
||||||
|
|
||||||
- В Vitastor хранятся виртуальные диски, также называемые "образы" или "иноды".
|
- Vitastor stores virtual disks, also named "images" or "inodes".
|
||||||
- Каждый образ хранится в определённом пуле. Пул определяет параметры хранения образов в нём,
|
- Each image is stored in some pool. Pool specifies storage parameters such as redundancy
|
||||||
такие, как схему избыточности (репликация или EC — коды коррекции ошибок), домен отказа
|
scheme (replication or EC — erasure codes, i.e. error correction codes), failure domain
|
||||||
и ограничения по выбору OSD для размещения данных образов. См. [Конфигурация пулов](../config/pool.ru.md).
|
and restrictions on OSD selection for image data placement. See [Pool configuration](../config/pool.en.md) for details.
|
||||||
- Каждый образ разбивается на объекты фиксированного размера, равного [block_size](../config/layout-cluster.ru.md#block_size)
|
- Each image is split into objects/blocks of fixed size, equal to [block_size](../config/layout-cluster.en.md#block_size)
|
||||||
(по умолчанию 128 КБ), умноженному на число частей данных для EC или на 1 для реплик.
|
(128 KB by default), multiplied by data part count for EC or 1 for replicas. That is,
|
||||||
То есть, например, если в пуле используется схема кодирования 4+2 (4 части данных + 2 чётности),
|
if a pool uses EC 4+2 coding scheme (4 data parts + 2 parity parts), then, with the
|
||||||
то при стандартном block_size образы разбиваются на части по 512 КБ.
|
default block_size, images are split into 512 KB objects.
|
||||||
- Клиентские запросы чтения/записи разделяются на части, соответствующие этим объектам.
|
- Client read/write requests are split into parts at object boundaries.
|
||||||
- Для каждого объекта определяется номер PG, которой он принадлежит, путём простого взятия
|
- Each object is mapped to a PG number it belongs to, by simply taking a remainder of
|
||||||
остатка от деления ID образа и смещения на число PG в пуле, которому принадлежит образ.
|
division of its offset by PG count of the image's pool.
|
||||||
- Клиент читает информацию о первичном OSD всех PG из etcd. Первичный OSD каждой PG назначается
|
- Client reads primary OSD for all PGs from etcd. Primary OSD for each PG is assigned
|
||||||
монитором в процессе работы кластера, вместе с текущим целевым набором OSD этой PG.
|
by the monitor during cluster operation, along with the full PG OSD set.
|
||||||
- Клиент соединяется (если ещё не соединён) с первичными OSD всех PG, объекты которых участвуют
|
- If not already connected, client connects to primary OSDs of all PGs involved in a
|
||||||
в запросе и одновременно отправляет им запросы чтения/записи частей.
|
read/write request and sends parts of the request to them.
|
||||||
- Если какие-то из первичных OSD недоступны, клиент повторяет попытки соединения бесконечно до
|
- If a primary OSD is unavailable, client retries connection attempts indefinitely
|
||||||
тех пор, пока они не станут доступны или пока PG не будут назначены другие первичные OSD.
|
either until it becomes available or until the monitor assigns another OSD as primary
|
||||||
- Клиент также повторяет запросы, если первичные OSD отвечают кодом ошибки EPIPE, означающим,
|
for that PG.
|
||||||
что запрос не может быть обработан в данный момент. Это происходит, если PG либо не активна
|
- Client also retries requests if the primary OSD replies with error code EPIPE, meaning
|
||||||
вообще, либо не активна на данном OSD в данный момент (например, если в этот момент меняется
|
that the PG is inactive at this OSD at the moment - for example, when the primary OSD
|
||||||
её первичный OSD) или если в процессе обработки запроса сам первичный OSD теряет подключение
|
is switched, or if the primary OSD itself loses connection to replicas during request
|
||||||
к репликам.
|
handling.
|
||||||
- Первичный OSD определяет, на каких OSD находятся части объекта. По умолчанию все объекты
|
- Primary OSD determines where the parts of the object are stored. By default, all objects
|
||||||
считаются находящимися на текущем целевом наборе OSD соответствующей PG, но какие-то могут
|
are assumed to be stored at the target OSD set of a PG, but some of them may be present
|
||||||
находиться на других OSD, если эти объекты деградированы или перемещены, или идёт процесс
|
at a different OSD set if they are degraded or moved, or if the data rebalancing process
|
||||||
ребаланса. Запросы для проверки по сети не отправляются, информация о местоположении всех
|
is active. OSDs doesn't do any network requests, if calculates locations of all objects
|
||||||
объектов рассчитывается первичным OSD при активации PG и хранится в памяти.
|
during PG activation and stores it in memory.
|
||||||
- Первичный OSD соединяется (если ещё не соединён) с вторичными OSD, на которых располагаются
|
- Primary OSD handles the request locally when it can - for example, when it's a read
|
||||||
части объекта, и отправляет им запросы чтения/записи, а также читает/пишет из/в своё локальное
|
from a replicated pool or when it's a read from a EC pool involving only one data part
|
||||||
хранилище, если сам входит в набор.
|
stored on the OSD's local disk.
|
||||||
- После завершения всех вторичных операций чтения/записи первичный OSD отправляет ответ клиенту.
|
- When a request requires reads or writes to additional OSDs, primary OSD uses already
|
||||||
|
established connections to secondary OSDs of the PG to execute these requests. This happens
|
||||||
|
in parallel to local disk operations. All such connections are guaranteed to be already
|
||||||
|
established when the PG is active, and if any of them is dropped, PG is restarted and
|
||||||
|
all current read/write operations to it fail with EPIPE error and are retried by clients.
|
||||||
|
- After completing all secondary read/write requests, primary OSD sends the response to
|
||||||
|
the client.
|
||||||
|
|
||||||
### Особенности обработки запросов
|
### Nuances of request handling
|
||||||
|
|
||||||
- Если в пуле используются коды коррекции ошибок и при этом часть OSD недоступна, первичный
|
- If a pool uses erasure codes and some of the OSDs are unavailable, primary OSDs recover
|
||||||
OSD при чтении восстанавливает данные из оставшихся частей.
|
data from the remaining parts during read.
|
||||||
- Каждый объект имеет номер версии. При записи объекта первичный OSD сначала читает из номер
|
- Each object has a version number. During write, primary OSD first determines the current
|
||||||
версии объекта. Так как первичный OSD обычно сам хранит копию или часть объекта, номер
|
version of the object. As primary OSD usually stores the object or its part itself, most
|
||||||
версии обычно читается из памяти самого OSD. Однако, если ни одна часть обновляемого объекта
|
of the time version is read from the memory of the OSD itself. However, if primary OSD
|
||||||
не находится на первичном OSD, для получения номера версии он обращается к одному из вторичных
|
doesn't contain parts of the object, it requests the version number from a secondary OSD
|
||||||
OSD, на которых копия объекта есть. Обращения к диску при этом всё равно не происходит,
|
which has that part. Such request still doesn't involve reading from the disk though,
|
||||||
так как метаданные объектов, включая номер версии, все OSD хранят в памяти.
|
because object metadata, including version number, is always stored in OSD memory.
|
||||||
- Если в пуле используются коды коррекции ошибок, перед частичной записью объекта для вычисления
|
- If a pool uses erasure codes, partial writes of an object require reading other parts of
|
||||||
чётности зачастую требуется чтение частей объекта с вторичных OSD или с локального диска
|
it from secondary OSDs or from the local disk of the primary OSD itself. This is called
|
||||||
самого первичного OSD.
|
"read-modify-write" process.
|
||||||
- Также, если в пуле используются коды коррекции ошибок, для закрытия Write Hole применяется
|
- If a pool uses erasure codes, two-phase write process is used to get rid of the Write Hole
|
||||||
двухфазный алгоритм записи: сначала на все вторичные OSD записывается новая версия частей
|
problem: first a new version of object parts is written to all secondary OSDs without
|
||||||
объекта, но при этом старая версия не удаляется, а потом, после получения подтверждения
|
removing the previous version, and then, after receiving successful write confirmations
|
||||||
успешной записи от всех вторичных OSD, новая версия фиксируется и разрешается удаление старой.
|
from all OSDs, new version is committed and the old one is allowed to be removed.
|
||||||
- Если в кластере не включён режим immediate_commit, то запросы записи, отправляемые клиентами,
|
- In a pool doesn't use immediate_commit mode, then write requests sent by clients aren't
|
||||||
не считаются зафиксированными на физических накопителях сразу. Для фиксации данных клиенты
|
treated as committed to physical media instantly. Clients have to send separate type of
|
||||||
должны отдельно отправлять запросы SYNC (отдельный от чтения и записи вид запроса),
|
requests (SYNC) to commit changes, and before it isn't sent, new versions of data are
|
||||||
а пока такой запрос не отправлен, считается, что записанные данные могут исчезнуть,
|
allowed to be lost if some OSDs die. Thus, when immediate_commit is disabled, clients
|
||||||
если соответствующий OSD упадёт. Поэтому, когда режим immediate_commit отключён, все
|
store copies of all write requests in memory and repeat them from there when the
|
||||||
запросы записи клиенты копируют в памяти и при потере соединения и повторном соединении
|
connection to primary OSD is lost. This in-memory copy is removed after a successful
|
||||||
с OSD повторяют из памяти. Скопированные в память данные удаляются при успешном fsync,
|
SYNC, and to prevent excessive memory usage, clients also do an automatic SYNC
|
||||||
а чтобы хранение этих данных не приводило к чрезмерному потреблению памяти, клиенты
|
every [client_dirty_limit](../config/network.en.md#client_dirty_limit) written bytes.
|
||||||
автоматически выполняют fsync каждые [client_dirty_limit](../config/network.ru.md#client_dirty_limit)
|
|
||||||
записанных байт.
|
|
||||||
|
|
||||||
## Similarities to Ceph
|
## Similarities to Ceph
|
||||||
|
|
||||||
|
|
|
@ -114,16 +114,22 @@
|
||||||
находиться на других OSD, если эти объекты деградированы или перемещены, или идёт процесс
|
находиться на других OSD, если эти объекты деградированы или перемещены, или идёт процесс
|
||||||
ребаланса. Запросы для проверки по сети не отправляются, информация о местоположении всех
|
ребаланса. Запросы для проверки по сети не отправляются, информация о местоположении всех
|
||||||
объектов рассчитывается первичным OSD при активации PG и хранится в памяти.
|
объектов рассчитывается первичным OSD при активации PG и хранится в памяти.
|
||||||
- Первичный OSD соединяется (если ещё не соединён) с вторичными OSD, на которых располагаются
|
- Когда это возможно, первичный OSD обрабатывает запрос локально. Например, так происходит
|
||||||
части объекта, и отправляет им запросы чтения/записи, а также читает/пишет из/в своё локальное
|
при чтениях объектов из пулов с репликацией или при чтении из EC пула, затрагивающего
|
||||||
хранилище, если сам входит в набор.
|
только часть, хранимую на диске самого первичного OSD.
|
||||||
|
- Когда запрос требует записи или чтения с вторичных OSD, первичный OSD использует заранее
|
||||||
|
установленные соединения с ними для выполнения этих запросов. Это происходит параллельно
|
||||||
|
локальным операциям чтения/записи с диска самого OSD. Так как соединения к вторичным OSD PG
|
||||||
|
устанавливаются при её запуске, то они уже гарантированно установлены, когда PG активна,
|
||||||
|
и если любое из этих соединений отключается, PG перезапускается, а все текущие запросы чтения
|
||||||
|
и записи в неё завершаются с ошибкой EPIPE, после чего повторяются клиентами.
|
||||||
- После завершения всех вторичных операций чтения/записи первичный OSD отправляет ответ клиенту.
|
- После завершения всех вторичных операций чтения/записи первичный OSD отправляет ответ клиенту.
|
||||||
|
|
||||||
### Особенности обработки запросов
|
### Особенности обработки запросов
|
||||||
|
|
||||||
- Если в пуле используются коды коррекции ошибок и при этом часть OSD недоступна, первичный
|
- Если в пуле используются коды коррекции ошибок и при этом часть OSD недоступна, первичный
|
||||||
OSD при чтении восстанавливает данные из оставшихся частей.
|
OSD при чтении восстанавливает данные из оставшихся частей.
|
||||||
- Каждый объект имеет номер версии. При записи объекта первичный OSD сначала читает из номер
|
- Каждый объект имеет номер версии. При записи объекта первичный OSD сначала получает номер
|
||||||
версии объекта. Так как первичный OSD обычно сам хранит копию или часть объекта, номер
|
версии объекта. Так как первичный OSD обычно сам хранит копию или часть объекта, номер
|
||||||
версии обычно читается из памяти самого OSD. Однако, если ни одна часть обновляемого объекта
|
версии обычно читается из памяти самого OSD. Однако, если ни одна часть обновляемого объекта
|
||||||
не находится на первичном OSD, для получения номера версии он обращается к одному из вторичных
|
не находится на первичном OSD, для получения номера версии он обращается к одному из вторичных
|
||||||
|
@ -131,20 +137,20 @@
|
||||||
так как метаданные объектов, включая номер версии, все OSD хранят в памяти.
|
так как метаданные объектов, включая номер версии, все OSD хранят в памяти.
|
||||||
- Если в пуле используются коды коррекции ошибок, перед частичной записью объекта для вычисления
|
- Если в пуле используются коды коррекции ошибок, перед частичной записью объекта для вычисления
|
||||||
чётности зачастую требуется чтение частей объекта с вторичных OSD или с локального диска
|
чётности зачастую требуется чтение частей объекта с вторичных OSD или с локального диска
|
||||||
самого первичного OSD.
|
самого первичного OSD. Это называется процессом "чтение-модификация-запись" (read-modify-write).
|
||||||
- Также, если в пуле используются коды коррекции ошибок, для закрытия Write Hole применяется
|
- Если в пуле используются коды коррекции ошибок, для закрытия Write Hole применяется
|
||||||
двухфазный алгоритм записи: сначала на все вторичные OSD записывается новая версия частей
|
двухфазный алгоритм записи: сначала на все вторичные OSD записывается новая версия частей
|
||||||
объекта, но при этом старая версия не удаляется, а потом, после получения подтверждения
|
объекта, но при этом старая версия не удаляется, а потом, после получения подтверждения
|
||||||
успешной записи от всех вторичных OSD, новая версия фиксируется и разрешается удаление старой.
|
успешной записи от всех вторичных OSD, новая версия фиксируется и разрешается удаление старой.
|
||||||
- Если в кластере не включён режим immediate_commit, то запросы записи, отправляемые клиентами,
|
- Если в пуле не включён режим immediate_commit, то запросы записи, отправляемые клиентами,
|
||||||
не считаются зафиксированными на физических накопителях сразу. Для фиксации данных клиенты
|
не считаются зафиксированными на физических накопителях сразу. Для фиксации данных клиенты
|
||||||
должны отдельно отправлять запросы SYNC (отдельный от чтения и записи вид запроса),
|
должны отдельно отправлять запросы SYNC (отдельный от чтения и записи вид запроса),
|
||||||
а пока такой запрос не отправлен, считается, что записанные данные могут исчезнуть,
|
а пока такой запрос не отправлен, считается, что записанные данные могут исчезнуть,
|
||||||
если соответствующий OSD упадёт. Поэтому, когда режим immediate_commit отключён, все
|
если соответствующий OSD упадёт. Поэтому, когда режим immediate_commit отключён, все
|
||||||
запросы записи клиенты копируют в памяти и при потере соединения и повторном соединении
|
запросы записи клиенты копируют в памяти и при потере соединения и повторном соединении
|
||||||
с OSD повторяют из памяти. Скопированные в память данные удаляются при успешном fsync,
|
с OSD повторяют из памяти. Скопированные в память данные удаляются при успешном SYNC,
|
||||||
а чтобы хранение этих данных не приводило к чрезмерному потреблению памяти, клиенты
|
а чтобы хранение этих данных не приводило к чрезмерному потреблению памяти, клиенты
|
||||||
автоматически выполняют fsync каждые [client_dirty_limit](../config/network.ru.md#client_dirty_limit)
|
автоматически выполняют SYNC каждые [client_dirty_limit](../config/network.ru.md#client_dirty_limit)
|
||||||
записанных байт.
|
записанных байт.
|
||||||
|
|
||||||
## Схожесть с Ceph
|
## Схожесть с Ceph
|
||||||
|
|
|
@ -123,6 +123,14 @@ int disk_tool_t::resize_parse_params()
|
||||||
? parse_size(options["new_journal_offset"]) : dsk.journal_offset;
|
? parse_size(options["new_journal_offset"]) : dsk.journal_offset;
|
||||||
new_journal_len = options.find("new_journal_len") != options.end()
|
new_journal_len = options.find("new_journal_len") != options.end()
|
||||||
? parse_size(options["new_journal_len"]) : dsk.journal_len;
|
? parse_size(options["new_journal_len"]) : dsk.journal_len;
|
||||||
|
if (new_data_len+new_data_offset > dsk.data_device_size)
|
||||||
|
new_data_len = dsk.data_device_size-new_data_offset;
|
||||||
|
if (new_meta_device == dsk.data_device && new_data_offset < new_meta_offset &&
|
||||||
|
new_data_len+new_data_offset > new_meta_offset)
|
||||||
|
new_data_len = new_meta_offset-new_data_offset;
|
||||||
|
if (new_journal_device == dsk.data_device && new_data_offset < new_journal_offset &&
|
||||||
|
new_data_len+new_data_offset > new_journal_offset)
|
||||||
|
new_data_len = new_journal_offset-new_data_offset;
|
||||||
if (new_meta_device == dsk.meta_device &&
|
if (new_meta_device == dsk.meta_device &&
|
||||||
new_journal_device == dsk.journal_device &&
|
new_journal_device == dsk.journal_device &&
|
||||||
new_data_offset == dsk.data_offset &&
|
new_data_offset == dsk.data_offset &&
|
||||||
|
@ -220,10 +228,10 @@ int disk_tool_t::resize_remap_blocks()
|
||||||
}
|
}
|
||||||
for (uint64_t i = 0; i < free_last; i++)
|
for (uint64_t i = 0; i < free_last; i++)
|
||||||
{
|
{
|
||||||
if (data_alloc->get(total_blocks-i))
|
if (data_alloc->get(total_blocks-i-1))
|
||||||
data_remap[total_blocks-i] = 0;
|
data_remap[total_blocks-i-1] = 0;
|
||||||
else
|
else
|
||||||
data_alloc->set(total_blocks-i, true);
|
data_alloc->set(total_blocks-i-1, true);
|
||||||
}
|
}
|
||||||
for (auto & p: data_remap)
|
for (auto & p: data_remap)
|
||||||
{
|
{
|
||||||
|
@ -482,7 +490,7 @@ int disk_tool_t::resize_rewrite_meta()
|
||||||
block_num = remap_it->second;
|
block_num = remap_it->second;
|
||||||
if (block_num < free_first || block_num >= total_blocks-free_last)
|
if (block_num < free_first || block_num >= total_blocks-free_last)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "BUG: remapped block not in range\n");
|
fprintf(stderr, "BUG: remapped block %lu not in range %lu..%lu\n", block_num, free_first, total_blocks-free_last);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
block_num += data_idx_diff;
|
block_num += data_idx_diff;
|
||||||
|
|
Loading…
Reference in New Issue