Google App Engine / Datastore / Flask / Python uygulamasında bellek sızıntısı

Sep 15 2020

Tüm App Engine örneklerimin bellek kullanımının sınıra ulaşana ve dolayısıyla kapatılana kadar artmaya devam ettiği basit bir haber toplayıcı site oluşturdum.

Minimum yeniden üretilebilir sürüme ulaşmak için uygulamamdaki her şeyi kaldırmaya başladım. Şu an sahip olduğum şey bu:


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
    

İstatistikler tipik bir testere dişi kalıbı gösterir: örnek başlatıldığında, 190-210 Mb'a gider, ardından bazı isteklerde, ancak TÜM taleplerde DEĞİL, bellek kullanımı 20-30 Mb artar. (Bu arada, bununla ilgili bilgi olduğundan emin olamasam da, yaklaşık olarak sorgu sonuçlarının tahmini boyutuna karşılık gelir.) Bu, kapatıldığında 512 Mb'yi aşana kadar devam eder. Genellikle "/" için 50. - 100. istek civarında gerçekleşir. Bu arada başka hiçbir talepte bulunulmaz.

Şimdi, "for" döngüsünü ortadan kaldırırsam ve yalnızca sorgu kalırsa, sorun ortadan kalkar, bellek kullanımı sabit 190 Mb'de kalır, 100'den fazla istekten sonra bile artış olmaz.

gc.collect () sonunda yardımcı olmuyor. Fonksiyonun başında ve sonunda tracemalloc istatistiklerindeki farklılığa bakmayı da denedim, yararlı bir şey bulamadım.

Herhangi biri benzer bir deneyim yaşadı mı lütfen? Burada neyin yanlış gidebileceğine dair bir fikriniz var mı? Hangi ek testleri / incelemeleri önerebilirsiniz? Bu muhtemelen benim kontrolümde olmayan bir Google App Engine / Datastore sorunu mu?

Teşekkür ederim.

Yanıtlar

1 yedpodtrzitko Sep 17 2020 at 02:48

@Alex diğer yanıtta oldukça iyi bir araştırma yaptı, bu yüzden şu öneriyi takip edeceğim: NDB Kitaplığını kullanmayı deneyin . Bu kitaplıktaki tüm aramalar, kapattıktan sonra temizlemeyi garanti eden bir bağlam yöneticisine sarılmalıdır. Bu, sorununuzu çözmenize yardımcı olabilir:

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

Şimdi, "for" döngüsünü ortadan kaldırırsam ve yalnızca sorgu kalırsa, sorun ortadan kalkar, bellek kullanımı sabit 190 Mb'de kalır, 100'den fazla istekten sonra bile artış olmaz.

query.fetch() sonuçların gerçek bir dizisi değil, bir yineleyici döndürür

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

Kaynak koduna bakıldığında, bu yineleyicide sorgunun sonraki sayfalarını getirmek için kod var gibi görünüyor. Yani for-loop, onu sonuçların tüm sayfalarını getirmeye zorluyor. Aslında, siz yinelemeye başlayana kadar aslında bir şey getireceğini sanmıyorum. Bu yüzden for-loop'unuzu kaldırmanız bir fark yaratır

Maalesef bunun ötesinde emin değilim, çünkü kaynak kodunu kazarken oldukça hızlı bir şekilde GRPC koçanlarına giriyorsunuz ve sorunun orada olup olmadığı belli değil.

Soruyu soran kişi, örneklemeyle ilgili bir bellek sızıntısı bulduğunda sizinkine benzer bir soru var datastore.Client(). Google Cloud Datastore Python kitaplıklarını kullanırken bellek sızıntısını nasıl araştırmalıyım?

Bu sonuçta GRPC'de kapanmazsa GRPC'nin sızacağı bir sorunla ilişkilendirildi. https://github.com/grpc/grpc/issues/22123

Umarım bu sizi doğru yönü gösterir