Contexte
Je travaille sur une application actix-web utilisant le diesel via r2d2 et je ne suis pas sûr de la meilleure façon de faire des requêtes asynchrones. J'ai trouvé trois options qui semblent raisonnables, mais je ne sais pas laquelle est la meilleure.
Solutions potentielles
Acteur de synchronisation
D'une part, je pourrais utiliser l'exemple d'actix , mais il est assez compliqué et nécessite beaucoup de passe-partout pour être construit. J'espère qu'il existe une solution plus raisonnable.
Actix_web::web::block
Comme autre option, je pourrais utiliser le actix_web::web::block
pour encapsuler mes fonctions de requête dans un avenir, mais je ne suis pas sûr des implications en termes de performances.
La requête s'exécute-t-elle alors dans le même système Tokio? D'après ce que j'ai pu trouver dans la source, il crée un thread dans le pool de threads actix-web sous-jacent . Est-ce un problème?
Si je lis le code correctement, r2d2 bloque son thread lors de l'acquisition d'une connexion, ce qui bloquerait une partie du pool d'actix-web principal. Même chose avec les requêtes de base de données. Cela bloquerait alors tout l'actix-web si je fais plus de requêtes que j'ai de threads dans ce pool? Si oui, gros problème.
Futures-cpupool
Enfin, le pari sûr qui peut avoir des frais généraux inutiles est futures-cpupool . Le problème principal est que cela signifie ajouter une autre caisse à mon projet, bien que je n'aime pas l'idée de plusieurs cpu-pools flottant inutilement dans mon application.
Étant donné que le r2d2 et le diesel se bloqueront, il y a une quantité surprenante de choses délicates ici.
Plus important encore, ne partagez pas ce cpupool avec quoi que ce soit n'utilisant pas le même pool r2d2 (car tous les threads créés peuvent simplement bloquer l'attente d'une connexion r2d2, verrouillant tout le pool lorsque le travail existe).
Deuxièmement (un peu plus évidemment), vous ne devriez donc pas avoir plus de connexions r2d2 que de threads dans le pool et vice-versa car le plus gros gaspillerait des ressources (connexions inutilisées / threads constamment bloqués) (peut-être un thread de plus, pour peut-être plus rapide transfert de connexion par le planificateur du système d'exploitation plutôt que par le planificateur cpupool).
Enfin, pensez à la base de données que vous utilisez et aux performances que vous y avez. L'exécution d'une seule connexion r2d2 et d'un seul thread dans le pool peut être préférable dans une application sqlite lourde en écriture (bien que je recommanderais une base de données appropriée pour cela).
Anciennes réponses
Anciennes solutions qui peuvent fonctionner
https://www.reddit.com/r/rust/comments/axy0hp/patterns_to_scale_actixweb_and_diesel/
Essentiellement, recommande Futures-cpupool.
Quelle est la meilleure approche pour encapsuler les E / S bloquantes dans les futurs rs?
Recommande Futures-cpupool pour les cas généraux.
De vieilles solutions qui ne fonctionnent pas
https://www.reddit.com/r/rust/comments/9fe1ye/noob_here_can_we_talk_about_async_and_databases/
Une très bonne solution pour une ancienne version d'actix-web. D'après ce que je peux trouver, les demandes n'ont plus de pool de processeurs.
la source
futures-cpupool
c'est la solution de contournement recommandée pour le manque deasync
support dans Diesel.Réponses:
Je pars avec futures-cpupool. C'est la meilleure solution en raison de la nature bloquante de mes interactions.
Utiliser actix_web :: web :: block est assez décent, mais utilisera un pool de threads partagé dans actix (et en raison des appels de blocage que j'utilise, cela peut bloquer l'ensemble du pool de threads et interférer avec d'autres tâches données à actix_web).
Il est préférable d'utiliser futures-cpupool pour créer un pool de threads distinct par base de données uniquement pour les interactions avec la base de données. De cette façon, vous regroupez toutes les tâches qui doivent s’attendre les unes les autres (quand il y a plus de tâches que de connexions) dans un pool, les empêchant de bloquer toutes les autres tâches qui n’ont pas besoin d’une connexion et potentiellement de limiter le nombre de threads au nombre de connexions (pour que la tâche ne soit planifiée que lorsqu'elle ne sera pas bloquée).
Dans le cas où vous ne souhaitez utiliser qu'une seule connexion à la base de données (ou très peu), l'acteur de synchronisation est une assez bonne option. Il agira comme un futur-cpupool avec un seul thread, garantissant que toutes les tâches sont exécutées une à la fois, sauf qu'il utilisera l'un des threads sous-jacents d'actix-web plutôt qu'un thread séparé (donc bon pour très peu de connexions) . Je trouve le passe-partout trop gros pour en valoir la peine.
la source