Article
· Août 25 8m de lecture

Pilote pour connecter IRIS à Metabase

Bonjour à toute la communauté InterSystems ! Je m'appelle Sidd, je suis stagiaire au bureau de Singapour et j'ai récemment eu l'occasion de développer un pilote afin de connecter IRIS à Metabase pour aider certains ingénieurs commerciaux ici. On m'a encouragé à partager cette information ici afin que ceux qui rencontrent un problème similaire puissent utiliser ce pilote et faire part de leurs commentaires sur les améliorations possibles. Le dépôt GitHub complet avec la procédure de démarrage rapide, un bref aperçu et le processus de création du pilote est disponible ici. L'objectif principal de cet article est d'approfondir le code principal du pilote et de discuter des idées de son amélioration.

Bref aperçu

La motivation à l'origine de ce pilote est assez simple : Metabase n'offre pas de prise en charge JDBC native pour InterSystems IRIS, nous avions donc besoin d'un pilote personnalisé pour combler l'écart entre le backend de Metabase et les bases de données IRIS. Sans ce pont, il n'y a tout simplement aucun moyen de connecter Metabase aux instances IRIS, ce qui pose évidemment un problème si vous essayez de créer des tableaux de bord et des analyses à partir de vos données IRIS.

La bonne nouvelle, c'est que comme IRIS est déjà fourni avec son propre pilote JDBC et que Metabase dispose d'une implémentation générique solide du pilote SQL JDBC, je n'ai pas eu à réinventer la roue. La plupart des tâches lourdes, telles que l'établissement des connexions à la base de données, le traitement des requêtes de base, la gestion des pools de connexions et les opérations SQL standard, ont pu être déléguées à l'infrastructure générique existante du pilote Metabase. Cela a permis d'économiser énormément de temps de développement et d'assurer la compatibilité avec les fonctionnalités principales de Metabase.

Remplacement des méthodes multiples

L'approche consistait essentiellement à démarrer un pilote minimal, à le tester par rapport à IRIS, à identifier les points faibles où des dysfonctionnements ou des comportements inattendus se produisaient, puis à remplacer de manière sélective ces méthodes par des implémentations spécifiques à IRIS.

Structure du pilote

La structure du pilote constitue la base en définissant les capacités SQL d'IRIS dans le contexte de Metabase et un système robuste de mappage des types qui traduit les types de données IRIS JDBC vers le système de types interne de Metabase.

Ceci est suivi de quelques fonctions DateTime qui permettent à Metabase de connaître la manière de générer le SQL nécessaire pour les visualisations de séries chronologiques. Cela n'a pas encore été testé de manière rigoureuse, mais plutôt implémenté en s'inspirant d'implémentations similaires dans d'autres pilotes JDBC.

Connexion à IRIS

L'une des méthodes les plus importantes à remplacer serait la méthode sql-jdbc.conn/connection-details->spec, qui permet au pilote d'établir une connexion avec IRIS JDBC.

(defn- jdbc-spec
    [{:keys [host port namespace user password additional-options]
        :or {host "localhost", port 1972, namespace "USER"}
        :as details}]

    (-> {:classname "com.intersystems.jdbc.IRISDriver"
    :subprotocol "IRIS"
    :subname (str "//" host ":" port "/" namespace)
    :user user
    :password password}
    (merge (dissoc details :host :port :namespace :user :password :additional-options))
    (sql-jdbc.common/handle-additional-options additional-options)))

(defmethod sql-jdbc.conn/connection-details->spec :iris-jdbc
    [_ details-map]
    (jdbc-spec details-map))

Cette méthode fait appel à la méthode d'assistance jdbc-spec qui lit d'abord les données saisies par l'utilisateur avant de créer une chaîne de connexion et de la transmettre à la fonction sql-jdbc appropriée qui se chargera de connecter l'application à l'instance IRIS.

Description des bases de données

Lorsque Metabase se connecte pour la première fois à une base de données, le programme exécute une série de requêtes SQL afin de déterminer, entre autres, les tables auxquelles l'utilisateur a accès ainsi que les métadonnées de ces tables. Cela se fait via un appel à la méthode describe-database, qui appelle ensuite sql-jdbc.sync/have-select-privilege? et sql-jdbc.sync/fallback-metadata-query, entre autres méthodes. Les premières tentatives pour remplacer la méthode globale describe-database n'ont pas vraiment fonctionné, car j'aurais dû implémenter toute la logique de la méthode. Je me suis donc concentré sur les appels de méthode qui échouaient et j'ai simplement décidé de les remplacer.

(defmethod sql-jdbc.sync/fallback-metadata-query :iris-jdbc
    [_ _db-name schema table]

    [(format "SELECT * FROM %s.%s WHERE 1=0 LIMIT 0"
        schema
        table)])

(defmethod sql-jdbc.sync/have-select-privilege? :iris-jdbc
    [_ _db schema table]

    [(format "SELECT 1 AS _ FROM %s.%s WHERE 1=0"
        schema
        table)])

En consultant les journaux Docker, nous avons constaté que Metabase interrogeait les métadonnées par défaut à l'aide d'une balise "zero row probe" qui interrogeait le nom et le type des colonnes d'une table. Cette opération était effectuée à l'aide d'une instruction SQL similaire à celle-ci:

SELECT TRUE FROM "schema"."table" WHERE 1 <> 1 LIMIT 0

Cet appel ne fonctionne pas sur IRIS pour deux raisons : l'utilisation du mot-clé TRUE et 1 <> 1. Cela a été fait dans la méthode describe-database avant l'appel de la méthode fallback-metadata-query et l'échec. La seule solution que j'ai trouvée pour résoudre ce problème était de remplacer la méthode fallback-metadata-query. J'ai donc modifié la requête SQL pour qu'elle puisse être exécutée par le pilote JDBC d'IRIS sur IRIS.

