From 548eb220dab0424c2c75c9a136dd9721cc0e6a81 Mon Sep 17 00:00:00 2001 From: ncantu Date: Mon, 26 Jan 2026 00:46:02 +0100 Subject: [PATCH] Optimize home page loading by prioritizing critical data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivations:** - La home est très lente au premier lancement - Tous les endpoints sont chargés en parallèle, y compris les plus lents - L'endpoint /api/utxo/list est très lent car il charge toute la liste **Root causes:** - loadData() charge 10 endpoints en parallèle sans priorisation - loadAvailableForAnchor() charge toute la liste UTXO juste pour obtenir un count - Les données moins critiques (avg fee, avg tx amount) bloquent l'affichage initial **Correctifs:** - Chargement en 2 phases : données critiques d'abord, données secondaires ensuite - Création d'un endpoint optimisé /api/utxo/count qui lit directement depuis le fichier texte - loadAvailableForAnchor() utilise maintenant l'endpoint optimisé au lieu de /api/utxo/list - Les données secondaires (avg fee, avg tx amount, avg block time) sont chargées en arrière-plan **Evolutions:** - Affichage initial plus rapide avec les données critiques - Meilleure expérience utilisateur avec chargement progressif - Endpoint optimisé pour obtenir le count sans charger toute la liste UTXO **Pages affectées:** - signet-dashboard/src/server.js : Nouvel endpoint /api/utxo/count - signet-dashboard/public/app.js : Optimisation de loadData() et loadAvailableForAnchor() --- signet-dashboard/public/app.js | 33 ++++++++++++--------- signet-dashboard/src/server.js | 54 ++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 14 deletions(-) diff --git a/signet-dashboard/public/app.js b/signet-dashboard/public/app.js index 3b30b91..3e1debd 100644 --- a/signet-dashboard/public/app.js +++ b/signet-dashboard/public/app.js @@ -61,23 +61,32 @@ function startBlockPolling() { /** * Charge toutes les données de supervision + * Optimisé pour charger d'abord les données critiques, puis les données moins critiques */ async function loadData() { try { + // Phase 1 : Charger les données critiques rapidement (affichage immédiat) await Promise.all([ loadBlockchainInfo(), loadLatestBlock(), loadWalletBalance(), loadAnchorCount(), - loadAvailableForAnchor(), loadNetworkPeers(), loadMiningDifficulty(), - loadAvgBlockTime(), - loadAvgFee(), - loadAvgTxAmount(), ]); updateLastUpdateTime(); + + // Phase 2 : Charger les données moins critiques en arrière-plan (peuvent être plus lentes) + // Ne pas attendre ces données pour mettre à jour l'heure + Promise.all([ + loadAvailableForAnchor(), + loadAvgBlockTime(), + loadAvgFee(), + loadAvgTxAmount(), + ]).catch((error) => { + console.error('Error loading secondary data:', error); + }); } catch (error) { console.error('Error loading data:', error); } @@ -190,13 +199,13 @@ async function loadAnchorCount() { /** * Charge la capacité d'ancrage restante + * Utilise l'endpoint optimisé /api/utxo/count pour éviter de charger toute la liste */ async function loadAvailableForAnchor() { const availableForAnchorValue = document.getElementById('available-for-anchor-value'); const availableForAnchorSpinner = document.getElementById('available-for-anchor-spinner'); - const confirmedAvailableForAnchorValue = document.getElementById('confirmed-available-for-anchor-value'); - if (!availableForAnchorValue || !availableForAnchorSpinner || !confirmedAvailableForAnchorValue) { + if (!availableForAnchorValue || !availableForAnchorSpinner) { console.error('Elements for available-for-anchor not found in DOM'); return; } @@ -204,24 +213,21 @@ async function loadAvailableForAnchor() { // Afficher le spinner availableForAnchorSpinner.style.display = 'inline'; availableForAnchorValue.textContent = '...'; - confirmedAvailableForAnchorValue.textContent = '...'; try { - const response = await fetch(`${API_BASE_URL}/api/utxo/list`); + // Utiliser l'endpoint optimisé qui lit directement depuis le fichier texte + const response = await fetch(`${API_BASE_URL}/api/utxo/count`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); - const counts = data.counts || {}; - const availableForAnchor = counts.availableForAnchor || 0; - const confirmedAvailableForAnchor = counts.confirmedAvailableForAnchor || 0; + const availableForAnchor = data.availableForAnchor || 0; - // Masquer le spinner et mettre à jour les valeurs + // Masquer le spinner et mettre à jour la valeur availableForAnchorSpinner.style.display = 'none'; availableForAnchorValue.textContent = availableForAnchor.toLocaleString('fr-FR') + ' ancrages'; - confirmedAvailableForAnchorValue.textContent = confirmedAvailableForAnchor.toLocaleString('fr-FR'); } catch (error) { console.error('Error loading available for anchor:', error); // Masquer le spinner @@ -231,7 +237,6 @@ async function loadAvailableForAnchor() { if (currentValue === '-' || currentValue === 'Erreur' || currentValue === '...') { availableForAnchorValue.textContent = '0 ancrages'; } - confirmedAvailableForAnchorValue.textContent = '0'; } } diff --git a/signet-dashboard/src/server.js b/signet-dashboard/src/server.js index 394e67e..0e699a9 100644 --- a/signet-dashboard/src/server.js +++ b/signet-dashboard/src/server.js @@ -273,6 +273,60 @@ app.get('/api/hash/list.txt', async (req, res) => { } }); +// Route optimisée pour obtenir uniquement les counts UTXO (sans charger toute la liste) +app.get('/api/utxo/count', async (req, res) => { + try { + const { readFileSync, existsSync } = await import('fs'); + const utxoListPath = join(__dirname, '../../utxo_list.txt'); + + if (!existsSync(utxoListPath)) { + return res.json({ + availableForAnchor: 0, + confirmedAvailableForAnchor: 0, + anchors: 0, + }); + } + + // Lire le fichier et compter rapidement sans parser toute la structure + const content = readFileSync(utxoListPath, 'utf8').trim(); + const lines = content.split('\n').filter(line => line.trim()); + + let anchors = 0; + let availableForAnchor = 0; + let confirmedAvailableForAnchor = 0; + const minAnchorAmount = 2000 / 100000000; // 2000 sats en BTC + + for (const line of lines) { + const parts = line.split(';'); + if (parts.length >= 7) { + const category = parts[0]; + const amount = parseFloat(parts[3]) || 0; + const confirmations = parseInt(parts[4], 10) || 0; + const isAnchorChange = parts[5] === 'true'; + + if (category === 'anchor' && amount >= minAnchorAmount && confirmations > 0) { + anchors++; + // On assume que les UTXOs dans le fichier ne sont pas dépensés (isSpentOnchain serait dans un autre champ) + // Pour être sûr, on vérifie seulement les confirmations + availableForAnchor++; + if (confirmations >= 6) { + confirmedAvailableForAnchor++; + } + } + } + } + + res.json({ + availableForAnchor, + confirmedAvailableForAnchor, + anchors, + }); + } catch (error) { + logger.error('Error getting UTXO count', { error: error.message }); + res.status(500).json({ error: error.message }); + } +}); + // Route pour obtenir la liste des UTXO (fichier texte) app.get('/api/utxo/list', async (req, res) => { try {