# Optimisation api-anchorage avec base de données **Date:** 2026-01-27 **Auteur:** Équipe 4NK ## Objectif Optimiser `api-anchorage` pour utiliser la base de données SQLite au lieu de charger tous les UTXOs (173k+) à chaque requête d'ancrage, réduisant ainsi drastiquement la consommation mémoire. ## Problème identifié ### Root cause Lors de chaque ancrage, `api-anchorage` : 1. Appelait `listunspent` via RPC qui retournait **173k+ UTXOs** 2. Chargeait tous ces UTXOs en mémoire 3. Filtrait et triait tous les UTXOs en mémoire 4. Sélectionnait un seul UTXO **Impact :** - Avec 50 ancrages/minute, cela représentait **8.65 millions d'objets UTXO chargés en mémoire par minute** - Croissance mémoire de ~16 MB toutes les 12 secondes - Saturation mémoire système en quelques minutes ### Observations - **Mémoire système** : 9.6 Gi utilisés / 12 Gi (80%) - **api-anchorage** : croissance de 429 MB → 445 MB en 12 secondes - **bitcoind** : 4.87 GB (36.9% RAM) ## Solution ### Stratégie Utiliser la base de données SQLite (partagée avec `signet-dashboard`) pour : 1. **Sélectionner directement un UTXO** depuis la DB avec une requête SQL optimisée 2. **Ne charger qu'un seul UTXO** au lieu de tous les UTXOs 3. **Marquer l'UTXO comme dépensé** dans la DB après utilisation ### Avantages - **Réduction mémoire** : De 173k+ objets → 1 objet par requête (réduction de 99.999%) - **Performance** : Requête SQL indexée beaucoup plus rapide que charger/filtrer 173k+ objets - **Synchronisation** : Utilise la même base de données que `signet-dashboard` qui maintient les UTXOs à jour ## Modifications ### 1. Nouveau module database.js **Fichier:** `api-anchorage/src/database.js` - Module de gestion de la base de données SQLite - Utilise la même base de données que `signet-dashboard` (`data/signet.db`) - Singleton pour la connexion - Gestion de la fermeture propre ### 2. Modification de bitcoin-rpc.js **Fichier:** `api-anchorage/src/bitcoin-rpc.js` **Avant:** ```javascript // Charger TOUS les UTXOs via RPC const rpcResponse = await fetch(rpcUrl, { method: 'POST', body: JSON.stringify({ method: 'listunspent', params: [1], }), }); const unspent = rpcResult.result; // 173k+ UTXOs // Filtrer et trier en mémoire const availableUtxos = unspent .filter(utxo => !this.isUtxoLocked(utxo.txid, utxo.vout)) .filter(utxo => (utxo.confirmations || 0) > 0) .sort((a, b) => b.amount - a.amount); // Sélectionner un UTXO selectedUtxo = availableUtxos.find(utxo => utxo.amount >= totalNeeded); ``` **Après:** ```javascript // Sélectionner directement un UTXO depuis la DB const db = getDatabase(); const utxoQuery = db.prepare(` SELECT txid, vout, address, amount, confirmations, block_time FROM utxos WHERE confirmations > 0 AND is_spent_onchain = 0 AND amount >= ? ORDER BY amount DESC LIMIT 1 `); const utxoFromDb = utxoQuery.get(totalNeeded); // Filtrer les UTXOs verrouillés si nécessaire if (lockedKeys.length > 0) { const candidates = utxoQuery.all(totalNeeded); utxoFromDb = candidates.find(utxo => { const key = `${utxo.txid}:${utxo.vout}`; return !this.lockedUtxos.has(key); }); } ``` **Changements:** - Suppression de l'appel RPC `listunspent` - Requête SQL directe avec `LIMIT 1` pour ne charger qu'un seul UTXO - Filtrage des UTXOs verrouillés en mémoire (seulement si nécessaire) - Marquer l'UTXO comme dépensé dans la DB après utilisation ### 3. Mise à jour de server.js **Fichier:** `api-anchorage/src/server.js` - Ajout de l'import `closeDatabase` - Fermeture propre de la base de données lors de l'arrêt ### 4. Dépendance better-sqlite3 **Fichier:** `api-anchorage/package.json` - Ajout de `better-sqlite3` comme dépendance ## Evolutions ### Synchronisation avec signet-dashboard La base de données est maintenue à jour par `signet-dashboard` qui : - Met à jour les UTXOs lors de nouveaux blocs - Marque les UTXOs comme dépensés - Gère les confirmations `api-anchorage` utilise cette base de données en lecture/écriture : - **Lecture** : Sélection d'un UTXO disponible - **Écriture** : Marquage d'un UTXO comme dépensé après utilisation ### Gestion des UTXOs verrouillés Les UTXOs verrouillés dans le mutex (`lockedUtxos`) sont filtrés : - Si aucun UTXO verrouillé : requête SQL simple avec `LIMIT 1` - Si UTXOs verrouillés : requête avec plusieurs candidats, filtrage en mémoire ## Pages affectées - `api-anchorage/src/database.js` : Nouveau module - `api-anchorage/src/bitcoin-rpc.js` : Remplacement de `listunspent` par requête SQL - `api-anchorage/src/server.js` : Fermeture propre de la DB - `api-anchorage/package.json` : Ajout de `better-sqlite3` ## Modalités de déploiement 1. **Installer la dépendance:** ```bash cd api-anchorage npm install ``` 2. **Vérifier que la base de données existe:** ```bash ls -la ../data/signet.db ``` 3. **Redémarrer le service:** ```bash sudo systemctl restart anchorage-api.service ``` 4. **Vérifier les logs:** ```bash journalctl -u anchorage-api.service -f | grep "Selected UTXO from database" ``` ## Modalités d'analyse ### Avant optimisation - **Mémoire par requête** : ~173k objets UTXO chargés - **Temps de réponse** : ~500-1000ms (appel RPC + filtrage) - **Croissance mémoire** : ~16 MB toutes les 12 secondes avec 50 ancrages/minute ### Après optimisation - **Mémoire par requête** : 1 objet UTXO chargé - **Temps de réponse** : ~10-50ms (requête SQL indexée) - **Croissance mémoire** : Négligeable (1 objet par requête) ### Métriques à surveiller - Temps de réponse des requêtes `/api/anchor/document` - Consommation mémoire de `api-anchorage` (devrait rester stable) - Nombre d'erreurs "No available UTXOs in database" - Synchronisation avec `signet-dashboard` (les UTXOs doivent être à jour) ## Notes - La base de données doit être maintenue à jour par `signet-dashboard` - Si la base de données n'est pas à jour, `api-anchorage` peut ne pas trouver d'UTXOs disponibles - Les UTXOs verrouillés sont toujours gérés via le mutex en mémoire - La requête SQL utilise les index existants (`idx_utxos_amount`, `idx_utxos_confirmations`, etc.)