7

मुझे लगता है कि ndb लाइब्रेरी में मेमोरी लीक है लेकिन मुझे यह नहीं मिल रहा है।Google ndb लाइब्रेरी में मेमोरी लीक

क्या नीचे वर्णित समस्या से बचने का कोई तरीका है?
क्या आपके पास समस्या का पता लगाने के लिए परीक्षण का एक और सटीक विचार है?


कि कैसे मैं इस समस्या reproduced:

2 फाइलों के साथ एक minimalist Google अनुप्रयोग इंजन में बनाया।
app.yaml:

application: myapplicationid 
version: demo 
runtime: python27 
api_version: 1 
threadsafe: yes 


handlers: 
- url: /.* 
    script: main.APP 

libraries: 
- name: webapp2 
    version: latest 

main.py:

# -*- coding: utf-8 -*- 
"""Memory leak demo.""" 
from google.appengine.ext import ndb 
import webapp2 


class DummyModel(ndb.Model): 

    content = ndb.TextProperty() 


class CreatePage(webapp2.RequestHandler): 

    def get(self): 
     value = str(102**100000) 
     entities = (DummyModel(content=value) for _ in xrange(100)) 
     ndb.put_multi(entities) 


class MainPage(webapp2.RequestHandler): 

    def get(self): 
     """Use of `query().iter()` was suggested here: 
      https://code.google.com/p/googleappengine/issues/detail?id=9610 
     Same result can be reproduced without decorator and a "classic" 
      `query().fetch()`. 
     """ 
     for _ in range(10): 
      for entity in DummyModel.query().iter(): 
       pass # Do whatever you want 
     self.response.headers['Content-Type'] = 'text/plain' 
     self.response.write('Hello, World!') 


APP = webapp2.WSGIApplication([ 
    ('/', MainPage), 
    ('/create', CreatePage), 
]) 

मैं आवेदन अपलोड किया है, /create एक बार कहा जाता है।
उसके बाद, / पर प्रत्येक कॉल उदाहरण के द्वारा उपयोग की गई स्मृति को बढ़ाती है। जब तक यह Exceeded soft private memory limit of 128 MB with 143 MB after servicing 5 requests total त्रुटि के कारण बंद हो जाता है। enter image description here

नोट:: समस्या की तरह web.py

+2

