Architecture de base de données OLTP : des principes à l'implémentation en Rust
Les principaux sous-systèmes du moteur OLTP ont été implémentés : stockage unifié dans un seul fichier par base de données, stockage sur disque avec MVCC via journal UNDO, récupération basée sur WAL et ARIES, un BufferPool partagé, un moteur en mémoire, et un protocole pgwire compatible avec PostgreSQL. Le code est fonctionnel mais nécessite des tests de charge et la stabilisation des cas limites. Ce n'est pas un produit final mais un stade précoce avec des contrats établis.
Principes de conception fondamentaux
Le projet repose sur des règles strictes pour la prise de décision. Chaque nouvelle décision doit s'y conformer ou justifier tout écart.
Restrictif par défaut
Les composants ont des limites explicites avec un comportement de fermeture en cas d'échec plutôt qu'une limitation. Cela garantit la prévisibilité sous charge.
| Composant | Limite | Action en cas de violation | SQLSTATE |
|-----------|---------|------------------------|----------|
| BufferPool | buffer_pool_size_mb | Éviction (CLOCK), vidage prioritaire WAL | — |
| TxnWriteSet | txn_max_write_set_mb | Rejet DML | 54023 |
| UndoStore | undo_max_size_mb | Rejet des écritures | 53100 |
| Pool de connexions | max_connections | Rejet des nouvelles connexions | 53300 |
| Délai d'exécution | statement_timeout_ms | Annulation de la requête | 57014 |
| Âge du snapshot | max_snapshot_age | Fermeture forcée des snapshots obsolètes | 40001 |
La fermeture en cas d'échec fournit des codes SQLSTATE clairs pour la logique de réessai d'application ou de disjoncteur.
Approche Contrat d'abord
Les contrats architecturaux sont définis dans les traits Rust : TableEngine, PageProvider, TransactionLogSink, StorageIo. Cela empêche la dérive d'implémentation en imposant les invariants au moment de la compilation.
Pourquoi Rust ?
Rust a été choisi pour sa latence prévisible, son contrôle de l'allocation mémoire et sa sécurité des types. Send/Sync aux frontières async/sync, erreurs explicites au lieu de problèmes d'exécution. C++ nécessite une discipline supplémentaire, tandis que Go est inadapté en raison du GC pour la latence de queue OLTP.
Séparation des responsabilités
La base de données fonctionne comme un moteur de données sans logique métier : pas de déclencheurs, de PL/pgSQL ou de procédures stockées initialement. Les fonctions définies par l'utilisateur pourront être ajoutées ultérieurement avec un bac à sable et sans effets secondaires.
Asynchronicité hybride
- Couche réseau/pgwire : async (acceptation, TLS, envoi).
- Noyau (exécution des requêtes, stockage, WAL, MVCC) : sync pour la correction et le débogage.
- La frontière est unidirectionnelle via un pont.
StorageIo est abstrait pour permettre une I/O asynchrone future sans réécrire la logique de transaction.
| Couche | Runtime | Exemples |
|------|---------|---------|
| Réseau/Protocole | async | pgwire, TLS |
| Exécution des requêtes | sync | plan, exécution |
| Stockage/WAL | sync (I/O async plus tard) | HeapStore, BufferPool |
| MVCC/Transactions | sync | snapshot, verrous |
Compatibilité PostgreSQL
Pgwire permet l'intégration avec les pilotes et ORM sans migration client. La compatibilité est au niveau du protocole via une couche frontière, sans copier les internes de PostgreSQL (tas MVCC, VACUUM). Le noyau conserve sa liberté architecturale.
Contraintes de plateforme et fiabilité
Linux uniquement pour se concentrer sur io_uring et eBPF sans compromis multiplateforme. Priorités : pas de corruption de données, annulation sécurisée des optimisations, résilience aux entrées utilisateur, sécurité intégrée.
Points clés à retenir
- Restrictif par défaut avec fermeture en cas d'échec pour la prévisibilité sous charge.
- Contrat d'abord via les traits Rust pour le contrôle des invariants.
- Async/sync hybride : stabilité du noyau avant l'accélération I/O.
- Compatibilité pgwire sans copier les internes de PostgreSQL.
- Moteur de données sans logique métier pour la pureté et la testabilité.
— Editorial Team
Aucun commentaire pour le moment.