Socle V004 – H2 et TechDB

Socle V004 - H2 et TechDB

21 – H2 TechDB (Nouveaute V4)

Version : 4.0.2 Date : 2026-01-15

1. Introduction

La H2 TechDB est une base de donnees embarquee introduite dans le Socle V4 pour stocker l’etat technique de maniere persistante.

Pourquoi H2 ?

Critere H2 Nitrite (ancien)
Embarque
ARM/AMD64 ⚠️ Problemes
UI debug ✅ H2 Console
JSON SQL ✅ JSON_VALUE
Stabilite ⚠️ v4 instable

Nouveautes V4.0.1

  • Standard de tables x_ : Nouvelle structure avec champs techniques standardises
  • H2 Console sur port 9376 : Interface web dediee pour explorer la base
  • TechDbReaderWorker : Worker passif de lecture des donnees
  • TechDbPurgeWorker : Purge automatique des donnees obsoletes

Nouveautes V4.0.2

  • API SQL REST : Nouveau endpoint /techdb/query pour requetes SQL via HTTP
  • Authentification Basic Auth : Securisation de l’acces API
  • Rate Limiting : Protection contre les abus (60 req/min/IP par defaut)
  • Mode Readonly : Protection contre les modifications accidentelles

2. Cas d’usage

La TechDB stocke :

  • Offsets/sequences : Position dans Kafka, NATS, DB2
  • Etat local des workers : Statut, derniere execution
  • Evenements techniques : Logs importants
  • Fallback logs : Logs non envoyes (LogForwarder)
  • Cle-valeur : Donnees generiques avec TTL

3. Configuration

3.1 application.yml

socle:
  techdb:
    enabled: ${TECHDB_ENABLED:true}
    url: jdbc:h2:file:${TECHDB_PATH:./data/socle-techdb};MODE=PostgreSQL;DB_CLOSE_DELAY=-1;AUTO_SERVER=TRUE
    username: ${TECHDB_USERNAME:LMVI}
    password: ${TECHDB_PASSWORD:LMVI-SOCLEV004}

    # H2 Console Web (port 9376)
    console:
      enabled: ${TECHDB_CONSOLE_ENABLED:true}
      port: ${TECHDB_CONSOLE_PORT:9376}
      bind_address: ${TECHDB_CONSOLE_BIND:}
      allow_remote: ${TECHDB_CONSOLE_ALLOW_REMOTE:false}

    # Purge automatique des donnees anciennes
    purge:
      enabled: ${TECHDB_PURGE_ENABLED:true}
      schedule: ${TECHDB_PURGE_SCHEDULE:0 0 3 * * ?}  # 3h du matin
      events_retention_days: ${TECHDB_PURGE_EVENTS_DAYS:7}
      logs_retention_days: ${TECHDB_PURGE_LOGS_DAYS:3}

3.2 Variables d’environnement

Variable Description Defaut
TECHDB_ENABLED Activer TechDB true
TECHDB_PATH Chemin fichier H2 ./data/socle-techdb
TECHDB_USERNAME Nom d’utilisateur LMVI
TECHDB_PASSWORD Mot de passe LMVI-SOCLEV004
TECHDB_CONSOLE_ENABLED Activer console web true
TECHDB_CONSOLE_PORT Port console 9376
TECHDB_CONSOLE_ALLOW_REMOTE Acces distant false
TECHDB_PURGE_ENABLED Activer purge auto true
TECHDB_PURGE_EVENTS_DAYS Retention events 7
TECHDB_PURGE_LOGS_DAYS Retention logs 3

3.3 Personnalisation des identifiants par environnement

Les identifiants par defaut (LMVI / LMVI-SOCLEV004) conviennent pour le developpement et les tests. Pour la production, il est recommande de personnaliser via variables d’environnement.

Docker Compose :

services:
  mon-app:
    environment:
      - TECHDB_USERNAME=MonUserProd
      - TECHDB_PASSWORD=MonMotDePasseSecurise123!

Java direct :

export TECHDB_USERNAME=MonUserProd
export TECHDB_PASSWORD=MonMotDePasseSecurise123!
java -jar mon-application.jar

