BMA: Optimize `/tx/history/:pubkey/pending`

I have opened issue #1442 to improve the response time of the BMA /tx/history/:pubkey/pending endpoint.

Cesium makes this request every time the wallet is refreshed. Users may be waiting for a transaction sent by another user, making it necessary for us to retrieve this data from the network.

However, this request can take a considerable amount of time, depending on the performance of the Duniter node.

Upon closely examining the code, I realize that we are systematically accessing all transactions of the public key (the entire history) via the Rust database (in the server.rs file and getTransactionsHistory(pubkey)).

I’m wondering if it would be better to:

  • Either retrieve the pending transactions from the node first, then filter based on the public key?
    • We may use the function getTransactionsPending(versionMin = 0, medianTime = 0).
    • But what are these versionMin and medianTime arguments for? What would be the optimal values in my case?
  • Or create a getPendingTransactionsByPubkey(pubkey) method?
    • I think we can use function get_transactions_history_for_bma() as template, from duniter-gva module.

cc @cgeek @tuxmain

versionMin is the minimum document version for a transaction to be included in the result.

medianTime is not used. (see function get_pending_txs)

I’m not sure what would be the simpler or cleaner way, but if needed I can add something like get_pending_transactions_for_bma.

Merci @tuxmain pour ta réponse. J’ai ajouté deux fonction au module Rust duniter-gva :

  • get_written_transactions_for_bma() qui remplit uniquement sent/received
  • get_pending_transactions_for_bma() : qui remplit uniquement sending/pending
  • j’ai modifiĂ© get_transactions_history_for_bma() pour qu’il appelle les deux autres

Ca compile et les test passent. Youpi !

Maintenant, j’observe get_written_transactions_for_bma() et je vois que le code récupère toujours toutes les TX de la pubkey (le start_k et end_k inclus tous les blocs).
Dans get_written_transactions_for_bma(), je vais donc ajouter deux arguments (start_block et end_block) à dans pour être cohérent avec les paramètres possible de BMA.

Tout cela devrait grandement améliorer les perfs des clients utilisant BMA. Youpi :slight_smile:

3 Likes

2 posts were merged into an existing topic: DĂ©bugger Duniter v1

J’ai fini 2 MR :

J’attends vos retours, @cgeek, @tuxmain , ou bien je merge direct ?

Je vois que @tuxmain tu as aussi des MR en attente. Est-ce toujours d’actualité ? Attends tu un retour particulier ?

1 Like

C’est bon pour moi. (je n’ai pas les droits sur les dépôts donc je ne peux ni approuver ni merger)

Ma MR sur duniter-core peut éventuellement éviter des crashes aux nœuds GVA mais je ne sais pas/plus si c’est déjà arrivé en production. Celles sur duniter-gva sont abandonnées, à moins que quelqu’un ait besoin de la requête idty_by_username.

J’ai les droits. Je peux lancer les merge si j’ai ton feu vert (je suis bien incapable de donner mon avis sur la MR elle-même :slight_smile: ).

J’ai commencé à faire l’oxydation, et j’ai un soucis sur get_written_transactions_for_bma_with_range().
j’ai l’impression que le end_block n’est pas inclus dans la recherche.
Tu en penses quoi @tuxmain ? Connais le système de recherche key/value qu’utilise duniter-gva ?

Est-ce du [start_k, end_k] ou du [start_k, end_k[ quand on fait :

   // ... get_written_transactions_for_bma_with_range()
     .iter_ref_slice(start_k..end_k, |_k, hashs| {
            let mut sent = SmallVec::<[GvaTxDbV1; 2]>::new();
            for hash in hashs {
                if let Some(tx_db) = gva_db_ro.txs().get(HashKeyV2::from_ref(hash))? {
                    sent.push(tx_db);
                }
            }
            Ok(sent)
        })

en effet les ranges en Rust a..b incluent a et non b. Il faut faire end_block+1 pour l’inclure.

Edit: ou plutĂ´t end_block.saturating_add(1) sinon il y aura un panic pour end_block=u32::MAX.

Voilà qui est corrigé, je t’ai invité dans les Owners du groupe nodes/rust.

2 Likes

Merci @tuxmain du coup je m’en suis sorti pour les TX par plage de blocs. Ouf.

Tant que j’y suis, je vais ajouter à ma MR une fonction Rust pour retrouver les TX par plages de times. J’ai qu’il en existe une pour les pages graphql, mais elle utilise le median_time. Je ne sais plus pour BMA si c’était pareil.
Finalement il ne me reste qu’à récupérer les numéros de bloc et le tour est joué :slight_smile:

Voila, j’ai eu du mal mais ca fonctionne : toutes les requetes suivantes ont été optimisé :

  • /tx/history/:pubkey/pending
  • /tx/history/:pubkey/blocks/:from/:to
  • /tx/history/:pubkey/times/:from/:to

J’ai testé sur un noeud DUniter à jour, (branche dev - donc ~1.9.x) : Cesium est maintenant hyper rapide ! 20ms à 100ms pour récupérer les sources, TX, et tout ce qu’il faut sur un compte.

bref, c’est aussi fluide qu’au début de G1 :slight_smile:

Il n’y a plus que se décider pour savoir ce qu’on fait : une 1.8 ou une 1.9 ?

Si c’est une 1.8, il faut savoir si le module de stocker Rust utilisé par GVA, que j’ai utilisé pour mes optimisation, était déployée…
As tu une idée @cgeek ?

3 Likes

En cherchant “gva” dans le tag v1.8.6 la seule mention est dans un README, parlant au futur, alors que dans dev il y a plein d’occurrences dans le code. Je crois que le seul rôle du Rust en 1.8 est la PoW et une partie de la CLI, c’était surtout une expérimentation d’oxydation.

1 Like

Merci @tuxmain . Du coup, on va devoir définir une stratégie pour tester la v1.9
Je crois voir qu’il y a déjà ma mal de noeud 1.9 en prod, sans doute grâce à l’image Docker de @Pini ?

As tu des retours, Pini, de tout ça ? Ces nœuds 1.9 tournent bien et ne se désynchonisent pas ?

@cgeek qu’en penses-tu ? Si on fix les tests pour qu’ils passent sur la 1.9, on ne prend pas trop de risque, si ?

Les noeuds en 1.9 se comportent correctement. Il peut y avoir des désynchro dues à des forks qui ont du mal à se résoudre, mais je ne pense pas que ce soit pire que les 1.8. Il y a au moins 1 bug qui a été résolu dans la 1.9 et pas dans la 1.8 qui permet à la synchro de mieux se passer.

A priori, Duniter 1.8 n’utilise pas duniter-gva ni duniter-core. Les TX sont lues depuis la table SqlLite.
J’ai commencé à optimiser le code, pour que le filtrage soit fait par requête SQL, via un where sur block_number ou time, plutôt que de tout parser et filtrer en JS

1 Like

La MR suivante devrait déjà améliorer tx/history/* en Duniter v1.8 : fix(1442): Optimize BMA /tx/history (!1429) · Merge requests · nodes / typescript / duniter · GitLab

1 Like