Fuite de mémoire dans l'application Google App Engine / Datastore / Flask / Python

Sep 15 2020

J'ai créé un simple site d'agrégation de nouvelles, dans lequel l'utilisation de la mémoire de toutes mes instances App Engine continue d'augmenter jusqu'à atteindre la limite et donc d'être arrêtée.

J'ai commencé à tout éliminer de mon application pour arriver à une version minimale reproductible. Voici ce que j'ai maintenant:


app = Flask(__name__)

datastore_client = datastore.Client()

@app.route('/')
def root():
    
    query = datastore_client.query(kind='source')
    query.order = ['list_sequence']
    sources = query.fetch() 
    
    for source in sources:
        pass
    

Les statistiques montrent un schéma en dents de scie typique: au démarrage de l'instance, il passe à 190 - 210 Mo, puis sur certaines demandes, mais PAS TOUTES les demandes, l'utilisation de la mémoire augmente de 20 à 30 Mo. (Ceci, en passant, correspond à peu près à la taille estimée des résultats de la requête, bien que je ne puisse pas être sûr que ce sont des informations pertinentes.) Cela se produit jusqu'à ce qu'il dépasse 512 Mo, lorsqu'il est arrêté. Cela se produit généralement aux alentours de la 50e - 100e requête à "/". Aucune autre demande n'est faite à quoi que ce soit d'autre dans l'intervalle.

Maintenant, si j'élimine le cycle «pour», et que seule la requête demeure, le problème disparaît, l'utilisation de la mémoire reste à 190 Mo à plat, aucune augmentation même après plus de 100 requêtes.

gc.collect () à la fin n'aide pas. J'ai également essayé de regarder la différence dans les statistiques tracemalloc au début et à la fin de la fonction, je n'ai rien trouvé d'utile.

Quelqu'un a-t-il vécu quelque chose de similaire, s'il vous plaît? Des idées sur ce qui pourrait mal tourner ici? Quels tests / investigations supplémentaires pouvez-vous recommander? S'agit-il d'un problème Google App Engine / Datastore sur lequel je n'ai aucun contrôle?

Je vous remercie.

Réponses

1 yedpodtrzitko Sep 17 2020 at 02:48

@Alex dans l'autre réponse a fait une assez bonne recherche, donc je vais poursuivre avec cette recommandation: essayez d'utiliser la bibliothèque NDB . Tous les appels avec cette bibliothèque doivent être enveloppés dans un gestionnaire de contexte, qui devrait garantir le nettoyage après la fermeture. Cela pourrait vous aider à résoudre votre problème:

ndb_client = ndb.Client(**init_client)

with ndb_client.context():
    query = MyModel.query().order(MyModel.my_column)
    sources = query.fetch()
    for source in sources:
        pass

# if you try to query DataStore outside the context manager, it will raise an error
query = MyModel.query().order(MyModel.my_column)
2 Alex Sep 16 2020 at 20:03

Maintenant, si j'élimine le cycle «pour», et que seule la requête demeure, le problème disparaît, l'utilisation de la mémoire reste à 190 Mo à plat, aucune augmentation même après plus de 100 requêtes.

query.fetch() renvoie un itérateur, pas un tableau réel des résultats

https://googleapis.dev/python/datastore/latest/queries.html#google.cloud.datastore.query.Query.fetch

En regardant le code source, il semble que cet itérateur a du code pour récupérer les pages suivantes de la requête. Donc, vous êtes for-loop le force à récupérer toutes les pages des résultats. En fait, je ne pense pas qu'il récupère quoi que ce soit jusqu'à ce que vous commenciez à itérer. Donc, ce serait pourquoi supprimer votre boucle for ferait une différence

Malheureusement, au-delà de cela, je ne suis pas sûr, car lorsque vous fouillez dans le code source, vous rencontrez assez rapidement les stubs GRPC et on ne sait pas si le problème est là.

Il y a cette question qui est similaire à la vôtre, où le demandeur a trouvé une fuite de mémoire impliquée dans l'instanciation datastore.Client(). Comment rechercher une fuite de mémoire lors de l'utilisation des bibliothèques Python Google Cloud Datastore?

Cela a finalement été lié à un problème dans GRPC où GRPC fuirait s'il ne se ferme pas https://github.com/grpc/grpc/issues/22123

Espérons que cela vous oriente dans la bonne direction