SharedStorage/README.md

213 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

В байтах (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)
1. Клиент читает `head`.
2. Вычисляет `next_head = (head + 1) % capacity`.
3. Проверяет, что `next_head != tail` (иначе очередь заполнена, нужно ждать).
4. Пишет данные в слот `Command` по индексу `head`:
- ставит `ready = 0`
- заполняет `op`, `key`, `value_size`, `value`
- обнуляет `response_size`, `response_ready`
5. После полной записи устанавливает `ready = 1` (memory\_order\_release).
6. После этого обновляет `head = next_head` (memory\_order\_release).
---
# 🧩 Процесс чтения команды сервером
1. Сервер читает `tail`.
2. Если `tail == head`, значит нет команд (ждет).
3. Читает слот `Command` по индексу `tail`.
4. Проверяет `ready == 1`.
5. Выполняет команду:
- для `PUT`, `DELETE` ничего не нужно возвращать
- для `FIND` после обработки:
- заполняет `response`
- устанавливает `response_size`
- устанавливает `response_ready = 1` (memory\_order\_release)
6. После обработки увеличивает `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
```
---
# 📋 Минимальная инструкция для других языков
1. Открыть или создать POSIX shared memory (`/shm_rates1`).
2. Маппить размер (например: 24 + 2080×64 байта = \~133 120 байт).
3. Знать layout структуры `SharedCommandQueue` и `Command`.
4. Атомарно обновлять `head`/`tail` через обычные load/store (x86/arm архитектуры обеспечивают это через aligned операции на 64 бита).
---
# ⚙️ Примерная формула расчета памяти:
```text
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>) |
| 20752079 | 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)
```
---
# 🧪 Мини-псевдокод для любого языка
```pseudo
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`.
---