343f479127
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
- Update architecture diagram to include WebSocket server (port 9913) - Fix HTTP port from 9470 to 9912 (TCP + 440) in protocol docs - Fix WebSocket port from 9471 to 9913 (TCP + 441) in protocol docs - Fix WebSocket Nim example: remove non-existent onMessage API and unsafe cast[string](data), use correct WsServer API with safe helpers - Clarify TCP_NODELAY is also handled internally by hunos (HTTP) - Note that bytesToString/stringToBytes are wire-protocol helpers
12 KiB
12 KiB
Протоколна Референция
BaraDB поддържа множество протоколи за клиентска комуникация:
- Бинарен Wire Протокол — високопроизводителен, ниска латентност
- HTTP/REST API — езиково-независим, лесен за дебъгване
- WebSocket — стрийминг и pub/sub
Бинарен Wire Протокол
Бинарният протокол използва big-endian кодиране за всички многобайтови стойности.
Оптимизации на Връзката
- TCP_NODELAY — Включен както на сървърните, така и на клиентските сокети за минимизиране на латентността при малки съобщения (заявки, acks). Това деактивира алгоритъма на Nagle, гарантирайки че пакетите се изпращат незабавно без буфериране.
- Rate Limiting — Token-bucket rate limiter е интегриран на ниво протокол. Заявки надвишаващи лимита получават код за грешка 429.
Жизнен Цикъл на Връзката
Клиент Сървър
| |
|─── TCP свързване ───────────>|
|<── TLS ръкостискане (опц.) ──|
|─── Auth съобщение ──────────>|
|<── Auth_OK / Грешка ─────────|
|─── Query съобщение ─────────>|
|<── Data / Complete / Error ──|
|─── Close съобщение ─────────>|
|<── TCP затваряне ────────────|
Формат на Съобщенията
Всяко съобщение започва с 12-байтов хедър:
┌─────────────┬─────────────┬─────────────┬─────────────────────┐
│ Kind │ Length │ RequestId │ Payload │
│ (4 bytes) │ (4 bytes) │ (4 bytes) │ │
│ uint32 BE │ uint32 BE │ uint32 BE │ (Length bytes) │
└─────────────┴─────────────┴─────────────┴─────────────────────┘
Типове Съобщения
| Тип | ID | Посока | Описание |
|---|---|---|---|
| Query | 0x01 | К→С | Изпълни заявка |
| QueryParams | 0x02 | К→С | Параметризирана заявка |
| Auth | 0x07 | К→С | Заявка за автентикация |
| Ping | 0x09 | К→С | Keepalive ping |
| Close | 0x0A | К→С | Затваряне на връзка |
| Data | 0x81 | С→К | Резултат от заявка |
| Complete | 0x82 | С→К | Заявката е завършена |
| Auth_OK | 0x83 | С→К | Успешна автентикация |
| Pong | 0x84 | С→К | Keepalive отговор |
| Error | 0x06 | С→К | Грешка |
Query Съобщение Payload
┌───────────────────┬────────────────────────────┐
│ Query String │ │
│ (променлива) │ │
│ UTF-8 │ │
└───────────────────┴────────────────────────────┘
Data Съобщение Payload
┌──────────────┬─────────────────────────────────────────────┐
│ Брой Колони │ Дефиниции на Колони + Данни от Редове │
│ (4 bytes) │ │
│ uint32 BE │ │
└──────────────┴─────────────────────────────────────────────┘
Типове Полета
| Тип | ID | Размер | Описание |
|---|---|---|---|
| NULL | 0x00 | 0 | NULL стойност |
| BOOL | 0x01 | 1 | true/false |
| INT8 | 0x02 | 1 | Signed 8-bit integer |
| INT16 | 0x03 | 2 | Signed 16-bit integer |
| INT32 | 0x04 | 4 | Signed 32-bit integer |
| INT64 | 0x05 | 8 | Signed 64-bit integer |
| FLOAT32 | 0x06 | 4 | IEEE 754 единична точност (big-endian) |
| FLOAT64 | 0x07 | 8 | IEEE 754 двойна точност (big-endian) |
| STRING | 0x08 | променлив | UTF-8 низ (4-байтов префикс за дължина) |
| BYTES | 0x09 | променлив | Сурови байтове (4-байтов префикс за дължина) |
| ARRAY | 0x0A | променлив | Масив от стойности |
| OBJECT | 0x0B | променлив | Ключ-стойност обект |
| VECTOR | 0x0C | променлив | Float32 масив (4-байтов префикс, big-endian floats) |
Error Съобщение Payload
┌──────────────┬──────────────┬────────────────────────────┐
│ Код Грешка │ Дълж. Съобщ. │ Съобщение за Грешка │
│ (4 bytes) │ (4 bytes) │ (Дълж. Съобщ. bytes) │
│ uint32 BE │ uint32 BE │ UTF-8 │
└──────────────┴──────────────┴────────────────────────────┘
HTTP/REST API
Базов URL: http://localhost:9912/api/v1 (HTTP порт = TCP порт + 440)
Rate Limiting
Всички query endpoints (/query, /batch) са защитени от token-bucket rate limiter:
- Глобален лимит — Конфигурира се чрез
BARADB_RATE_LIMIT_GLOBAL(по подразбиране: 10,000 заявки/минута) - Per-client лимит — Конфигурира се чрез
BARADB_RATE_LIMIT_PER_CLIENT(по подразбиране: 1,000 заявки/минута) - Идентификация на клиент — Използва
X-Forwarded-Forхедър когато е зад прокси, иначе използва глобален ключ
При rate limiting, сървърът връща HTTP 429 с:
{"error": "Rate limit exceeded"}
Endpoints
Health
GET /health
Отговор:
{
"status": "healthy",
"version": "1.1.4",
"uptime_seconds": 86400
}
Ready
GET /ready
Връща 200 когато е готов, 503 при стартиране.
Query
POST /query
Content-Type: application/json
Authorization: Bearer <token>
{
"query": "SELECT name, age FROM users WHERE age > 18",
"params": [],
"format": "json"
}
Отговор:
{
"columns": ["name", "age"],
"rows": [
["Alice", 30],
["Bob", 25]
],
"row_count": 2,
"duration_ms": 12
}
Batch
POST /batch
Content-Type: application/json
{
"queries": [
"INSERT users { name := 'Alice', age := 30 }",
"INSERT users { name := 'Bob', age := 25 }"
]
}
Отговор:
{
"results": [
{"status": "ok", "affected_rows": 1},
{"status": "ok", "affected_rows": 1}
]
}
Schema
GET /schema
Metrics
GET /metrics
Prometheus-съвместими метрики. Виж Ръководство за Мониторинг.
Explain
POST /explain
Content-Type: application/json
{
"query": "SELECT * FROM users WHERE age > 18"
}
Отговор:
{
"plan": "IndexScan",
"index": "idx_users_age",
"estimated_rows": 42,
"cost": 120
}
Backup
POST /backup
Content-Type: application/json
{
"destination": "/backup/snapshot.db"
}
Административни Операции
POST /admin/compact
POST /admin/rebalance
POST /admin/check
HTTP Статус Кодове
| Код | Значение |
|---|---|
| 200 | Успех |
| 400 | Лоша заявка (синтактична грешка) |
| 401 | Неоторизиран (изисква се auth) |
| 403 | Забранен (недостатъчни права) |
| 404 | Не е намерен (таблица/тип не съществува) |
| 429 | Твърде много заявки (rate limited) |
| 500 | Вътрешна сървърна грешка |
| 503 | Услугата е недостъпна (стартира) |
WebSocket Протокол
URL: ws://localhost:9913 (WebSocket порт = TCP порт + 441)
Формат на Frame
WebSocket текстови frame-ове съдържат JSON съобщения:
{
"id": 1,
"type": "query",
"query": "SELECT * FROM users"
}
Типове Съобщения
| Тип | Посока | Описание |
|---|---|---|
query |
К→С | Изпълни заявка |
subscribe |
К→С | Абониране за промени |
unsubscribe |
К→С | Отписване |
ping |
К→С | Keepalive |
result |
С→К | Резултат от заявка |
notification |
С→К | Известие за промяна |
error |
С→К | Грешка |
pong |
С→К | Keepalive отговор |
Pub/Sub Пример
const ws = new WebSocket('ws://localhost:9913');
ws.onopen = () => {
// Абониране за промени в таблица
ws.send(JSON.stringify({
id: 1,
type: 'subscribe',
table: 'users'
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'notification') {
console.log('Промяна:', msg.operation, msg.data);
}
};
Стрийминг Заявки
ws.send(JSON.stringify({
id: 2,
type: 'query',
query: 'SELECT * FROM logs ORDER BY timestamp',
streaming: true
}));
// Сървърът изпраща множество result frame-ове
// Последният frame има {"complete": true}
Nim API Примери
Бинарен Протокол
import barabadb/protocol/wire
let msg = makeQueryMessage(1, "SELECT * FROM users")
let ready = makeReadyMessage(1)
let error = makeErrorMessage(1, 42, "Syntax error")
Connection Pool
import barabadb/protocol/pool
var pool = newConnectionPool(
minConnections = 5,
maxConnections = 100,
idleTimeout = 30000
)
let conn = pool.acquire()
# Използване на връзка...
pool.release(conn)
Автентикация
import barabadb/protocol/auth
var am = newAuthManager("secret-key")
let token = am.createToken(JWTClaims(sub: "user1", role: "admin"))
let result = am.validateCredentials(
AuthCredentials(authMethod: amToken, payload: token)
)
Rate Limiting
import barabadb/protocol/ratelimit
var rl = newRateLimiter(
rlaTokenBucket,
globalRate = 10000,
perClientRate = 1000
)
if rl.allowRequest("client-123"):
echo "Заявката е разрешена"