mansart-pool est un pool de connexions JDBC implémenté en pur Java 25, sans dépendance externe, avec ` Semaphore + ConcurrentLinkedDeque` lock-free et un housekeeper sur virtual thread. Il est totalement découplé du reste de Mansart : son seul contrat est javax.sql.DataSource.

Mission

  • Fournir un DataSource performant, simple, configurable.

  • Être natif virtual threads — aucune contention synchronized sur le chemin chaud.

  • Rester optionnel — l’utilisateur peut brancher mansart-jakarta-data sur n’importe quel DataSource (Hikari, Tomcat-JDBC, c3p0).

Positionnement dans le workspace

mansart-pool est un peer indépendant des autres modules Mansart. Il ne dépend ni de mansart-data-api, ni de mansart-transactions. À l’inverse, mansart-jakarta-data et mansart-transactions peuvent l’utiliser, mais n’en dépendent pas en compile.

Diagram

Modules

Sous-module Rôle

mansart-pool-api

PoolConfig, PoolConfig.Builder, PoolMetrics, PoolException, ValidationMode. Aucune dépendance.

mansart-pool-core

MansartDataSource (impl DataSource), PooledConnection, housekeeper. Génère PooledConnectionProxy au build via Class-File API.

API publique

PoolConfig config = PoolConfig.builder()
    .jdbcUrl("jdbc:postgresql://db.local:5432/shop")
    .username("shop").password("...")
    .minSize(4).maxSize(32)
    .acquireTimeout(Duration.ofSeconds(5))
    .idleTimeout(Duration.ofMinutes(10))
    .maxLifetime(Duration.ofMinutes(30))
    .validationMode(ValidationMode.IS_VALID)
    .leakDetectionThreshold(Duration.ofSeconds(30))
    .build();

DataSource ds = MansartDataSource.create(config);
PoolMetrics metrics = ((MansartDataSource) ds).metrics();

Voir Référence pour le tableau complet des propriétés.

Architecture runtime

MansartDataSource (impl DataSource)
    │
    ├── Deque<PooledConnection> idle    (ConcurrentLinkedDeque, lock-free)
    ├── Set<PooledConnection>   inUse   (ConcurrentHashMap.newKeySet)
    ├── Semaphore               permits (taille = maxSize, fair=false)
    ├── ScopedValue<Connection> CURRENT (propagation transaction)
    └── ScheduledTask           housekeeper (virtual thread)

Acquisition (getConnection()) :

  1. permits.acquire(acquireTimeout) — borne dure.

  2. idle.poll() — LIFO chaud.

  3. Si valid (isValid ou validationQuery) → inUse.add ; sinon → realClose + créer nouvelle.

  4. Retour : PooledConnectionProxy qui intercepte close().

Restitution (Connection.close() sur le proxy) :

  1. pc.reset() — rollback si dirty, autoCommit=true, isolation par défaut.

  2. inUse.remove(pc).

  3. Si aliveAndYoung()idle.offerFirst(pc) ; sinon → realClose.

  4. permits.release().

Le proxy PooledConnectionProxy est généré au build via Class-File API JEP 484 — pas via java.lang.reflect.Proxy. Une seule classe qui implémente toutes les méthodes Connection, intercepte close(). Voir Fonctionnement interne.

Intégration ScopedValue pour transactions

La connexion enlistée par mansart-transactions est portée par ScopedValue<Connection> CURRENT au lieu d’un ThreadLocal. Avantages :

  • Propagation automatique dans StructuredTaskScope.fork(…​).

  • Pas de fuite mémoire ThreadLocal sur les virtual threads à durée de vie courte.

  • Sémantique claire : la valeur n’est visible que dans la portée lexicale du ScopedValue.where(…​).

Métriques

PoolMetrics (snapshot lock-free) :

  • idleSize(), inUseSize(), totalSize().

  • acquireCount(), acquireWaitNanos() (cumul + percentiles).

  • creationCount(), evictionCount().

  • leakCount().

Bridge Micrometer / Prometheus dans le backlog (MP3).

Détection de fuites

leakDetectionThreshold(Duration) enregistre, à l’acquire, une stack-trace dans le PooledConnection. Si la connexion reste empruntée plus longtemps que le seuil, le housekeeper log un WARNING avec la stack — diagnostic immédiat du chemin de code fautif. À activer en staging, désactiver en prod si l’overhead est mesuré.

Comparatif vs HikariCP

  • Threading — Mansart natif virtual threads, pas de pinning sous charge Loom. HikariCP utilise des synchronized qui pinent.

  • Dépendances — Mansart zéro-dep. HikariCP dépend de SLF4J + javassist (en transitif via micrometer).

  • AOT — Mansart compatible GraalVM native-image et Leyden CDS sans config. HikariCP nécessite --initialize-at-build-time pour javassist.

  • API — très proches (HikariConfigPoolConfig). Voir Migration.

Chiffres : voir lien:https://codeberg.org/Vidocq/mansart/src/branch/main/mansart-pool/BENCH.md[mansart-pool BENCH.md] (baseline 2026-05-06 vs HikariCP).

Roadmap

  • ✅ MP1 — API + skeleton.

  • ⏳ MP2 — Implémentation core (MansartDataSource, semaphore, deque, validation).

  • ⏳ MP3 — Métriques + leak detection.

  • ⏳ MP4 — Intégration mansart-jakarta-data (transparent côté utilisateur).

Voir lien:https://codeberg.org/Vidocq/mansart/src/branch/main/mansart-pool/PLAN.md[mansart-pool PLAN.md].