Ingérer des fichiers en temps réel dans BigQuery

Une des problématiques rencontrées dans le cloud et dans l’IT en général est de pouvoir charger des fichiers dans un DataWarehouse, et de les requêter le plus rapidement possible.

Dans l’écosystème Google Cloud, la solution d'entrepôt de données BigQuery est probablement la plus performante et simple d’utilisation, mais comment charger des données sur cette solution rapidement et de manière efficace ?

Dans cet article, je vais décrire plusieurs exemples de pipeline de données qui vont répondre à la même finalité, mais avec des subtilités qui vont les rendre grandement différents les uns des autres.

Toutes les architectures auront la même source et la même cible : nous allons recevoir des fichiers dans Cloud Storage par différents providers, pour les charger dans BigQuery.

Je pars du principe que les fichiers que nous allons recevoir sont nativement supportés par BigQuery, et que le schéma ne changera pas au fil du temps.

Toutes les architectures proposées sont basées sur ma propre expérience sur des problématiques rencontrées chez des clients finaux et peuvent être obsolètes dans plusieurs années au fur et à mesure que la plateforme GCP ainsi que le marché du cloud évoluent dans le temps.

1 - Table externe

La table externe n’est peut-être pas le moyen le plus puissant en termes de temps d’exécution des requêtes dans BigQuery, mais elle permet une chose, c’est de réduire drastiquement la durée de développement.

La puissance des tables externes réside dans le fait que vous n’aurez pas besoin de charger les données dans BigQuery via du code, puisque celles-ci sont lues directement dans Cloud Storage par le moteur d’exécution de BigQuery puis analysées et traitées.

Les tables externes permettent de répondre à l’use case suivant :

“J’ai besoin de lire de temps en temps des données stockées dans Cloud Storage, je n’ai pas énormément de données et je n’ai pas d’attentes vis-à-vis du temps d'exécution"


Cette méthode est extrêmement simple car tout le code se résume à un simple “create external table”, de plus, aucune latence ne sera constatée car il est possible de lire les nouveaux fichiers depuis la table dès que ceux-ci arrivent dans le bucket.

Le gros inconvénient de cette architecture est qu’elle n’est pas scalable, une table externe doit obligatoirement scanner et ouvrir l’intégralité du bucket pour pouvoir exécuter sa requête, vous risquez d’avoir des temps d’exécution longs voir même de partir en timeout si votre requête est complexe et que vous possédez une volumétrie importante.

2 - BigQuery Transfer Service

Le BigQuery Transfer Service est probablement la solution qui va répondre à une grande majorité des besoins batch, la puissance de cet outil est dans sa simplicité d’utilisation. Tout peut se faire via l’interface web et vous pouvez avoir un pipeline EL en quelques clics, il est même capable de lancer des rattrapages au cas où.

L’utilisation de cet outil se traduit par le besoin suivant :

J’ai besoin de charger mes fichiers stockées dans cloud storage au maximum une fois toutes les heures, j’ai besoin de profiter au maximum de la puissance de BigQuery et je souhaite faire l’étape de transformation ou de nettoyage des fichiers dans BigQuery


Cette solution vous permet de faire un pipeline ELT des plus classiques, cependant elle ne permet pas de faire des chargements instantanés dans BigQuery car l’outil ne peut se déclencher que toutes les heures au maximum.

De plus, vous ne pouvez faire aucune transformation pendant le chargement car il s’agit d’un outil EL, donc tout ce que vous souhaitez faire devra se faire en amont ou alors dans BigQuery via du SQL.

3 - Cloud Functions

Avec le service Cloud Function, on entre dans la catégorie des armes de gros calibre en termes de performance en vous permettant de charger un fichier dans BigQuery quasi instantanément.

Il s’agit d’un service de type FaaS capable d’encaisser des grosses volumétries (dans la limite de votre quota) avec la possibilité d’auto-scaler de manière totalement transparente et instantanée.

De plus, vous pourrez appliquer des transformations via votre langage préféré, dans la mesure où celui-çi est capable de lire l’extension de votre fichier, mais aussi de charger des formats non supportés nativement par BigQuery comme du XML ou alors gérer des schémas personnalisés.

L’utilisation des Cloud Function peut se traduire par le use case suivant :

“ J’ai besoin de charger des fichiers stockés dans Cloud Storage immédiatement dans BigQuery, j’ai une volumétrie qui varie du tout au rien et j’ai besoin d’appliquer des transformations simples comme du nettoyage de champ. Je sais que le langage que j’utilise peux ouvrir mon fichier afin d’en modifier son contenu“

Et voici un schema :


Warning

J’ouvre une grosse parenthèse sur cette architecture car elle possède un inconvénient, si vous souhaitez uniquement charger le fichier via un load et non un insert, il va falloir faire attention aux quotas BigQuery, ceux-ci ne permettent pas de charger plus de 1 500 fichiers en un jour sur une même table.

Si vous ne pouvez pas modifier votre quota, il existe des solutions afin de contourner le problème, il suffit de créer une table toutes les X heures via le client BigQuery sur Cloud Function, puis de requêter toutes les tables via des expressions régulières.