Kubernetes :

env:
  - name: TECHDB_USERNAME
    valueFrom:
      secretKeyRef:
        name: techdb-credentials
        key: username
  - name: TECHDB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: techdb-credentials
        key: password

Note : Si vous changez les identifiants sur une base existante, vous devez supprimer le fichier socle-techdb.mv.db et laisser l’application recreer la base.

4. Schéma de base

Les tables sont créées automatiquement au démarrage :

-- Offsets / séquences
CREATE TABLE IF NOT EXISTS socle_offsets (
    id IDENTITY PRIMARY KEY,
    source_name VARCHAR(200) NOT NULL,
    partition_key VARCHAR(200) NOT NULL,
    last_sequence BIGINT NOT NULL,
    last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    extra JSON,
    UNIQUE(source_name, partition_key)
);

-- État local des workers
CREATE TABLE IF NOT EXISTS socle_worker_state (
    id IDENTITY PRIMARY KEY,
    worker_id VARCHAR(200) NOT NULL UNIQUE,
    status VARCHAR(20) NOT NULL,
    last_heartbeat TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    metadata JSON
);

-- Événements techniques
CREATE TABLE IF NOT EXISTS socle_events (
    id IDENTITY PRIMARY KEY,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    type VARCHAR(100) NOT NULL,
    payload JSON
);

-- Fallback logs (LogForwarder)
CREATE TABLE IF NOT EXISTS socle_log_fallback (
    id IDENTITY PRIMARY KEY,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    log_entry JSON NOT NULL,
    retry_count INT DEFAULT 0
);

5. Interface TechDbManager

package eu.lmvi.socle.techdb;

@Component
public class TechDbManager {

    // ===== Lifecycle =====

    /**
     * Initialise la base H2 et crée les tables
     */
    public void initialize();

    /**
     * Ferme proprement la connexion
     */
    public void close();

    /**
     * Vérifie la santé de la base
     */
    public boolean isHealthy();

    // ===== Offsets =====

    /**
     * Sauvegarde un offset
     */
    public void saveOffset(String sourceName, String partitionKey,
                           long sequence, Map<String, Object> extra);

    /**
     * Récupère un offset
     */
    public OptionalLong getOffset(String sourceName, String partitionKey);

    /**
     * Liste tous les offsets d'une source
     */
    public List<OffsetRecord> getOffsets(String sourceName);

    // ===== Worker State =====

    /**
     * Sauvegarde l'état d'un worker
     */
    public void saveWorkerState(String workerId, String status,
                                Map<String, Object> metadata);

    /**
     * Récupère l'état d'un worker
     */
    public Optional<WorkerState> getWorkerState(String workerId);

    // ===== Events =====

    /**
     * Enregistre un événement technique
     */
    public void logEvent(String type, Map<String, Object> payload);

    /**
     * Récupère les événements
     */
    public List<TechEvent> getEvents(String type, Instant since, int limit);

    // ===== Generic KV =====

    public void put(String key, String value);
    public Optional<String> get(String key);
    public void delete(String key);
}

6. Utilisation

6.1 Injection

@Service
public class MonWorker implements Worker {

    @Autowired
    private TechDbManager techDb;

    @Override
    public void doWork() {
        // Récupérer le dernier offset
        OptionalLong lastOffset = techDb.getOffset("kafka", "my-topic-0");
        long startFrom = lastOffset.orElse(0L);

        // Traiter les messages...
        long newOffset = processMessages(startFrom);

        // Sauvegarder le nouvel offset
        techDb.saveOffset("kafka", "my-topic-0", newOffset,
            Map.of("processed", true));
    }
}

6.2 Gestion des offsets

// Sauvegarder
techDb.saveOffset("nats", "events.orders", 123456789L,
    Map.of("consumer", "order-processor"));

// Récupérer
OptionalLong offset = techDb.getOffset("nats", "events.orders");
if (offset.isPresent()) {
    log.info("Dernier offset: {}", offset.getAsLong());
}

// Lister tous les offsets NATS
List<OffsetRecord> offsets = techDb.getOffsets("nats");
offsets.forEach(o -> log.info("{}: {}", o.partitionKey(), o.lastSequence()));

