web-dev-qa-db-fra.com

Le diesel doit-il être exécuté à l'aide d'un acteur de synchronisation, actix_web :: web :: block ou futures-cpupool?

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

Pour celui que je pourrais utiliser l'exemple d'actix , mais c'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 envelopper mes fonctions de requête dans un futur, 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 bien le code, 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. Exécuter une seule connexion r2d2 et 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 de blocage 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/

Un correctif vraiment sympa pour une ancienne version d'actix-web. D'après ce que je peux trouver, les demandes n'ont plus de pool de processeurs.

10
logina

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 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.

2
logina