QoreDB LogoQoreDB
Retour au blog
ArchitectureJournal technique

DuckDB comme moteur de fédération : pourquoi et comment

Quand on travaille avec plusieurs bases de données en parallèle, une question revient vite : comment croiser les données de PostgreSQL avec celles de MongoDB sans tout exporter manuellement ? La plupart des outils contournent le problème en proposant des vues unifiées…

Raphaël – Creator of QoreDBRaphaël – Creator of QoreDB
6 min de lectureMis à jour le 20 mai 2026
DuckDB comme moteur de fédération : pourquoi et comment

Quand on travaille avec plusieurs bases de données en parallèle, une question revient vite : comment croiser les données de PostgreSQL avec celles de MongoDB sans tout exporter manuellement ? La plupart des outils contournent le problème en proposant des vues unifiées superficielles ou en déléguant à un middleware externe. QoreDB prend une autre direction : embarquer DuckDB comme moteur SQL éphémère pour exécuter localement des jointures cross-database.

L'idée centrale est simple. Plutôt que de forcer chaque driver à supporter un protocole de fédération, on extrait les données de chaque source en parallèle, on les charge dans une instance DuckDB en mémoire, et on exécute la requête fédérée localement. DuckDB sert de moteur de calcul temporaire, pas de stockage. Chaque requête crée une instance fraîche qui disparaît après exécution.

Le pipeline de fédération en cinq étapes

Le mécanisme repose sur un pipeline orchestré par le FederationManager côté Rust. La première étape est le parsing. L'utilisateur écrit une requête SQL standard, mais utilise des identifiants en trois ou quatre parties pour référencer des tables sur des connexions différentes. Par exemple, prod_pg.public.users désigne la table users du schéma public sur la connexion aliasée prod_pg. Le parser, basé sur un AST SQL, détecte ces identifiants composés et construit une carte des sources à interroger.

Deuxième étape : la planification. Le planner résout chaque alias vers une session de connexion active et génère un plan de requêtes source. Chaque table fédérée est associée à un alias local temporaire, comme __fed_users_0, et la requête originale est réécrite pour utiliser ces alias à la place des identifiants composés. Cette réécriture traverse l'AST complet : clauses FROM, JOIN, WHERE, SELECT, GROUP BY, sous-requêtes et CTE.

Troisième étape : le fetch parallèle. Le manager utilise tokio::spawn pour lancer les requêtes sur toutes les sources simultanément. Chaque source a un timeout de 30 secondes et une limite de 100 000 lignes par défaut. Si une source atteint cette limite, un warning est remonté dans les métadonnées de résultat, mais l'exécution continue.

Quatrième étape : le chargement. Les données récupérées sont insérées dans des tables temporaires DuckDB via des transactions par batch de 1 000 lignes. Un système de mapping de types convertit les types natifs de chaque moteur source vers les types DuckDB correspondants. Plus de 50 mappings sont gérés, avec un fallback en VARCHAR pour les types non reconnus.

Cinquième et dernière étape : l'exécution. La requête réécrite est lancée sur l'instance DuckDB locale. DuckDB gère les jointures, agrégations et filtres avec son optimiseur OLAP. Les résultats sont renvoyés au frontend, accompagnés de métadonnées détaillées : temps de fetch par source, nombre de lignes, temps d'exécution DuckDB, et éventuels warnings.

Pourquoi DuckDB et pas un autre moteur

DuckDB coche plusieurs cases essentielles pour cet usage. C'est un moteur SQL analytique embarquable, conçu pour fonctionner en processus (in-process), sans serveur externe. Il s'intègre nativement en Rust via des bindings C, ce qui correspond à l'architecture Tauri de QoreDB. Son orientation OLAP le rend performant sur les jointures et agrégations, exactement le type d'opérations qu'on attend d'une fédération.

Le choix d'instancier DuckDB de manière éphémère, en mémoire, pour chaque requête fédérée, est délibéré. Il évite tout problème de cohérence entre les données locales et les sources distantes. Il n'y a pas de cache à invalider, pas d'état persistant à maintenir. Chaque exécution repart de zéro avec des données fraîchement récupérées.