6.3 État des workers

// Sauvegarder l'état
techDb.saveWorkerState("kafka-consumer-001", "RUNNING",
    Map.of(
        "lastProcessed", Instant.now(),
        "messagesPerMinute", 523
    ));

// Récupérer l'état
Optional<WorkerState> state = techDb.getWorkerState("kafka-consumer-001");
state.ifPresent(s -> {
    log.info("Worker {} - Status: {}", s.workerId(), s.status());
});

6.4 Événements techniques

// Logger un événement
techDb.logEvent("PIPELINE_ERROR", Map.of(
    "pipeline", "order-processing",
    "error", "Connection timeout",
    "messageId", "msg-123"
));

// Récupérer les événements récents
List<TechEvent> errors = techDb.getEvents(
    "PIPELINE_ERROR",
    Instant.now().minus(1, ChronoUnit.HOURS),
    100
);

7. H2 Console Web (Port 9376)

Le Socle V4 expose une console H2 dediee sur le port 9376 via le TechDbConsoleWorker.

Acces

http://localhost:9376

Informations de connexion

Champ Valeur
JDBC URL jdbc:h2:./data/socle-techdb (local) ou jdbc:h2:/app/data/socle-techdb (Docker)
User LMVI (defaut, personnalisable via TECHDB_USERNAME)
Password LMVI-SOCLEV004 (defaut, personnalisable via TECHDB_PASSWORD)
Driver org.h2.Driver

Configuration

socle:
  techdb:
    console:
      enabled: true       # Activer/desactiver
      port: 9376          # Port dedie
      bind_address: ""    # Vide = toutes interfaces
      allow_remote: false # Securite: localhost only

Requetes utiles

-- Voir tous les offsets
SELECT * FROM techdb_offsets ORDER BY x_dateCreated DESC;

-- Offsets Kafka uniquement
SELECT * FROM techdb_offsets WHERE topic LIKE 'kafka%';

-- Etat des workers
SELECT worker_name, state, last_run_at, error_count FROM techdb_worker_state;

-- Evenements non traites
SELECT * FROM techdb_events WHERE processed = FALSE ORDER BY x_dateCreated;

-- Logs non envoyes
SELECT COUNT(*) as pending FROM techdb_log_buffer WHERE forwarded = FALSE;

-- Cles KV avec expiration
SELECT kv_key, value_type, expires_at FROM techdb_kv WHERE expires_at IS NOT NULL;

8. API SQL REST (Nouveaute V4.0.2)

Le Socle V4 expose une API REST permettant d’executer des requetes SQL sur la TechDB. Cette API est distincte de la console H2 et offre un acces programmatique securise.

8.1 Configuration

socle:
  techdb:
    sql_api:
      enabled: ${TECHDB_SQL_API_ENABLED:false}
      auth:
        user: ${TECHDB_API_USER:admin}
        password: ${TECHDB_API_PASSWORD:}
      security:
        readonly: ${TECHDB_SQL_API_READONLY:true}
        blocked_tables: ${TECHDB_SQL_API_BLOCKED_TABLES:}
      limits:
        max_rows: ${TECHDB_SQL_API_MAX_ROWS:1000}
        timeout_seconds: ${TECHDB_SQL_API_TIMEOUT:30}
        rate_limit_per_minute: ${TECHDB_SQL_API_RATE_LIMIT:60}
      logging:
        log_queries: ${TECHDB_SQL_API_LOG_QUERIES:true}

8.2 Variables d’environnement

Variable Description Defaut
TECHDB_SQL_API_ENABLED Activer l’API SQL false
TECHDB_API_USER Utilisateur Basic Auth admin
TECHDB_API_PASSWORD Mot de passe (vide = pas d’auth) « 
TECHDB_SQL_API_READONLY Mode lecture seule true
TECHDB_SQL_API_BLOCKED_TABLES Tables interdites (CSV) « 
TECHDB_SQL_API_MAX_ROWS Limite de lignes 1000
TECHDB_SQL_API_TIMEOUT Timeout requetes (sec) 30
TECHDB_SQL_API_RATE_LIMIT Requetes/minute/IP 60

