Accéder au contenu principal

Plugin Lakebase

Plugin Lakebase

Fournit un pool de connexions PostgreSQL pour Databricks Lakebase Autoscaling, avec actualisation automatique des tokens OAuth.

Fonctionnalités clés :

  • pg.Pool standard, compatible avec n'importe quelle bibliothèque PostgreSQL ou ORM
  • Actualisation automatique des tokens OAuth (tokens d'une heure, marge d'actualisation de 2 minutes)
  • Mise en cache des tokens pour limiter les appels d'API
  • Instrumentation OpenTelemetry intégrée (durée des requêtes, connexions du pool, actualisation des tokens)
  • Logger AppKit configuré par défaut pour les événements de requête et de connexion

Premiers pas avec Lakebase

Le moyen le plus simple de démarrer avec le plugin Lakebase est d'utiliser la Databricks CLI pour créer une nouvelle application Databricks avec AppKit et le plugin Lakebase déjà installés.

Prérequis

Étapes

  1. Commencez par créer un projet Lakebase Postgres Autoscaling en suivant la documentation de démarrage.
  2. Pour ajouter le plugin Lakebase à votre projet, exécutez la commande databricks apps init, puis sélectionnez le plugin Lakebase dans le menu interactif. La CLI vous guide ensuite dans le choix d'un projet, d'une branche et d'une base de données Lakebase.
    • Lorsque la question vous est posée, répondez Yes pour déployer l'application sur Databricks Apps dès sa création.

Utilisation de base

import { createApp, lakebase, server } from "@databricks/appkit";

await createApp({
  plugins: [server(), lakebase()],
});

Accéder au pool

Une fois l'initialisation effectuée, accédez à Lakebase via l'objet AppKit.lakebase :

const AppKit = await createApp({
  plugins: [server(), lakebase()],
});

await AppKit.lakebase.query(`CREATE SCHEMA IF NOT EXISTS app`);

await AppKit.lakebase.query(`CREATE TABLE IF NOT EXISTS app.orders (
  id SERIAL PRIMARY KEY,
  user_id VARCHAR(255) NOT NULL,
  amount DECIMAL(10, 2) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`);

const result = await AppKit.lakebase.query(
  "SELECT * FROM app.orders WHERE user_id = $1",
  [userId],
);

// pg.Pool brut (pour les ORM ou un usage avancé)
const pool = AppKit.lakebase.pool;

// Objets de configuration prêts à l'emploi pour les ORM
const ormConfig = AppKit.lakebase.getOrmConfig();  // { host, port, database, ... }
const pgConfig = AppKit.lakebase.getPgConfig();    // pg.PoolConfig

Configuration

Variables d'environnement

Les variables d'environnement requises sont les suivantes :

VariableDescription
LAKEBASE_ENDPOINTChemin de la ressource endpoint (ex. projects/.../branches/.../endpoints/...)
PGHOSTHôte Lakebase (injecté automatiquement en production par la ressource Databricks Apps postgres)
PGDATABASENom de la base de données (injecté automatiquement en production par la ressource Databricks Apps postgres)
PGSSLMODEMode TLS - défini sur require (injecté automatiquement en production par la ressource Databricks Apps postgres)

Lors d'un déploiement sur Databricks Apps avec une ressource de base de données postgres configurée, les variables PGHOST, PGDATABASE, PGSSLMODE, PGUSER, PGPORT et PGAPPNAME sont injectées automatiquement par la plateforme. Seule LAKEBASE_ENDPOINT doit être définie explicitement :

env:
  - name: LAKEBASE_ENDPOINT
    valueFrom: postgres

Pour le développement local, le fichier .env est généré automatiquement par databricks apps init avec les valeurs correspondant à votre projet Lakebase.

Pour la référence complète de la configuration (SSL, taille du pool, délais d'expiration, journalisation, exemples d'ORM), consultez le README de @databricks/lakebase.

Configuration du pool

Passez un objet pool pour remplacer les valeurs par défaut :

await createApp({
  plugins: [
    lakebase({
      pool: {
        max: 10,                      // Nombre max. de connexions du pool (par défaut : 10)
        connectionTimeoutMillis: 5000, // Délai d'attente de connexion en ms (par défaut : 10000)
        idleTimeoutMillis: 30000,      // Délai d'inactivité des connexions en ms (par défaut : 30000)
      },
    }),
  ],
});

On-Behalf-Of (OBO) — connexions par utilisateur