Pour illustrer, si l’on souhaite créer une table nommée MY_TABLE toutes les heures, nous allons nous retrouver avec plusieurs tables qui auront le nom suivant

MY_TABLE_2020_08_20_00
MY_TABLE_2020_08_20_01
MY_TABLE_2020_08_20_02
…
MY_TABLE_2020_08_20_23
MY_TABLE_2020_08_21_00

Et si nous souhaitons lire la journée du 20 Août 2020, nous pouvons exécuter le SQL suivant:

SELECT * FROM MY_TABLE_2020_08_20_*;

Si vous ne souhaitez pas que vos utilisateurs travaillent sur plusieurs tables pour faire leur requêtes, il est possible d’utiliser une “vue partitionnée“ en deux étapes, la première étant de partitionner chacune des tables crées via Cloud Functions, puis de créer une vue exposant la pseudo-colonne de partitionnement via la requête SQL suivante :

SELECT *, EXTRACT(DATE FROM _PARTITIONTIME) AS date FROM MY_TABLE_*;

Vos utilisateurs pourront utiliser toute la puissance du partitionnement dans BigQuery, tout en ayant la possibilité de contourner la limite de quotas.

Pour résumer, l’utilisation de Cloud Functions permet de répondre à des besoins d’ingestion en temps réel, mais elle demandera à l’utilisateur d’avoir la connaissance pour déployer et maintenir du code sur Cloud Functions.

Vous devrez aussi faire attention à l’extension de fichier utilisée car celui-ci ne sera peut-être pas pris en charge par votre langage et donc vous ne pourrez pas réaliser des transformations.

De plus, la facturation au centième de milliseconde vous impose de simplifier au maximum la complexité et le temps de traitement de votre fonction afin de limiter les coûts.

Par exemple, si vous déployez un script python de quelques centaines de lignes avec plusieurs lectures sur des bases de données externes, Cloud Function n’est peut-être pas la solution la plus adaptée.

J’ajouterai enfin que si vous rencontrez des erreurs lors du chargement d’un fichier sur BigQuery, il faudra penser à une logique de sauvegarde vers un espace de stockage dédié aux erreurs afin de gérer ce genre de cas.

4 - Cloud Functions + PubSub + DataFlow

Avec DataFlow, nous entrons dans la catégorie des armes de destruction massive dans la catégorie performance, robustesse et flexibilité.

Il s’agit d’un service de traitement de données parfaitement intégré dans l’écosystème Google Cloud pouvant traiter la volumétrie totale de la plupart des SI Français.

Capable de gérer le batch et le streaming via la même API, il peut auto-scaler en fonction de la demande et s’interfacer avec tous les composants GCP.

Comme son ami Cloud Functions, vous aurez la possibilité de charger des fichiers non supportés nativement par BigQuery ainsi que de gérer des schémas personnalisés.

Ce service vous permet de répondre aux besoins de transformations des plus complexes le plus rapidement possible. Il n’existe pas d’autres services sur la plateforme GCP vous permettant de faire efficacement l’use case suivant :

“Je souhaite charger des fichiers immédiatement dans BigQuery, j’ai une volumétrie qui varie du tout au rien et je souhaite aussi apporter des transformations très complexes ainsi que des jointures sur des bases externes"

Warning

J’ouvre une nouvelle fois une parenthèse pour mettre en garde l'utilisation de DataFlow en mode streaming avec des fichiers.

Nativement, ce service n’est pas optimisé pour lire des fichiers dans Cloud Storage dès qu’ils sont chargés, il doit scanner l’intégralité du bucket de manière périodique pour détecter des nouveaux fichiers avant de les prendre en compte via un paramètre défini dans la configuration de votre FileIO.

Chaque scan peut entraîner des temps d’exécution longs si le bucket contient énormément de fichiers, mais aussi des coûts élevés dans Cloud Storage car le listing est une opération coûteuse.

Il est donc intéressant de placer une Cloud Function qui va s’occuper de surveiller le bucket pour que, dès l’arrivée d’un nouveau fichier, son chemin soit immédiatement poussé dans Pub/Sub, puis DataFlow s’occupera de récupérer ce chemin lu dans un topic dédié, pour lire le fichier dans Cloud Storage.

Des images valent bien plus que des mots, voici le schéma résumant cette architecture.


Avec ceci, vous êtes en mesure de tout faire car rien ne résiste à la puissance de DataFlow, il peut gérer des PetaBytes de données et s’auto-scaler en cas de forte volumétrie soudaine. Vous serez donc capable de gérer les pics d’activités ainsi que d’avoir des coûts faibles lorsque la volumétrie est basse, peu importe la complexité de votre code.

Cependant nous sommes encore à une époque où il n’existe pas d’excellents outils IT sans avoir un défaut, et cette architecture en possède un, c’est que c’est quand-même assez fastidieux !

Vous allez devoir écrire, maintenir et déployer du code pour Cloud Functions et DataFlow, avoir à gérer des abonnements sur Pub/Sub et implémenter un mécanisme de rejeu en cas d’erreur technique.

Mais c’est le prix à payer pour avoir l’arme la plus puissante de la plate-forme Google pour traiter des fichiers en temps réel.