8.3 Endpoints

Methode Path Description
POST /techdb/query Executer une requete SQL
GET /techdb/tables Liste des tables
GET /techdb/tables/{name} Details d’une table
GET /techdb/stats Statistiques DB

8.4 Authentification

L’API utilise Basic Auth. Si un mot de passe est configure, toutes les requetes doivent inclure l’en-tete:

Authorization: Basic base64(user:password)

Exemple avec curl:

# Sans authentification (si TECHDB_API_PASSWORD vide)
curl http://localhost:8080/techdb/tables

# Avec authentification
curl -u admin:monmotdepasse http://localhost:8080/techdb/query \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT * FROM techdb_kv LIMIT 10"}'

8.5 Executer une requete SQL

Requete:

curl -X POST http://localhost:8080/techdb/query \
  -H "Content-Type: application/json" \
  -u admin:secret \
  -d '{
    "sql": "SELECT * FROM techdb_events WHERE event_type = ?",
    "params": ["WORKER_START"],
    "maxRows": 100
  }'

Reponse succes:

{
  "success": true,
  "timestamp": 1705312800000,
  "executionTimeMs": 12,
  "rowCount": 15,
  "columns": ["X_ID", "X_DATECREATED", "EVENT_TYPE", "SOURCE", "DATAS"],
  "rows": [
    {"X_ID": 1, "X_DATECREATED": "2025-01-15T10:00:00", "EVENT_TYPE": "WORKER_START", ...},
    ...
  ],
  "truncated": false
}

Reponse erreur:

{
  "success": false,
  "timestamp": 1705312800000,
  "error": {
    "code": "SQL_SYNTAX_ERROR",
    "message": "Syntax error in SQL statement"
  }
}

8.6 Codes d’erreur

Code HTTP Status Description
SQL_SYNTAX_ERROR 400 Erreur de syntaxe SQL
DDL_NOT_ALLOWED 403 Operation DDL interdite
READONLY_VIOLATION 403 Non-SELECT en mode readonly
TABLE_BLOCKED 403 Table bloquee par config
QUERY_TIMEOUT 408 Timeout depasse
UNAUTHORIZED 401 Authentification requise
RATE_LIMITED 429 Limite requetes depassee
TECHDB_DISABLED 503 TechDB non disponible

8.7 Securite

Operations toujours interdites:

  • DDL: DROP, ALTER, CREATE, TRUNCATE, GRANT, REVOKE
  • Commandes dangereuses: SHUTDOWN, SCRIPT, BACKUP, RESTORE

Mode readonly (defaut):

  • Seuls les SELECT sont autorises
  • INSERT, UPDATE, DELETE interdits

Tables bloquees:

socle:
  techdb:
    sql_api:
      security:
        blocked_tables: techdb_log_buffer,techdb_kv  # CSV

8.8 Rate Limiting

L’API applique un rate limiting par IP (sliding window par minute).

Par defaut: 60 requetes/minute/IP

Depassement = HTTP 429 Too Many Requests

8.9 Metriques Prometheus

techdb_sql_api_queries_total{status="success|error|timeout"}
techdb_sql_api_queries_duration_seconds
techdb_sql_api_auth_failures_total
techdb_sql_api_rate_limited_total

8.10 Exemples pratiques

Lister les tables:

curl http://localhost:8080/techdb/tables

Details d’une table:

curl http://localhost:8080/techdb/tables/TECHDB_EVENTS

Statistiques DB:

curl http://localhost:8080/techdb/stats

Requete avec parametres:

curl -X POST http://localhost:8080/techdb/query \
  -H "Content-Type: application/json" \
  -d '{
    "sql": "SELECT worker_name, state, error_count FROM techdb_worker_state WHERE state = ?",
    "params": ["RUNNING"]
  }'

9. Workers TechDB

Le Socle V4 inclut 3 workers dedies a la gestion de TechDB:

9.1 TechDbReaderWorker

Worker PASSIVE exposant des methodes de lecture.

@Autowired
private TechDbReaderWorker reader;

// Recuperer tous les offsets
List<Map<String, Object>> offsets = reader.getAllOffsets();