Lorsque votre application nécessite la sécurité au niveau des lignes (RLS) ou une isolation des données par utilisateur, utilisez asUser(req) pour exécuter les requêtes via un pool de connexions Lakebase propre à chaque utilisateur. Le pool de chaque utilisateur est authentifié avec son identité Databricks : le current_user de PostgreSQL correspond donc à l'utilisateur réel.

Prérequis

  1. Activez l'autorisation utilisateur dans votre Databricks App avec le scope postgres. Consultez User authorization pour les instructions de configuration. Dans votre databricks.yml :

    resources:
      apps:
        app:
          user_api_scopes:
            - postgres

    Les applications dont l'ossature est générée avec databricks apps init et le plugin Lakebase incluent cette configuration automatiquement.

  2. Chaque utilisateur de l'application doit disposer d'un rôle Postgres dans Lakebase. Créez-en un avec la Databricks CLI :

    databricks postgres create-role "projects/{project_id}/branches/{branch_id}" \
      --json '{"spec": {"identity_type": "USER", "postgres_role": "user@example.com"}}'

    Vous pouvez également créer des rôles dans l'interface Lakebase, sous Branch OverviewAdd role.

    note

    N'accordez pas databricks_superuser aux utilisateurs OBO — les superutilisateurs contournent la RLS. Utilisez plutôt des grants à granularité fine.

Utilisation

Aucune configuration nécessaire — il suffit d'appeler asUser(req) :

const AppKit = await createApp({
  plugins: [server(), lakebase()],
});

// Requête via le service principal (par défaut — contourne la RLS en tant que propriétaire de la table)
const all = await AppKit.lakebase.query("SELECT * FROM app.orders");

// Requête dans le contexte de l'utilisateur (pool par utilisateur, RLS appliquée)
app.get("/api/my-orders", async (req, res) => {
  const result = await AppKit.lakebase
    .asUser(req)
    .query("SELECT * FROM app.orders ORDER BY created_at DESC");
  res.json(result.rows);
});

Lorsque asUser(req) est appelé :

  1. Le token et l'identité de l'utilisateur sont extraits des en-têtes x-forwarded-access-token et x-forwarded-email (définis automatiquement par Databricks Apps).
  2. Un pg.Pool propre à l'utilisateur est créé (ou réutilisé) à partir de ses identifiants OAuth.
  3. query() et pool utilisent le pool de l'utilisateur — current_user dans PostgreSQL reflète son identité.

Exemple de sécurité au niveau des lignes

