Retour à l'accueil

Conception de DB OLTP en Rust : principes

L'article décrit les principes de conception d'une base de données OLTP à partir de zéro en Rust : restrictif par défaut, contract-first via traits, asynchronie hybride. Stockage unifié implémenté, MVCC, WAL. Focus sur la prévisibilité et la fiabilité pour la production.

DB OLTP à partir de zéro : architecture et implémentation en Rust
Advertisement 728x90

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.

Google AdInline article slot

| Composant | Limite | Action en cas de violation | SQLSTATE |

|-----------|---------|------------------------|----------|

| BufferPool | buffer_pool_size_mb | Éviction (CLOCK), vidage prioritaire WAL | — |

Google AdInline article slot

| 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 |

Google AdInline article slot

| 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

Advertisement 728x90

Lire ensuite