introduction
Bien qu’ayant passé de mode ces dernières années, les flux RSS et leurs cousins les flux Atom sont encore largement utilisés auprès des applications Web afin d’exposer simplement et facilement leurs derniers événements consultables. Le site Web d’un journal peut lister ses derniers articles publiés. Un entrepôt logiciel peut rendre visible la publication d’une nouvelle version d’une bibliothèque logicielle. Un réseau social peut indiquer les derniers commentaires associés à un post.
Bien qu’étant réputés pour leur utilisation par des humains (par le biais d’une application les récupérant et les affichant automatiquement), ces flux ont également une vocation plus technique. Par exemple, il m’est arrivé d’utiliser les flux de Pypi afin de repérer automatiquement la sortie de mises à jour pour des bibliothèques logicielles utilisées dans le cadre d’un projet, ce qui facilitait le processus de montée en version. Un simple script peut repérer la sortie d’une nouvelle édition d’une revue scientifique et la télécharger automatiquement au format pdf.
Lorsque le nombre de flux supervisés augmente et que leur usage se complexifie, il peut être intéressant de les considérer comme des sources de données sur lesquelles on peut soumettre des requêtes, effectuer des croisements d’informations, réaliser des traitements.
Dans cet article, je présente un plugin pour Trino qui propose un connecteur pour les flux RSS.
Mise en œuvre
Rappelons que Trino est une application qui permet la représentation de différentes sources de données sous la forme de tables relationnelles interrogeables en SQL. En plus d’uniformiser leur interrogation, Trino facilite l’intégration de ces différentes sources au sein d’un système unique.
Afin de créer un nouveau connecteur, plusieurs concepts doivent être maîtrisés.
Un plugin est un composant logiciel écrit en Java qui permet d’étendre les capacités de Trino en proposant de nouveaux connecteurs (ce qui nous intéresse), de nouvelles fonctions de transformation des données, de nouveaux types de données, etc.
Un connecteur peut être exploité par un ou plusieurs catalogues qui précisent, grâce à leurs fichiers de configuration, la manière d’accéder à la source de données sous-jacente. Dans notre cas, le seul élément de configuration sera la liste des URL pointant vers les fichiers XML décrivant les flux RSS qui nous intéressent.
Un plugin repose essentiellement sur l’implémentation de fabriques définies par Trino: on implémente un ConnectorFactory qui génère à la demande des connecteurs. Un connecteur expose des TableFactory capable de générer des tables, au sens relationnel du terme, qui définissent la structure des données pouvant être générées.
Chaque table expose également des splits: des sous-unités de la source de données que Trino peut exploiter simultanément au moyen de ses différentes unités de traitement. Dans le cadre de ce plugin, chaque flux RSS défini dans le fichier de configuration d’un catalogue constituera un split.
Notre connecteur ne définit que deux tables: channel, dont les entrées représentent un flux RSS, et item, dont les entrées représentent les différents éléments contenus dans un flux. RSS ne spécifie qu’un petit nombre de champs obligatoires pour ces deux types d’informations, et ils ne sont pas toujours présents dans les flux de données. Chaque fournisseur y va de ses petites personnalisations et extensions. En particulier, le format des dates n’est pas standardisé et il faut que leur parseur se montre suffisamment flexible pour supporter toutes sortes de variations de ce format.
Les requêtes étant traitées en considérant les splits exposés par chaque table, la récupération des données d’un flux RSS se fait en téléchargeant et en parsant deux fois le fichier XML du flux, ce qui représente un gaspillage de ressources, en particulier celles du réseau. Chaque fichier XML étant typiquement de taille modeste, un raffinement prévu du plugin consiste à implémenter un petit proxy qui cache un fichier pendant un bref moment, par exemple une minute. Cela permettrait de n’interroger et ne parser la ressource distante qu’une fois par requête.
Démonstration
Déployons notre plugin pour jouer un peu avec les flux RSS.
Commencez par récupérer sur Github un fichier jar contenant le plugin. Il s’agit d’un fat jar, c’est-à-dire qu’il contient toutes les dépendances sont il a besoin pour fonctionner. Pratique pour réaliser des tests, mais on préférera souvent gérer ces dépendances sous la forme de jars distincts dans des environnements de production.
Écrivez un fichier de configuration pour un catalogue basé sur le connecteur implémenté dans ce plugin:
connector.name=rss
rss.uris=https://feeds.bbci.co.uk/news/rss.xml,https://www.nasa.gov/rss/dyn/lg_image_of_the_day.rss
Exécutez une instance de l’image Docker de Trino accessible sur DockerHub:
docker run -p 8080:8080 --mount type=bind,source="/path/to/rss.properties",target="/etc/trino/catalog/rss.properties" --mount type=bind,source="/path/to/trino-connector-rss-X.Y-with-dependencies.jar",target="/etc/trino/plugin/rss.jar" --name trino trinodb/trino:464
Depuis le client JDBC de votre choix, créez une connexion vers Trino.

Dans la base de données rss (le nom correspond à celui du fichier de configuration du catalogue), vous trouverez un schéma default contenant les tables channel et item.

Réalisons une jointure entre ces deux tables pour conclure l’exercice:
SELECT c.title, i.title, i.description, i.pub_date
FROM rss.default.channel AS c, rss.default.item AS i
WHERE c.link = i.channel_link

It works.
Pour conclure
Le développement d’un nouveau connecteur pour Trino nécessite la maîtrise de quelques concepts fondamentaux. L’approche par plugin nous facilite les choses, puisqu’il « suffit » d’intégrer notre développement dans le framework existant. Dans le cadre de notre plugin, certains aspects plus avancés, tels que les mécanismes de pushdown des contraintes de filtrage, n’ont pas été mis en œuvre car ils n’étaient pas pertinents au vu de la nature de notre source de données.
Est-ce parce que le plugin a été développé dans un moment où Trino subissait des changements conséquents ou est-ce propre à l’outil, toujours est-il qu’il a fallut choisir soigneusement les versions de la bibliothèque de Trino et de son image Docker pour parvenir à un résultat fonctionnel.
La documentation de Trino propose un exemple (basé sur des fichiers CSV accessibles en HTTP), mais le code source associé est un peu trop simple pour être généralisé. L’établissement d’une correspondance entre les champs de la source de données et les attributs des tables disponibles (aussi bien en ordonnancement qu’en type) ne va pas de soi.
L’expérience est, dans l’ensemble, positive. Elle confirme l’intérêt de l’outil et son potentiel pour faciliter l’exploitation de sources de données hétérogènes. Il reste cependant quelques étapes à franchir en terme de complexité afin de supporter des sources plus riches et plus volumineuses.