ncantu 0960e43a45 Optimisation mémoire api-anchorage avec base de données SQLite
**Motivations:**
- Réduction drastique de la consommation mémoire lors des ancrages
- Élimination du chargement de 173k+ UTXOs à chaque requête
- Stabilisation de la mémoire système sous charge élevée (50+ ancrages/minute)

**Root causes:**
- api-anchorage chargeait tous les UTXOs (173k+) via listunspent RPC à chaque ancrage
- Filtrage et tri de 173k+ objets en mémoire pour sélectionner un seul UTXO
- Croissance mémoire de ~16 MB toutes les 12 secondes avec 50 ancrages/minute
- Saturation mémoire système en quelques minutes

**Correctifs:**
- Création du module database.js pour gérer la base de données SQLite partagée
- Remplacement de listunspent RPC par requête SQL directe avec LIMIT 1
- Sélection directe d'un UTXO depuis la DB au lieu de charger/filtrer 173k+ objets
- Marquage des UTXOs comme dépensés dans la DB après utilisation
- Fermeture propre de la base de données lors de l'arrêt

**Evolutions:**
- Utilisation de la base de données SQLite partagée avec signet-dashboard
- Réduction mémoire de 99.999% (173k+ objets → 1 objet par requête)
- Amélioration des performances (requête SQL indexée vs filtrage en mémoire)
- Optimisation mémoire de signet-dashboard (chargement UTXOs seulement si nécessaire)
- Monitoring de lockedUtxos dans api-anchorage pour détecter les fuites
- Nettoyage des intervalles frontend pour éviter les fuites mémoire

**Pages affectées:**
- api-anchorage/src/database.js (nouveau)
- api-anchorage/src/bitcoin-rpc.js
- api-anchorage/src/server.js
- api-anchorage/package.json
- signet-dashboard/src/bitcoin-rpc.js
- signet-dashboard/public/app.js
- features/optimisation-memoire-applications.md (nouveau)
- features/api-anchorage-optimisation-base-donnees.md (nouveau)
2026-01-27 21:12:22 +01:00

100 lines
3.0 KiB
JavaScript

/**
* Script d'initialisation de la base de données SQLite
* Crée les tables et les index nécessaires
*/
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { createRequire } from 'module';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Utiliser better-sqlite3 depuis signet-dashboard/node_modules
const require = createRequire(join(__dirname, '../signet-dashboard/package.json'));
const Database = require('better-sqlite3');
const dbPath = join(__dirname, 'signet.db');
const db = new Database(dbPath);
// Activer les clés étrangères
db.pragma('foreign_keys = ON');
// Table utxos
db.exec(`
CREATE TABLE IF NOT EXISTS utxos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category TEXT NOT NULL,
txid TEXT NOT NULL,
vout INTEGER NOT NULL,
address TEXT,
amount REAL NOT NULL,
confirmations INTEGER DEFAULT 0,
is_anchor_change BOOLEAN DEFAULT FALSE,
block_time INTEGER,
is_spent_onchain BOOLEAN DEFAULT FALSE,
is_locked_in_mutex BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(txid, vout)
);
CREATE INDEX IF NOT EXISTS idx_utxos_category ON utxos(category);
CREATE INDEX IF NOT EXISTS idx_utxos_txid_vout ON utxos(txid, vout);
CREATE INDEX IF NOT EXISTS idx_utxos_confirmations ON utxos(confirmations);
CREATE INDEX IF NOT EXISTS idx_utxos_amount ON utxos(amount);
CREATE INDEX IF NOT EXISTS idx_utxos_is_spent ON utxos(is_spent_onchain);
`);
// Table anchors (hash_list.txt)
db.exec(`
CREATE TABLE IF NOT EXISTS anchors (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hash TEXT NOT NULL UNIQUE,
txid TEXT NOT NULL,
block_height INTEGER,
confirmations INTEGER DEFAULT 0,
date TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_anchors_hash ON anchors(hash);
CREATE INDEX IF NOT EXISTS idx_anchors_txid ON anchors(txid);
CREATE INDEX IF NOT EXISTS idx_anchors_block_height ON anchors(block_height);
`);
// Table fees (fees_list.txt)
db.exec(`
CREATE TABLE IF NOT EXISTS fees (
id INTEGER PRIMARY KEY AUTOINCREMENT,
txid TEXT NOT NULL UNIQUE,
fee REAL NOT NULL,
fee_sats INTEGER NOT NULL,
block_height INTEGER,
block_time INTEGER,
confirmations INTEGER DEFAULT 0,
change_address TEXT,
change_amount REAL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_fees_txid ON fees(txid);
CREATE INDEX IF NOT EXISTS idx_fees_block_height ON fees(block_height);
`);
// Table cache pour suivre les mises à jour
db.exec(`
CREATE TABLE IF NOT EXISTS cache (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
`);
console.log('✅ Base de données initialisée avec succès');
console.log(`📁 Fichier: ${dbPath}`);
db.close();