Implementacja jądra OLTP: kontrakty API, rozmiar strony i adaptacyjne strojenie
Specjaliści tworzący nową bazę danych OLTP zaimplementowali ścisłą architekturę z izolowanymi warstwami za pomocą interfejsów trait w Rust. Każda warstwa ma precyzyjne kontrakty API, rozmiar strony jest konfigurowalny dla każdego tablespace, a konfiguracja jest zarządzana przez Resource Broker bez ręcznego tuningu. Diagnostyka jest zintegrowana poprzez USDT i eBPF.
Ustawienia rozmiaru strony i tablespace
Rozmiar strony danych waha się od 4-8 KB dla czystego OLTP do 16-32 KB dla HTAP na NVMe. Każdy tablespace to oddzielny plik z niezmiennym rozmiarem strony po utworzeniu. Domyślnie 16 KB. PageId jest złożony: [tablespace_id:16][page_index:48]. BufferPool kieruje zapytania do podbuforów według rozmiaru.
Superblok na początku pliku przechowuje metadane tablespace niezależnie od rozmiaru strony. Podczas montowania sprawdzana jest konfiguracja; niezgodność powoduje odmowę uruchomienia bez degradacji.
Architekturalne warstwy i kontrakty API
Jądro jest podzielone na cztery warstwy:
- Warstwa Adaptera (async tokio): kończy TLS, parsuje protokół sieciowy.
#[async_trait]
pub trait NetworkAdapter {
async fn handle_connection(&self, stream: Box<dyn ConnectionStream>) -> Result<(), NetworkError>;
}
- Warstwa Kompatybilności: parsuje SQL do AST, emuluje pg_catalog, tłumaczy na LogicalPlan, odrzuca nieobsługiwane funkcje (błąd 0A000).
pub trait CompatLayer {
fn translate_query(&self, ast: SqlAst) -> Result<LogicalPlan, CompatError>;
}
- Silnik Rdzenia (sync): optymalizator, executor, menedżer transakcji. Wykonywany w puli spawn_blocking.
pub trait ExecutionEngine {
fn execute_plan(&self, plan: LogicalPlan, session: &Session) -> Result<ResultSet, ExecutionError>;
}
- Menedżer Magazynu: fasada nad PageProvider i TransactionLogSink. Jądro żąda stron i rekordów UNDO.
pub trait StorageManager {
fn pin_page(&self, page_id: PageId, mode: LockMode) -> Result<PageGuard, StorageError>;
fn append_undo(&self, txn_id: TxnId, record: UndoRecord) -> Result<UndoPtr, StorageError>;
}
Granice warstw zapobiegają wyciekom szczegółów: metryki blokad nie uwzględniają asynchronicznego oczekiwania.
Adaptacyjne strojenie i Resource Broker
Około 60 parametrów jest podzielonych na budżety, bariery ochronne i nadpisy. Operator ustawia limity na wysokim poziomie:
[resources]
memory_budget = "16GB"
cpu_budget = "auto"
io_iops = 5000
Doradcy (MemoryAdvisor, IoAdvisor, CpuAdvisor) redistribuują zasoby co sekundę:
- CpuAdvisor: DOP i pula wątków przeciwko throttlingowi.
- MemoryAdvisor: BufferPool vs work_mem pod obciążeniem.
- IoAdvisor: priorytet transakcjom, świadomość burstów w chmurze.
Mechanizmy ochronne
- Hystereza: okno 5 s, kroki 5% przeciwko oscylacjom.
- Twarde minima: min. 128 MB dla BufferPool/UNDO.
- Płynne przejście: nowe limity dla nowych alokacji.
- Eksperckie nadpisy blokują autotuning.
Tło czyszczenia logu UNDO zamiast VACUUM. Zmiany w runtime bez restartu.
Diagnostyka: sondy USDT i eBPF
Mechanizmy diagnostyczne są wbudowane w binarny plik. Sondy USDT do analizy runtime, eBPF do śledzenia bez narzutu.
Co ważne:
- Rozmiar strony per-tablespace z ochroną przy starcie.
- Ścisłe kontrakty trait między warstwami zapobiegają wyciekom.
- Resource Broker: budżety → autotuning z barierami.
- Diagnostyka przez eBPF/USDT w jądrze.
- Rezygnacja z ręcznego tuningu na rzecz mechanizmów adaptacyjnych.
— Editorial Team
Brak komentarzy.