Le cas MongoDB : aplatir du NoSQL en relationnel

Fédérer des bases SQL entre elles est relativement direct : les données ont déjà une structure tabulaire. MongoDB pose un défi différent. Les documents BSON ont un schéma flexible, parfois hétérogène d'un document à l'autre. Le pipeline de fédération inclut une étape de flattening dédiée. Quand les données proviennent de MongoDB, le manager extrait toutes les clés uniques de l'ensemble des documents récupérés, crée des colonnes dynamiques, et convertit les valeurs JSON en types relationnels. Un document avec les champs userId et name devient une ligne avec deux colonnes typées. Les valeurs complexes (objets imbriqués, tableaux) sont sérialisées en texte JSON.

Ce mécanisme permet d'écrire une jointure entre une table PostgreSQL et une collection MongoDB dans une seule requête SQL. Le développeur n'a pas besoin d'exporter manuellement, de transformer les données dans un script intermédiaire, ou de monter une infrastructure ETL. Tout se passe en local, dans le processus QoreDB.

Batch et streaming : deux modes d'exécution

La fédération supporte deux modes. En mode batch, toutes les données sont chargées dans DuckDB, la requête est exécutée, et le résultat complet est renvoyé d'un bloc. C'est le mode par défaut, adapté aux résultats de taille raisonnable. En mode streaming, le pipeline reste identique pour la phase de fetch et de chargement, mais les résultats sont envoyés ligne par ligne au frontend via le canal Tauri. Le frontend reçoit d'abord les colonnes, puis les lignes au fil de l'eau, et enfin un événement de fin. Ce mode est utile quand le résultat de la jointure fédérée est volumineux et que l'utilisateur veut voir les premières lignes rapidement.

En pratique : la réécriture SQL transparente

Du point de vue de l'utilisateur, la fédération se résume à une convention de nommage. On préfixe les tables par l'alias de connexion et le namespace. Le reste de la syntaxe SQL est standard. Par exemple, pour joindre des utilisateurs PostgreSQL avec des commandes MySQL, on écrit quelque chose comme SELECT u.name, o.total FROM prod_pg.public.users u JOIN staging_mysql.shop.orders o ON u.id = o.user_id. Le parser détecte les deux sources, le planner génère les requêtes d'extraction, et DuckDB exécute la jointure.

Les métadonnées de fédération renvoyées au frontend incluent le temps de fetch par source, le nombre de lignes extraites, le temps d'exécution DuckDB, et la liste des warnings éventuels (limite de lignes atteinte, timeout approché). Ces informations sont visibles dans l'interface, ce qui donne au développeur une vision claire de ce qui s'est passé pendant l'exécution.

Un moteur de calcul local, pas un data warehouse

DuckDB dans QoreDB n'est pas utilisé comme un entrepôt de données. Il n'y a pas de persistance, pas de matérialisation de vues, pas de scheduling de rafraîchissement. C'est un moteur de calcul ponctuel, activé à la demande, qui disparaît après chaque requête. Ce positionnement est cohérent avec la philosophie desktop de QoreDB : tout reste local, tout est éphémère par défaut, et l'utilisateur garde le contrôle sur ce qui est exécuté.

La fédération via DuckDB transforme QoreDB d'un simple client multi-bases en un outil capable de croiser les données de n'importe quelle combinaison de sources supportées, dans une seule requête SQL, sans infrastructure additionnelle. Le pipeline est entièrement local, parallélisé, et conçu pour un usage interactif par un développeur ou un SRE qui a besoin de réponses rapides sur des données distribuées.

Newsletter

Restez informé des nouveautés

Rejoignez notre newsletter pour recevoir les mises à jour majeures, les nouveaux drivers et nos coulisses techniques.

🎁 Bonus : Recevez notre fiche mémo d'optimisation SQL — 9 pages, PostgreSQL / MySQL / SQLite (PDF) !
Partager

Explorer

Articles similaires

DuckDB comme moteur de fédération : pourquoi et comment - Blog - QoreDB