-- En tant que service principal (lors de la configuration de l'application) :
ALTER TABLE app.orders ENABLE ROW LEVEL SECURITY;

CREATE POLICY user_orders ON app.orders
  FOR ALL TO PUBLIC
  USING (owner = current_user);

-- Accorder les droits pour que les utilisateurs OBO puissent interroger les données
GRANT USAGE ON SCHEMA app TO PUBLIC;
GRANT SELECT, INSERT ON ALL TABLES IN SCHEMA app TO PUBLIC;

Fonctionnement

  • Le pool du service principal (AppKit.lakebase.pool) est toujours créé et utilisé pour les opérations DDL, l'amorçage des données et les requêtes d'administration.
  • Les pools par utilisateur sont créés au premier appel à asUser(req) et mis en cache par identité d'utilisateur. Chaque pool dispose de son propre cycle d'actualisation du token OAuth.
  • Les connexions inactives au sein des pools par utilisateur se ferment automatiquement (délai d'inactivité de 30 s). Les objets de pool vides sont nettoyés périodiquement.
  • À l'arrêt, tous les pools (service principal + utilisateur) sont fermés proprement.
  • En mode développement (NODE_ENV=development), si aucun token utilisateur n'est disponible, asUser(req) bascule sur le pool du service principal en émettant un avertissement.
RLS et superutilisateurs

Les superutilisateurs PostgreSQL contournent entièrement la sécurité au niveau des lignes (RLS). Les utilisateurs disposant du rôle databricks_superuser verront toutes les lignes, quelles que soient les politiques RLS. Pour garantir l'application de la RLS, utilisez des grants à granularité fine plutôt que le rôle de superutilisateur.

Permissions de base de données

Lorsque vous créez l'application avec la ressource Lakebase en suivant le guide Démarrage, le service principal se voit automatiquement accorder la permission CONNECT_AND_CREATE sur la ressource postgres. Il peut ainsi se connecter à la base de données et créer de nouveaux objets, mais pas accéder aux schémas ou aux tables existants.

Développement local

Pour développer localement avec une base de données Lakebase déployée :

  1. Déployez d'abord l'application. Le service principal crée le schéma et les tables de la base de données lors du premier déploiement. Les applications générées avec databricks apps init s'en chargent automatiquement : elles vérifient au démarrage si les tables existent et n'en créent pas si c'est le cas.

  2. Accordez le rôle databricks_superuser (ignorez cette étape si vous êtes propriétaire du projet Lakebase — vous disposez déjà d'un accès complet) :

    # Créer un nouveau rôle avec databricks_superuser
    databricks postgres create-role "projects/{project_id}/branches/{branch_id}" \
      --json '{"spec": {"identity_type": "USER", "postgres_role": "user@example.com", "membership_roles": ["DATABRICKS_SUPERUSER"]}}'

    Pour accorder le rôle superutilisateur à un rôle existant, utilisez update-role :

    databricks postgres update-role \
      "projects/{project_id}/branches/{branch_id}/roles/{role_id}" \
      "spec.membership_roles" \
      --json '{"spec": {"membership_roles": ["DATABRICKS_SUPERUSER"]}}'

    Vous pouvez également gérer les rôles dans l'interface Lakebase Autoscaling, sur la page Branch Overview de votre projet → Add role / Edit role.

  3. Exécutez l'application localement : votre identité d'utilisateur Databricks (adresse e-mail) sert à l'authentification OAuth. Le rôle databricks_superuser donne un accès DML complet (lecture/écriture des données) mais pas DDL (création de schémas ou de tables) — d'où l'importance de déployer au préalable (voir la note ci-dessous).

Pour les autres utilisateurs, répétez l'étape 2 afin de créer un rôle OAuth avec databricks_superuser pour chacun d'eux.

tip

L'authentification par mot de passe Postgres est une alternative plus simple, qui évite la complexité des permissions de rôle OAuth. Elle exige toutefois de définir un mot de passe pour l'utilisateur sur la page Branch Overview de l'interface Lakebase Autoscaling.

Pourquoi déployer d'abord ?

Lors du déploiement de l'application, le service principal crée les schémas et les tables et en devient le propriétaire. databricks_superuser donne un accès DML complet (lecture/écriture) mais pas DDL : le développement local ne fonctionne donc qu'une fois le schéma créé.

Si vous lancez d'abord npm run dev, ce sont vos identifiants qui deviennent propriétaires du schéma et l'application déployée se heurte à une erreur permission denied. Pour rétablir la situation, exportez d'abord vos données (pg_dump ou copie temporaire du schéma), puis supprimez le schéma et redéployez. Après le redéploiement, le service principal recrée le schéma au démarrage. (La propriété d'un schéma PostgreSQL est liée au rôle qui l'a créé et ne peut pas être réattribuée par des utilisateurs ordinaires.)

Permissions à granularité fine

Dans la plupart des cas, databricks_superuser suffit. Si vous avez plutôt besoin de grants au niveau du schéma, consultez la documentation officielle :

Script SQL pour des grants à granularité fine

Déployez et exécutez l'application au moins une fois avant d'appliquer ces grants, afin que le service principal initialise d'abord le schéma de la base de données.

Remplacez subject par l'adresse e-mail de l'utilisateur et schema par le nom de votre schéma :

CREATE EXTENSION IF NOT EXISTS databricks_auth;

DO $$
DECLARE
  subject TEXT := 'your-subject';  -- Adresse e-mail de l'utilisateur, par exemple name@databricks.com
  schema TEXT := 'your_schema'; -- Remplacez 'your_schema' par le nom de votre schéma
BEGIN
  -- Créer le rôle OAuth pour l'identité Databricks
  PERFORM databricks_create_role(subject, 'USER');

  -- Accès à la connexion et au schéma
  EXECUTE format('GRANT CONNECT ON DATABASE "databricks_postgres" TO %I', subject);
  EXECUTE format('GRANT ALL ON SCHEMA %s TO %I', schema, subject);

  -- Privilèges sur les objets existants
  EXECUTE format('GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA %s TO %I', schema, subject);
  EXECUTE format('GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA %s TO %I', schema, subject);
  EXECUTE format('GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA %s TO %I', schema, subject);
  EXECUTE format('GRANT ALL PRIVILEGES ON ALL PROCEDURES IN SCHEMA %s TO %I', schema, subject);

  -- Privilèges par défaut sur les objets futurs
  EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT ALL ON TABLES TO %I', schema, subject);
  EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT ALL ON SEQUENCES TO %I', schema, subject);
  EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT ALL ON FUNCTIONS TO %I', schema, subject);
  EXECUTE format('ALTER DEFAULT PRIVILEGES IN SCHEMA %s GRANT ALL ON ROUTINES TO %I', schema, subject);
END $$;

Databricks Developer Hub

Prêt à lancer votre prochaine application agentique en quelques minutes ?

Lire la documentation