शायद [ndb इन-संदर्भ कैश] (https://cloud.google.com/appengine/docs/python/ndb/cache), मुझे उम्मीद है। –

+0

मुझे अजगर के बारे में कुछ नहीं पता लेकिन आपका कोड पढ़ना मैं कहूंगा कि आपका मेमोरी खत्म हो रहा है क्योंकि आपका 'ndb.put_multi' एक ही लेनदेन में 100 इकाइयों को सम्मिलित करने का प्रयास करता है। शायद यही कारण है कि बहुत मेमोरी आवंटित की जा रही है। मुलायम निजी मेमोरी सीमा से अधिक संभवतः इसलिए है क्योंकि आपका अगला अनुरोध तब भी चल रहा है जब आपका अगला अनुरोध मेमोरी लोड में जोड़ने में आता है। यह तब नहीं होना चाहिए जब आप कॉल के बीच थोड़ी देर प्रतीक्षा करें (क्रमशः लेनदेन पूरा होने तक प्रतीक्षा करें)। प्रतिक्रिया समय में भारी वृद्धि होने पर भी ऐप इंजन को एक अतिरिक्त उदाहरण शुरू करना चाहिए। – konqi

+0

@DanielRoseman "संदर्भ में कैश केवल एक धागे की अवधि के लिए बनी रहती है।" यदि आप संदर्भ में कैश साफ़ करते हैं या कैशिंग अक्षम करने के लिए नीति सेट करते हैं, तो स्मृति उपयोग अधिक धीरे-धीरे बढ़ता है लेकिन रिसाव बनी रहती है। – greg

उत्तर

4

अधिक जांच के बाद, और एक Google इंजीनियर की मदद से, मुझे अपनी स्मृति खपत में दो स्पष्टीकरण मिले हैं।

प्रसंग और धागा

ndb.Context एक "धागा स्थानीय" वस्तु है और जब एक नया अनुरोध धागा में आते ही मंजूरी दे दी है। तो धागा अनुरोधों के बीच उस पर पकड़। कई धागे एक GAE उदाहरण में मौजूद हो सकता है और यह सैकड़ों अनुरोध से पहले एक धागा दूसरी बार प्रयोग किया जाता है और यह संदर्भ को मंजूरी दे दी है लग सकता है।
यह एक स्मृति रिसाव नहीं है, लेकिन स्मृति में संदर्भ एक छोटे जीएई उदाहरण में उपलब्ध स्मृति से अधिक हो सकता है।

वर्कअराउंड:
आप एक GAE उदाहरण में उपयोग में धागे की संख्या कॉन्फ़िगर नहीं कर सकता। तो प्रत्येक संदर्भ को सबसे छोटा संभव रखना सबसे अच्छा है। संदर्भ में कैश से बचें, और प्रत्येक अनुरोध के बाद इसे साफ़ करें।

घटना कतार

ऐसा लगता है कि NDB गारंटी नहीं है कि घटना कतार एक अनुरोध के बाद खाली कर दिया है। फिर यह एक स्मृति रिसाव नहीं है। लेकिन यह आपके धागे संदर्भ में Futures छोड़ देता है, और आप पहली समस्या पर वापस आ गए हैं।

वर्कअराउंड:
लपेटें सभी अपने कोड है कि @ndb.toplevel साथ NDB का उपयोग करें।() और gc.collect

+0

ग्रेग, क्या Google इंजीनियर आपको कोई संकेत देता है यदि यह इरादा व्यवहार या बग है? यह निश्चित रूप से मेरे लिए एक बग की तरह लगता है। –

+0

प्रत्येक अनुरोध के बाद आप संदर्भ को कैसे साफ़ करते हैं? – diogovk

+0

मैंने उपरोक्त सभी को किया है, और इस मुद्दे के बारे में Google समर्थन से भी संपर्क किया है ... और वे यह भी स्वीकार नहीं करते हैं कि यह मौजूद है। मुझे अभी भी एक रिसाव मिलती है जो इतनी चरम है कि एक ऐसी प्रक्रिया जो थोड़ी अधिक होती है जो एनडीबी प्रविष्टियों के माध्यम से फिर से होती है और परिणाम को बड़ी मात्रा में कतार में लाती है, कुछ मिनटों में 500 एम मेमोरी लीक करती है। कोई अन्य संभावित स्पष्टीकरण? – Sniggerfardimungus

3

, webapp2 से एक और ढांचे के साथ reproduced किया जा सकता है एक ज्ञात मुद्दा नहीं है स्मृति उपयोग ग्राफ के

उदाहरण (आप स्मृति विकास और दुर्घटनाओं देख सकते हैं) एनडीबी के साथ। आप it here के बारे में पढ़ सकते हैं और वहाँ एक काम around here है:

गैर नियतिवाद fetch_page के साथ मनाया eventloop.rpcs की यात्रा के क्रम है, जो datastore_rpc.MultiRpc.wait_any() और apiproxy_stub_map में भेजा जाता है के कारण है। __check_one इटरेटर से अंतिम आरपीसी का चयन करता है।

10 में PAGE_SIZE साथ ला रहा है और अधिक सही निर्धारित करने के लिए और अधिक परिणाम देखते हैं कि क्या संख्या = 10, सीमा = 11, बैकएंड मजबूर करने के लिए एक मानक तकनीक के साथ एक RPC करता है। यह रिटर्न 10 परिणाम, लेकिन जिस तरह से QueryIterator सुलझाया है में एक बग के कारण, एक RPC अंतिम प्रविष्टि लाने (प्राप्त कर्सर का उपयोग और = 1 गिनती) से जोड़ा जाता है। एनडीबी फिर इस आरपीसी को संसाधित किए बिना इकाइयों के बैच को वापस कर देता है। मुझे विश्वास है कि इस आरपीसी जब तक यादृच्छिक पर चयनित मूल्यांकन नहीं दिया जाएगा (अगर MultiRpc एक आवश्यक RPC पहले यह खपत), क्योंकि यह ग्राहक कोड ब्लॉक नहीं करता।

वर्कअराउंड: iter() का उपयोग करें। इस फ़ंक्शन में यह समस्या नहीं है (गणना और सीमा वही होगी)। Iter() उपरोक्त के कारण प्राप्त पृष्ठ के साथ जुड़े प्रदर्शन और स्मृति समस्याओं के लिए एक समाधान के रूप में उपयोग किया जा सकता है।

+0

मैंने इन धागे को पढ़ा है, लेकिन 'iter() 'का उपयोग स्मृति रिसाव को रोकता नहीं है। – greg

+0

आपको अपने निष्कर्ष वहां धागे पर पोस्ट करना चाहिए ताकि अभियंता इसे देख सकें। – Ryan

+0

ग्रेग, पेरिस में आपके साथ अच्छा चैट कर रहा है। मैं इसके बजाए "iter()" का उपयोग करके कोड को संपादित करने का सुझाव दूंगा, और मेमोरी रिसाव का साक्ष्य प्रदान करूंगा। – Riccardo

1

एक संभावित समाधान, context.clear_cache उपयोग करने के लिए है() प्राप्त पद्धति पर।

def get(self): 

    for _ in range(10): 
     for entity in DummyModel.query().iter(): 
      pass # Do whatever you want 
    self.response.headers['Content-Type'] = 'text/plain' 
    self.response.write('Hello, World!') 
    context = ndb.get_context() 
    context.clear_cache() 
    gc.collect() 
संबंधित मुद्दे