fab-geocommuns / RNB-coeur

Le coeur du Référentiel National des Bâtiments : imports, APIs, logique métier
https://rnb.beta.gouv.fr
Apache License 2.0
3 stars 0 forks source link

API Diff : passer la réponse en streaming #462

Closed fchabouis closed 3 weeks ago

fchabouis commented 4 weeks ago

Dans cette PR, j'utilise la fonction StreamingHttpResponse fournie par Django qui permet de renvoyer une réponse HTTP dont le contenu est généré au fur et à mesure de l'envoi, par opposition à HttpResponse qui prend en argument un contenu qui doit déja être généré au moment où l'envoi commence.

La fonction copy_expert de psycopg2 permet également de streamer du contenu depuis la base de donnée vers le code Python.

La difficultée rencontrée a été de mettre ces 2 streams "bout à bout", c'est à dire que le résultat de copy_expert soit passé au fur et à mesure à StreamingHttpResponse, sans que l’entièreté de la donnée de soit stockée à aucun moment par le code python.

La seule manière de faire que j'ai trouvée jusqu'à présent a été de forker le processus, que le processus child fasse la requête Postgres et stream le résultat sur un pipe qui peut être lu au fur et à mesure par le processus parent qui en envoie le contenu à StreamingHttpResponse.

J'aurais préféré que tout soit fait par le même processus, pour ne pas créer de process au niveau de l'OS pour répondre à une requête GET sur le site, parce qu'on ne peut créer qu'un nombre limité de process sur le serveur avant d'avoir des problèmes.

On peut quand même merger la fonctionnalité, quitte à revenir et l'améliorer par la suite si on voit que créer un thread pour l'occasion pose problème.

J'ai fait un test en local sur un diff massif. Avant la PR, la requête HTTP restait en attente de réponse pendant 8minutes, puis se mettait à télécharger le diff à toute vitesse (toute la donnée est donc générée sur le serveur avant de commencer à être envoyée), risque de timeout important et de crash du serveur si toute la ram est utilisée. Avec le streaming, la réponse met seulement quelques secondes à démarrer, puis se télécharge à un rythme bien plus lent, qui est en fait la vitesse à laquelle la base de donnée arrive à sortir ses résultats. La durée totale de l'opération est sensiblement la même.

j'ai posé une question ici aussi, pas eu de réponse pour le moment.