В байтах (C++ layout):
Смещение | Размер | Тип данных | Описание |
---|---|---|---|
0 | 1 | uint8_t (atomic) |
ready (0 = не готова, 1 = готова) |
1 | 1 | uint8_t |
op — код операции (PUT = 0, DELETE = 1, FIND = 2) |
2 | 16 | uint8_t[16] |
key — хэш ключа (Hash128, 128 бит) |
18 | 4 | uint32_t |
value_size — длина данных value |
22 | 1024 | uint8_t[1024] |
value — содержимое для записи (PUT) |
1046 | 4 | uint32_t |
response_size — длина ответа в response |
1050 | 1024 | uint8_t[1024] |
response — содержимое ответа после FIND |
2074 | 1 | uint8_t (atomic) |
response_ready (0 = нет ответа, 1 = ответ готов) |
--- | --- | Итого: 2075 байт на одну команду |
⚠️ Реально в памяти компилятор добавит выравнивание.
Обычно команда занимает 2080 байт с учетом паддинга (например, до 8 байт).
2. Структура очереди SharedCommandQueue
Смещение | Размер | Тип данных | Описание |
---|---|---|---|
0 | 8 | size_t (atomic) |
head (где следующий push) |
8 | 8 | size_t (atomic) |
tail (где следующий pop) |
16 | 8 | size_t |
capacity (количество слотов) |
24 | 2080 × N | Command[N] |
Массив команд |
N = максимальное количество команд в очереди. Например: 64.
📦 Процесс записи команды (PUT, FIND, DELETE)
- Клиент читает
head
. - Вычисляет
next_head = (head + 1) % capacity
. - Проверяет, что
next_head != tail
(иначе очередь заполнена, нужно ждать). - Пишет данные в слот
Command
по индексуhead
:- ставит
ready = 0
- заполняет
op
,key
,value_size
,value
- обнуляет
response_size
,response_ready
- ставит
- После полной записи устанавливает
ready = 1
(memory_order_release). - После этого обновляет
head = next_head
(memory_order_release).
🧩 Процесс чтения команды сервером
- Сервер читает
tail
. - Если
tail == head
, значит нет команд (ждет). - Читает слот
Command
по индексуtail
. - Проверяет
ready == 1
. - Выполняет команду:
- для
PUT
,DELETE
ничего не нужно возвращать - для
FIND
после обработки:- заполняет
response
- устанавливает
response_size
- устанавливает
response_ready = 1
(memory_order_release)
- заполняет
- для
- После обработки увеличивает
tail = (tail + 1) % capacity
(memory_order_release).
📑 Коды операций
Код | Операция |
---|---|
0 | PUT |
1 | DELETE |
2 | FIND |
📈 Диаграмма
Client (head) Server (tail)
↓ ↑
[ empty ][ empty ][ empty ][ empty ][ empty ]
↑ write PUT -> ready=1 ↑ read -> process -> tail++
↑ write FIND -> ready=1 ↑ read -> process -> response_ready=1
📋 Минимальная инструкция для других языков
- Открыть или создать POSIX shared memory (
/shm_rates1
). - Маппить размер (например: 24 + 2080×64 байта = ~133 120 байт).
- Знать layout структуры
SharedCommandQueue
иCommand
. - Атомарно обновлять
head
/tail
через обычные load/store (x86/arm архитектуры обеспечивают это через aligned операции на 64 бита).
⚙️ Примерная формула расчета памяти:
total_size = 24 + (round_up(sizeof(Command)) × capacity)
- 24 байта на
head
+tail
+capacity
- capacity = число команд
- sizeof(Command) = около 2080 байт
❗ Важно
head
иtail
— должны быть атомарными.- Доступ к слотам только по индексам
(head % capacity)
или(tail % capacity)
. - Все указанные поля фиксированы и известны по смещению.
- Все строки (
value
,response
) — это просто байтовые массивы без\0
.
📦 Памятка: Структура SharedCommandQueue в памяти
Общая структура:
Смещение (байты) | Размер | Описание |
---|---|---|
0 | 8 | head (size_t, atomic) |
8 | 8 | tail (size_t, atomic) |
16 | 8 | capacity (size_t, НЕ atomic) |
24 | 2080 × N | Команды Command[] (N штук подряд) |
📦 Памятка: Структура одного Command
Смещение | Размер | Описание |
---|---|---|
0 | 1 byte | ready (atomic<uint8_t>) |
1 | 1 byte | op (uint8_t) |
2 | 16 bytes | key (Hash128, 128 бит) |
18 | 4 bytes | value_size (uint32_t) |
22 | 1024 bytes | value (байтовый массив данных) |
1046 | 4 bytes | response_size (uint32_t) |
1050 | 1024 bytes | response (ответ от сервера) |
2074 | 1 byte | response_ready (atomic<uint8_t>) |
2075–2079 | 5 bytes | padding (выравнивание до 8 байт) |
⚡ Суммарный размер одной команды с паддингом = 2080 байт.
📋 HEX Layout всего буфера (SharedCommandQueue + команды)
00:00 - 00:07 -> head (size_t, LE)
00:08 - 00:0F -> tail (size_t, LE)
00:10 - 00:17 -> capacity (size_t, LE)
00:18 - 00:18+2080*N -> N команд, каждая:
00:00 -> ready (1 byte)
00:01 -> op (1 byte)
00:02 - 00:11 -> key (16 bytes)
00:12 - 00:15 -> value_size (4 bytes, LE)
00:16 - 04:15 -> value (1024 bytes)
04:16 - 04:19 -> response_size (4 bytes, LE)
04:20 - 08:1F -> response (1024 bytes)
08:20 -> response_ready (1 byte)
08:21 - 08:27 -> padding (5 bytes)
🧪 Мини-псевдокод для любого языка
ptr = mmap(...)
head = read_uint64(ptr, 0)
tail = read_uint64(ptr, 8)
capacity = read_uint64(ptr, 16)
command_base = 24
// Пример: прочитать первую команду (index = 0)
offset = command_base + index * 2080
ready = read_uint8(ptr, offset + 0)
op = read_uint8(ptr, offset + 1)
key = read_bytes(ptr, offset + 2, 16)
value_size = read_uint32(ptr, offset + 18)
value = read_bytes(ptr, offset + 22, value_size)
response_ready = read_uint8(ptr, offset + 2074)
response_size = read_uint32(ptr, offset + 1046)
response = read_bytes(ptr, offset + 1050, response_size)
📊 Краткая таблица памяти:
Поле | Тип | Офсет | Размер |
---|---|---|---|
head | size_t (8B) | 0 | 8B |
tail | size_t (8B) | 8 | 8B |
capacity | size_t (8B) | 16 | 8B |
commands | Command[N] | 24 | 2080×N |
📢 Важные правила при использовании
- Всегда выравнивание по 8 байтам (иначе UB на ARM/M1).
- Использовать little-endian (x86, arm64 всегда LE).
- При копировании строк следить за value_size / response_size.
- Проверять флаг
ready
перед чтением команды. - После обработки команды — инкрементировать
tail
мод capacity. - Ответ через
response
+response_size
+response_ready
только дляFIND
.
Конфиг
Пример:
# Список баз данных
[[database]]
name = "rates1"
[[database]]
name = "test"
# Настройки базы rates1
[databases.rates1]
shm_queue_capacity = 2048 # Размер очереди команд в shared memory (количество команд)
max_memtable_size = 2097152 # Максимальный размер активной Memtable в байтах (до сброса на диск)
l0_queue_capacity = 2048 # Размер очереди для фоновой компакции L0 SSTables
estimated_element_size = 256 # Оценка среднего размера одного элемента в Memtable в байтах
# Настройки базы test
[databases.test]
shm_queue_capacity = 1024
max_memtable_size = 1048576
l0_queue_capacity = 1024
estimated_element_size = 128
Замечания:
Если параметры не заданы для конкретной базы, используются значения по умолчанию:
shm_queue_capacity = 1024
max_memtable_size = 1048576
l0_queue_capacity = 1024
estimated_element_size = 128
Все размеры (max_memtable_size, estimated_element_size) указываются в байтах.
Shared Memory для каждой базы будет создана с именем /shm_<имя_базы>.
Очереди (l0_queue, shm_queue) построены на lock-free кольцевых буферах.
Полное описание параметров конфига
Параметр | Тип | Описание |
---|---|---|
shm_queue_capacity |
size_t |
Размер очереди команд в shared memory. Это количество слотов команд (PUT/DELETE/FIND), которые могут одновременно находиться в памяти. Если очередь заполнится — новые команды временно не смогут быть отправлены. Важно: shared memory выделяется исходя из этого размера, поэтому очередь большого размера требует больше памяти. |
max_memtable_size |
size_t |
Максимальный размер активной Memtable в байтах. Когда объем данных в Memtable превышает это значение, Memtable сбрасывается на диск в SSTable-файл. Если значение маленькое — будет много мелких SSTable-файлов. Если значение большое — реже будет происходить сброс, но потребление оперативной памяти будет выше. |
l0_queue_capacity |
size_t |
Размер очереди файлов для компакции L0 уровня. Когда Memtable сбрасывается в SSTable, файл ставится в очередь на компакцию. Этот параметр задает максимальное количество файлов, которые можно держать в очереди одновременно. Если значение маленькое — компакция будет происходить чаще. Если большое — будет задержка в обработке компакции при большом потоке вставок. |
estimated_element_size |
size_t |
Оценка среднего размера одного элемента в Memtable. Используется для приближенного расчета реального размера Memtable. Нужно, чтобы не замерять точно каждый вставленный элемент, а быстро определять когда пора сбрасывать Memtable. Типичный случай: текстовые значения без больших вложений имеют размер 64-256 байт. |
Description
Languages
C++
95.7%
CMake
3.2%
C
1.1%