Un appel similaire:

SELECT TRUE AS _ FROM "schema"."table" WHERE 1 <> 1

était utilisé par Metabase pour vérifier si un utilisateur avait les privilèges de sélection pour une table donnée, ce qui échouait pour des raisons similaires

Ce qui est intéressant, c'est que le remplacement de la requête de secours sous-jacente semble être le moyen le plus simple de résoudre ce problème, du moins pour IRIS. Cependant, tous les autres pilotes disponibles dans le référentiel Metabase ont leur propre version de describe-database, ce qui me porte à croire qu'il existe un moyen plus clair et plus efficace d'obtenir le même résultat.

Maintenabilité

IRIS ne supporte pas non plus la maintenabilité au-delà de CLOSE_CURSORS_ON_COMMIT, alors que Metabase s'attend à ce qu'une base de données la supporte par défaut. Bien qu'IRIS prenne en charge la maintenabilité au-delà de HOLD_CURSORS_ON_COMMIT, j'ai décidé de m'inspirer d'autres pilotes confrontés au même problème en désactivant complètement la maintenabilité jusqu'à ce que je trouve un moyen de l'implémenter uniquement au-delà de HOLD_CURSORS_ON_COMMIT.

(defmethod sql-jdbc.execute/statement :iris-jdbc
    [driver ^Connection conn]
    (.createStatement conn))

(defmethod sql-jdbc.execute/prepared-statement :iris-jdbc
    [driver ^Connection conn ^String sql]
    (.prepareStatement conn sql))

Prévention des schémas/tables du système

À sa conception, IRIS est préchargé avec un ensemble de schémas et de tables système qui fournissent des fonctionnalités très utiles, mais qui ne sont pas nécessaires pour l'analyse commerciale. La manière la plus simple d'empêcher la synchronisation des schémas avec Metabase consiste à remplacer la méthode sql-jdbc.sync/excluded-schemas, qui consiste en renvoyant un ensemble de chaînes contenant les noms des schémas que vous souhaitez exclure.

(defmethod sql-jdbc.sync/excluded-schemas :iris-jdbc [_]
   #{"Ens"})

C'est utile, mais IRIS a tout simplement trop de schémas système pour que cela fonctionne dans la pratique. J'ai donc choisi à la place de remplacer la méthode sql-jdbc.sync/filtered-syncable-schemas.

(def ^:private sql-jdbc-default
    (get-method sync.interface/filtered-syncable-schemas :sql-jdbc))

(defmethod sync.interface/filtered-syncable-schemas :iris-jdbc
    [driver ^java.sql.Connection conn ^java.sql.DatabaseMetaData meta
    ^String incl-pat ^String excl-pat]

    (let [filtered-schemas
        (into []
            (remove (fn [schema]
                (or
                (str/starts-with? schema "%")
                (str/starts-with? schema "EnsLib_")
                (str/starts-with? schema "Ens_")
                (str/starts-with? schema "EnsPortal")
                (= schema "INFORMATION_SCHEMA")
                (= schema "Ens"))))
            (sql-jdbc-default driver conn meta incl-pat excl-pat))]


    (doseq [schema filtered-schemas]
        (log/infof "[IRIS-DRIVER] Remaining schema → %s" schema))

    filtered-schemas))

L'implémentation par défaut de cette méthode consiste à récupérer tous les schémas disponibles et à supprimer ceux qui sont inclus dans l'ensemble renvoyé par la méthode sql-jdbc.sync/excluded-schemas. Le simple remplacement de la méthode m'obligerait à réécrire le code permettant de récupérer tous les schémas disponibles. Pour éviter cela, j'ai défini une méthode privée sql-jdbc-default qui enregistre l'implémentation par défaut de la méthode avant de la remplacer. De cette façon, je peux appeler l'implémentation par défaut dans ma méthode remplacée, ce qui me permet de filtrer les schémas de manière dynamique.

Projets futurs

Mon but final est d'intégrer officiellement ce pilote dans le référentiel principal de Metabase, ce qui le rendrait accessible à l'ensemble de la communauté sans nécessiter de l'installer manuellement. Pour cela, je vais devoir développer une suite de tests complète qui couvre tous les cas limite et garantit que le pilote fonctionne de manière fiable avec différentes versions et configurations d'IRIS. Cela implique d'écrire des tests unitaires pour les mappages de types, des tests d'intégration pour diverses opérations SQL et probablement quelques benchmarks de performances pour s'assurer que nous n'introduisons aucune régression.

Au-delà d'une simple fusion, il existe certainement certains domaines dans lesquels l'implémentation actuelle pourrait être améliorée. Le système de mappage des types, bien que fonctionnel, pourrait être plus nuancé, en particulier en ce qui concerne la manière dont nous traitons les types de données plus spécialisés d'IRIS. Pour ce faire, je devrais examiner l'implémentation JDBC d'IRIS afin de déterminer les mappages exacts entre les types IRIS et Java.

Enfin, je suis convaincu que les implémentations des fonctions arithmétiques DateTime et de date peuvent être améliorées, en commençant par quelques tests visant à déterminer si l'implémentation actuelle fonctionne correctement.

Conclusion

Je vais certainement travailler surtoutes ces améliorations dès que j'aurai un peu de temps libre. La communauté semble très intéressée par une meilleure prise en charge d'IRIS dans Metabase, cela semble donc être un investissement utile. N'hésitez pas à me faire part de vos idées ou à soumettre des pull requests :)

Un grand remerciement à @Martyn Lee et @Bryan Hoon du bureau de Singapour pour m'avoir donné l'opportunité de travailler sur ce projet et m'avoir aidé à résoudre certains problèmes qui se sont présentés au cours du processus.

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer