Sollte Diesel mit einem Synchronisator, actix_web :: web :: block oder futures-cpupool betrieben werden?

10

Hintergrund

Ich arbeite an einer Actix-Webanwendung mit Diesel über r2d2 und bin mir nicht sicher, wie ich am besten asynchrone Abfragen durchführen kann. Ich habe drei Optionen gefunden, die vernünftig erscheinen, bin mir aber nicht sicher, welche die beste ist.

Potentielle Lösungen

Schauspieler synchronisieren

Zum einen könnte ich das Actix-Beispiel verwenden , aber es ist ziemlich kompliziert und erfordert einiges an Boilerplate, um es zu bauen. Ich hoffe, es gibt eine vernünftigere Lösung.

Actix_web::web::block

Als weitere Option könnte ich actix_web::web::blockmeine Abfragefunktionen in eine Zukunft einbinden, bin mir jedoch nicht sicher, welche Auswirkungen dies auf die Leistung hat.

Läuft die Abfrage dann im selben Tokio-System? Nach dem, was ich in der Quelle finden konnte, wird ein Thread im zugrunde liegenden Actix-Web-Threadpool erstellt . Ist das ein Problem?

Wenn ich den Code richtig lese, blockiert r2d2 seinen Thread beim Erwerb einer Verbindung, wodurch ein Teil des zentralen Actix-Web-Pools blockiert wird. Gleiches gilt für Datenbankabfragen. Dies würde dann das gesamte actix-web blockieren, wenn ich mehr Abfragen mache, als ich Threads in diesem Pool habe? Wenn ja, großes Problem.

Futures-cpupool

Schließlich ist futures-cpupool die sichere Wette, die möglicherweise unnötigen Overhead verursacht . Das Hauptproblem ist, dass dies bedeutet, meinem Projekt eine weitere Kiste hinzuzufügen, obwohl mir die Idee, dass mehrere CPU-Pools unnötig in meiner Anwendung herumschweben, nicht gefällt.

Da sowohl r2d2 als auch Diesel blockieren, gibt es hier überraschend viele knifflige Dinge.

Teilen Sie diesen cpupool vor allem nicht mit Benutzern, die nicht denselben r2d2-Pool verwenden (da alle erstellten Threads möglicherweise nur das Warten auf eine r2d2-Verbindung blockieren und den gesamten Pool sperren, wenn Arbeit vorhanden ist).

Zweitens (etwas offensichtlicher) sollten Sie daher nicht mehr r2d2-Verbindungen als Threads im Pool haben und umgekehrt, da der größere Ressourcen verschwenden würde (nicht verwendete Verbindungen / ständig blockierte Threads) (vielleicht ein weiterer Thread, vielleicht schneller Verbindungsübergabe durch den OS-Scheduler und nicht durch den cpupool-Scheduler.

Denken Sie schließlich daran, welche Datenbank Sie verwenden und welche Leistung Sie dort haben. Das Ausführen einer einzelnen Verbindung r2d2 und eines einzelnen Threads im Pool ist möglicherweise am besten in einer schreiblastigen SQLite-Anwendung (obwohl ich eine geeignete Datenbank für eine solche empfehlen würde).

Alte Antworten

Alte Lösungen, die funktionieren könnten

https://www.reddit.com/r/rust/comments/axy0hp/patterns_to_scale_actixweb_and_diesel/

Im Wesentlichen empfiehlt Futures-cpupool.

Was ist der beste Ansatz, um blockierende E / A in zukünftigen Daten zu kapseln?

Empfiehlt Futures-cpupool für allgemeine Fälle.

Alte Lösungen, die nicht funktionieren

https://www.reddit.com/r/rust/comments/9fe1ye/noob_here_can_we_talk_about_async_and_databases/

Eine wirklich schöne Lösung für eine alte Actix-Web-Version. Nach allem, was ich finden kann, haben Anfragen keinen CPU-Pool mehr.

Logina
quelle
Aus den Kommentaren in dieser Ausgabe geht hervor , dass dies futures-cpupooldie empfohlene Problemumgehung für die mangelnde asyncUnterstützung von Diesel ist.
Jmb
Das ist eher eine allgemeine Lösung. Ich hoffe auf etwas, das das Actix-System nutzt. Trotzdem werde ich mich jetzt mit futures-cpupool befassen, um nach Problemen zu suchen.
Logina
Willkommen bei Stack Overflow! Es sieht so aus, als ob Ihre Frage durch die Antworten von Was ist der beste Ansatz, um blockierende E / A in zukünftigen Rs zu kapseln , beantwortet werden könnte ? . Wenn nicht, bearbeiten Sie bitte Ihre Frage, um die Unterschiede zu erklären. Andernfalls können wir diese Frage als bereits beantwortet markieren.
Shepmaster
Da der cpupool auch mit dem blockierenden Verbindungspool in r2d2 interagiert, bin ich mir nicht sicher, wie ich das am besten beheben kann. Ich prüfe es jetzt selbst und werde es bald aktualisieren.
Logina

Antworten:

3

Ich gehe mit Futures-Cpupool. Es ist die beste Lösung, da meine Interaktionen blockierend sind.

Die Verwendung von actix_web :: web :: block ist anständig genug, verwendet jedoch einen gemeinsam genutzten Thread-Pool in actix (und aufgrund der von mir verwendeten blockierenden Aufrufe kann dies den gesamten Thread-Pool blockieren und andere Aufgaben von actix_web stören).

Es ist besser, futures-cpupool zu verwenden, um einen separaten Threadpool pro Datenbank nur für Datenbankinteraktionen zu erstellen. Auf diese Weise gruppieren Sie alle Aufgaben, die aufeinander warten müssen (wenn mehr Aufgaben als Verbindungen vorhanden sind), in einem Pool, um zu verhindern, dass andere Aufgaben blockiert werden, für die keine Verbindung erforderlich ist, und begrenzen möglicherweise die Anzahl der Threads auf die Anzahl der Verbindungen (damit die Aufgabe nur geplant wird, wenn sie nicht blockiert wird).

In dem Fall, dass Sie nur eine Datenbankverbindung (oder nur sehr wenige) verwenden möchten, ist der Synchronisierungsakteur eine ziemlich gute Option. Es verhält sich wie ein Futures-cpupool mit einem Thread und stellt sicher, dass alle Aufgaben einzeln ausgeführt werden, außer dass einer der zugrunde liegenden Threads von actix-web anstelle eines separaten Threads verwendet wird (daher nur für sehr wenige Verbindungen geeignet). . Ich finde das Boilerplate jedoch zu groß, um es wert zu sein.

Logina
quelle
6
Lesen Sie meine Ergebnisse oben - bitte geben Sie Informationen, die für die Antwort relevant sind, in die Antwort ein , nicht in die Frage.
Shepmaster