**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)
299 lines
9.3 KiB
Markdown
299 lines
9.3 KiB
Markdown
# Optimisation api-anchorage pour réduire la charge sur bitcoind
|
||
|
||
**Date:** 2026-01-27
|
||
**Auteur:** Équipe 4NK
|
||
|
||
## Objectif
|
||
|
||
Réduire la charge RPC de `api-anchorage` sur `bitcoind` sans modifier le code de l'application, uniquement via des optimisations de configuration et d'infrastructure.
|
||
|
||
## Analyse de la consommation RPC
|
||
|
||
### Appels RPC identifiés
|
||
|
||
#### 1. Health Check (`/health`)
|
||
- **Fréquence:** À chaque requête GET `/health`
|
||
- **Appels RPC:**
|
||
- `getNetworkInfo()`
|
||
- `getBlockchainInfo()`
|
||
- **Impact:** 2 appels RPC par health check
|
||
|
||
#### 2. Création de transaction d'ancrage (`POST /api/anchor/document`)
|
||
- **Appels RPC par transaction:**
|
||
- `getNewAddress()` - **9 fois** (1 anchor + 7 provisioning + 1 change)
|
||
- `getBalance()` - 1 fois
|
||
- `listunspent` - 1 fois (via fetch direct)
|
||
- `createrawtransaction` - 1 fois
|
||
- `signrawtransactionwithwallet` - 1 fois
|
||
- `sendrawtransaction` - 1 fois
|
||
- `getTransaction()` - 1 fois
|
||
- `getBlockchainInfo()` - 1 fois
|
||
- `getRawTransaction()` - **plusieurs fois** (pour calculer les frais, 1 par input)
|
||
- **Total:** ~15-20 appels RPC par transaction d'ancrage
|
||
|
||
#### 3. Vérification d'ancrage (`POST /api/anchor/verify`)
|
||
- **Appels RPC:**
|
||
- `getBlockchainInfo()` - 1 fois
|
||
- `getBlockHash()` - **100 fois** (recherche dans les 100 derniers blocs)
|
||
- `getBlock()` - **100 fois** (un par bloc)
|
||
- `getRawTransaction()` - **plusieurs centaines** (une par transaction dans chaque bloc)
|
||
- **Impact:** Très élevé si beaucoup de transactions par bloc
|
||
|
||
## Optimisations possibles (sans modifier le code)
|
||
|
||
### 1. Cache HTTP pour le health check
|
||
|
||
**Problème:** Le health check fait 2 appels RPC à chaque requête.
|
||
|
||
**Solution:** Mettre en cache la réponse du health check côté nginx.
|
||
|
||
**Configuration nginx (sur le proxy):**
|
||
```nginx
|
||
location /health {
|
||
proxy_pass http://192.168.1.103:3010/health;
|
||
proxy_cache_valid 200 30s; # Cache 30 secondes
|
||
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
|
||
add_header X-Cache-Status $upstream_cache_status;
|
||
}
|
||
```
|
||
|
||
**Impact:** Réduction de 95%+ des appels RPC pour le health check si appelé fréquemment.
|
||
|
||
### 2. Augmenter le timeout RPC
|
||
|
||
**Configuration actuelle:** `BITCOIN_RPC_TIMEOUT=30000` (30 secondes)
|
||
|
||
**Recommandation:** Augmenter à 60000ms (60 secondes) pour éviter les timeouts lors de pics de charge.
|
||
|
||
**Fichier:** `.env`
|
||
```env
|
||
BITCOIN_RPC_TIMEOUT=60000
|
||
```
|
||
|
||
**Impact:** Réduction des erreurs et reconnexions lors de pics de charge.
|
||
|
||
### 3. Optimiser la configuration bitcoind RPC
|
||
|
||
**Configuration bitcoin.conf:**
|
||
```conf
|
||
# Augmenter le nombre de threads RPC
|
||
rpcthreads=16
|
||
|
||
# Augmenter la taille du cache RPC
|
||
rpcmaxconnections=128
|
||
|
||
# Timeout RPC plus long
|
||
rpcservertimeout=60
|
||
|
||
# Désactiver les fonctionnalités non utilisées
|
||
disablewallet=0 # Nécessaire pour api-anchorage
|
||
txindex=1 # Nécessaire pour les vérifications
|
||
```
|
||
|
||
**Impact:** Meilleure gestion des requêtes concurrentes, réduction de la latence.
|
||
|
||
### 4. Limiter la fréquence des health checks externes
|
||
|
||
**Problème:** Si un monitoring externe appelle `/health` très fréquemment (toutes les 5-10 secondes), cela génère beaucoup d'appels RPC.
|
||
|
||
**Solution:**
|
||
- Configurer le monitoring pour appeler `/health` toutes les 30-60 secondes au lieu de 5-10 secondes
|
||
- Utiliser le cache nginx (voir point 1) pour les appels plus fréquents
|
||
|
||
**Impact:** Réduction proportionnelle des appels RPC.
|
||
|
||
### 5. Pool de connexions HTTP keep-alive
|
||
|
||
**Problème:** La bibliothèque `bitcoin-core` peut créer de nouvelles connexions pour chaque requête.
|
||
|
||
**Solution:** Vérifier que bitcoind accepte les connexions keep-alive (par défaut activé).
|
||
|
||
**Vérification:**
|
||
```bash
|
||
# Vérifier que bitcoind accepte keep-alive
|
||
docker exec bitcoin-signet-instance bitcoin-cli -signet getnetworkinfo | grep -i keep
|
||
```
|
||
|
||
**Impact:** Réduction de la surcharge de connexions TCP.
|
||
|
||
### 6. Limiter les requêtes concurrentes
|
||
|
||
**Problème:** Si plusieurs requêtes d'ancrage arrivent simultanément, elles font toutes des appels RPC en parallèle.
|
||
|
||
**Solution:** Mettre en place un rate limiting côté nginx pour limiter le nombre de requêtes simultanées.
|
||
|
||
**Configuration nginx:**
|
||
```nginx
|
||
limit_req_zone $binary_remote_addr zone=anchor_api:10m rate=10r/s;
|
||
|
||
location /api/anchor {
|
||
limit_req zone=anchor_api burst=5 nodelay;
|
||
proxy_pass http://192.168.1.103:3010;
|
||
}
|
||
```
|
||
|
||
**Impact:** Réduction de la charge lors de pics de trafic, meilleure stabilité.
|
||
|
||
### 7. Optimiser la recherche dans verifyAnchor
|
||
|
||
**Problème:** `verifyAnchor` recherche dans les 100 derniers blocs, ce qui génère des centaines d'appels RPC.
|
||
|
||
**Note:** Cette optimisation nécessiterait une modification du code pour réduire le nombre de blocs recherchés ou utiliser un index. **Non applicable sans modification du code.**
|
||
|
||
**Alternative:** Limiter l'accès à l'endpoint `/api/anchor/verify` via rate limiting strict.
|
||
|
||
**Configuration nginx:**
|
||
```nginx
|
||
limit_req_zone $binary_remote_addr zone=verify_api:10m rate=1r/s;
|
||
|
||
location /api/anchor/verify {
|
||
limit_req zone=verify_api burst=2 nodelay;
|
||
proxy_pass http://192.168.1.103:3010;
|
||
}
|
||
```
|
||
|
||
**Impact:** Réduction drastique des appels RPC pour la vérification.
|
||
|
||
### 8. Monitoring et alertes
|
||
|
||
**Solution:** Mettre en place un monitoring des appels RPC pour identifier les pics de charge.
|
||
|
||
**Métriques à surveiller:**
|
||
- Nombre d'appels RPC par seconde
|
||
- Latence des appels RPC
|
||
- Taux d'erreur RPC
|
||
- Utilisation CPU/mémoire de bitcoind
|
||
|
||
**Impact:** Identification proactive des problèmes de performance.
|
||
|
||
## Recommandations prioritaires
|
||
|
||
### Priorité haute (impact immédiat)
|
||
|
||
1. **Cache HTTP pour `/health`** - Impact immédiat si le health check est appelé fréquemment
|
||
2. **Rate limiting pour `/api/anchor/verify`** - Réduction drastique des appels RPC coûteux
|
||
3. **Augmenter `BITCOIN_RPC_TIMEOUT`** - Réduction des erreurs et reconnexions
|
||
|
||
### Priorité moyenne
|
||
|
||
4. **Optimiser la configuration bitcoind RPC** - Amélioration générale des performances
|
||
5. **Rate limiting général pour `/api/anchor`** - Protection contre les pics de trafic
|
||
|
||
### Priorité basse
|
||
|
||
6. **Monitoring des appels RPC** - Visibilité et optimisation future
|
||
7. **Limiter la fréquence des health checks externes** - Si applicable
|
||
|
||
## Modalités de déploiement
|
||
|
||
### 1. Cache HTTP pour health check
|
||
|
||
**Fichier:** Configuration nginx sur le proxy (192.168.1.100)
|
||
|
||
**Étapes:**
|
||
1. Identifier le fichier de configuration nginx pour `certificator.4nkweb.com`
|
||
2. Ajouter la configuration de cache pour `/health`
|
||
3. Tester avec `curl -I https://certificator.4nkweb.com/health`
|
||
4. Vérifier le header `X-Cache-Status`
|
||
|
||
### 2. Augmenter le timeout RPC
|
||
|
||
**Fichier:** `/home/ncantu/Bureau/code/bitcoin/api-anchorage/.env`
|
||
|
||
**Modification:**
|
||
```env
|
||
BITCOIN_RPC_TIMEOUT=60000
|
||
```
|
||
|
||
**Redémarrage:**
|
||
```bash
|
||
systemctl restart anchorage-api.service
|
||
```
|
||
|
||
### 3. Rate limiting nginx
|
||
|
||
**Fichier:** Configuration nginx sur le proxy
|
||
|
||
**Étapes:**
|
||
1. Ajouter les zones de rate limiting
|
||
2. Appliquer aux endpoints appropriés
|
||
3. Tester avec des requêtes multiples
|
||
4. Ajuster les limites selon les besoins
|
||
|
||
### 4. Optimisation bitcoind
|
||
|
||
**Fichier:** Configuration bitcoin dans le conteneur Docker
|
||
|
||
**Étapes:**
|
||
1. Identifier le fichier `bitcoin.conf` du conteneur
|
||
2. Ajouter les paramètres d'optimisation
|
||
3. Redémarrer le conteneur bitcoin-signet-instance
|
||
4. Vérifier les performances
|
||
|
||
## Modalités d'analyse
|
||
|
||
### Métriques à surveiller
|
||
|
||
**Avant optimisation:**
|
||
```bash
|
||
# Compter les appels RPC par minute
|
||
docker exec bitcoin-signet-instance bitcoin-cli -signet getrpcinfo
|
||
|
||
# Surveiller la charge CPU de bitcoind
|
||
top -p $(pgrep -f bitcoind)
|
||
|
||
# Surveiller les logs d'api-anchorage
|
||
journalctl -u anchorage-api.service -f
|
||
```
|
||
|
||
**Après optimisation:**
|
||
- Comparer le nombre d'appels RPC
|
||
- Comparer la latence des requêtes
|
||
- Comparer l'utilisation CPU/mémoire de bitcoind
|
||
- Vérifier le taux de cache hit pour `/health`
|
||
|
||
### Tests de charge
|
||
|
||
**Test 1: Health check fréquent**
|
||
```bash
|
||
# Avant cache
|
||
for i in {1..100}; do curl -s https://certificator.4nkweb.com/health > /dev/null; done
|
||
|
||
# Après cache (vérifier X-Cache-Status: HIT)
|
||
for i in {1..100}; do curl -sI https://certificator.4nkweb.com/health | grep X-Cache-Status; done
|
||
```
|
||
|
||
**Test 2: Vérification d'ancrage**
|
||
```bash
|
||
# Tester avec rate limiting
|
||
for i in {1..10}; do
|
||
curl -X POST https://certificator.4nkweb.com/api/anchor/verify \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"hash":"0000000000000000000000000000000000000000000000000000000000000000"}' \
|
||
-w "\nTime: %{time_total}s\n"
|
||
done
|
||
```
|
||
|
||
## Impact attendu
|
||
|
||
### Réduction des appels RPC
|
||
|
||
- **Health check:** 95%+ de réduction si appelé fréquemment (avec cache)
|
||
- **Verify anchor:** 90%+ de réduction (avec rate limiting à 1 req/s)
|
||
- **Create anchor:** 10-20% de réduction (avec rate limiting et timeout optimisé)
|
||
|
||
### Amélioration des performances
|
||
|
||
- **Latence RPC:** Réduction de 20-30% avec configuration bitcoind optimisée
|
||
- **Stabilité:** Réduction des erreurs de timeout et reconnexions
|
||
- **Charge CPU bitcoind:** Réduction de 30-50% lors de pics de trafic
|
||
|
||
## Limitations
|
||
|
||
Ces optimisations ne peuvent pas résoudre:
|
||
- Le nombre élevé d'appels `getNewAddress()` dans `createAnchorTransaction` (9 appels par transaction)
|
||
- La recherche exhaustive dans `verifyAnchor` (100 blocs × transactions par bloc)
|
||
- Les appels `getRawTransaction()` multiples pour le calcul des frais
|
||
|
||
Pour ces optimisations, une modification du code serait nécessaire.
|