// Recuperer les evenements recents
List<Map<String, Object>> events = reader.getRecentEvents(100);

// Executer une requete personnalisee
List<Map<String, Object>> results = reader.executeCustomQuery(
    "SELECT * FROM techdb_worker_state WHERE state = 'RUNNING'"
);

9.2 TechDbPurgeWorker

Worker CRON qui purge automatiquement les donnees obsoletes.

socle:
  techdb:
    purge:
      enabled: true
      schedule: "0 0 3 * * ?"  # 3h du matin
      events_retention_days: 7
      logs_retention_days: 3

Donnees purgees :

  • Evenements traites > 7 jours
  • Logs forwardes > 3 jours
  • Cles KV expirees

9.3 TechDbConsoleWorker

Worker PASSIVE qui demarre la console H2 sur le port 9376.

@Autowired
private TechDbConsoleWorker console;

// Verifier si la console est accessible
boolean running = console.isConsoleRunning();

// Obtenir les infos de connexion
Map<String, String> info = console.getConnectionInfo();

10. Fonctions JSON H2

H2 2.x supporte les fonctions JSON SQL standard :

-- Extraction de valeur
SELECT JSON_VALUE('{"name":"John","age":30}', '$.name');
-- Résultat: John

-- Extraction d'objet
SELECT JSON_QUERY('{"data":{"items":[1,2,3]}}', '$.data');
-- Résultat: {"items":[1,2,3]}

-- Test d'existence
SELECT JSON_EXISTS('{"name":"John"}', '$.name');
-- Résultat: TRUE

-- Construction JSON
SELECT JSON_OBJECT('name': 'John', 'age': 30);
-- Résultat: {"name":"John","age":30}

-- Filtrage sur JSON
SELECT * FROM socle_events
WHERE JSON_VALUE(payload, '$.severity') = 'ERROR';

11. Integration avec SharedDataRegistry

TechDB complète SharedDataRegistry :

Aspect SharedDataRegistry TechDbManager
Scope Runtime (mémoire) Persistant (fichier)
Survie restart Non Oui
Performance Ultra rapide Rapide
Usage Métriques live Offsets, état

Exemple de synergie

@Service
public class MonService {

    @Autowired
    private TechDbManager techDb;

    @Autowired
    private SharedDataRegistry sharedData;

    public void initialize() {
        // Restaurer l'offset depuis TechDB
        OptionalLong persisted = techDb.getOffset("kafka", "topic-0");

        // Créer le compteur en mémoire
        sharedData.createSequence("kafka.offset.topic-0",
            persisted.orElse(0L),
            HealthLevel.CRITICAL);
    }

    public void onMessage(long offset) {
        // Mettre à jour en mémoire (rapide)
        sharedData.setSequence("kafka.offset.topic-0", offset);

        // Persister périodiquement (moins fréquent)
        if (offset % 1000 == 0) {
            techDb.saveOffset("kafka", "topic-0", offset, null);
        }
    }
}

12. Bonnes pratiques

DO

  • ✅ Utiliser TechDB pour les données qui doivent survivre au restart
  • ✅ Persister les offsets périodiquement (pas à chaque message)
  • ✅ Utiliser JSON pour les métadonnées flexibles
  • ✅ Activer H2 Console uniquement en dev

DON’T

  • ❌ Stocker des données volumineuses (utiliser PostgreSQL)
  • ❌ Faire des requêtes complexes en boucle doWork()
  • ❌ Activer H2 Console en production
  • ❌ Utiliser pour du cache haute fréquence (utiliser KvBus)

13. Troubleshooting

Base corrompue

# Supprimer et recréer
rm -rf ./data/socle-techdb.*
# Redémarrer l'application

Fichier verrouillé

Database may be already in use: "locked by another process"

Solution : Arrêter l’autre instance ou utiliser AUTO_SERVER=TRUE dans l’URL.

Console H2 inaccessible

  1. Vérifier socle.techdb.console.enabled: true
  2. Vérifier que l’application tourne
  3. Essayer avec le chemin complet du fichier

14. References

Socle V004 – TechDB H2

Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *