2010-02-25 8 views
15

मुझे डेटाबेस और मेमकैच के लिए डीजेगो चलाने वाली पाइथन स्क्रिप्ट मिली है, लेकिन यह विशेष रूप से स्टैंडअलोन डिमन के रूप में चल रहा है (यानी वेबसर्वर अनुरोधों का जवाब नहीं दे रहा है)। डिमन status=STATUS_NEW के साथ ऑब्जेक्ट्स के लिए एक Django मॉडल अनुरोध की जांच करता है, फिर उन्हें STATUS_WORKING चिह्नित करता है और उन्हें कतार में रखता है।डेटाबेस के पायथन/डीजेगो मतदान में मेमोरी लीक

कई प्रक्रियाएं (मल्टीप्रोसेस पैकेज का उपयोग करके बनाई गई) चीजों को कतार से बाहर खींचती हैं और pr.id के साथ अनुरोध पर काम करती हैं जो कि कतार में पारित की गई थी। मेरा मानना ​​है कि स्मृति रिसाव शायद निम्न कोड में है (लेकिन यह कतार के दूसरी तरफ 'वर्कर' कोड में हो सकता है, हालांकि यह असंभव है क्योंकि स्मृति आवश्यकता तब भी बढ़ रही है जब कोई आवश्यकता नहीं आती है - यानी जब कर्मचारी सभी Queue.get() पर अवरुद्ध कर रहे हैं)।

from requisitions.models import Requisition # our Django model 
from multiprocessing import Queue 

while True: 
    # Wait for "N"ew requisitions, then pop them into the queue. 
    for pr in Requisition.objects.all().filter(status=Requisition.STATUS_NEW): 
     pr.set_status(pr.STATUS_WORKING) 
     pr.save() 
     queue.put(pr.id) 

    time.sleep(settings.DAEMON_POLL_WAIT) 

जहां settings.DAEMON_POLL_WAIT=0.01

ऐसा लगता है कि अगर मैं इसे कुछ समय के लिए चला रहा हूं (यानी दो दिन) पाइथन प्रक्रिया अनंत आकार तक बढ़ेगी और अंत में सिस्टम स्मृति से बाहर हो जाएगा।

यहां क्या हो रहा है (या मैं कैसे पता लगा सकता हूं), और सबसे महत्वपूर्ण बात यह है कि आप एक डिमन कैसे चला सकते हैं?

मेरी पहली सोचा समारोह के गतिशील बदलने के लिए, विशेष रूप से

from django.core.cache import cache 

while True: 
    time.sleep(settings.DAEMON_POLL_WAIT) 
    if cache.get('new_requisitions'): 
     # Possible race condition 
     cache.clear() 
     process_new_requisitions(queue) 

def process_new_requisitions(queue): 
    for pr in Requisition.objects.all().filter(status=Requisition.STATUS_NEW): 
     pr.set_status(pr.STATUS_WORKING) 
     pr.save() 
     queue.put(pr.id) 

प्रक्रिया है कि status=STATUS_NEW साथ Requisitions बनाने है एक cache.set('new_requisitions', 1) कर सकते हैं एक django.core.cache cache में नई मांग के लिए चेक वस्तुओं डाल, यानी कर रहा है (या वैकल्पिक रूप से हम एक सिग्नल या Requisition.save() ईवेंट पकड़ सकते हैं जहां एक नया अनुरोध बनाया जा रहा है और फिर वहां से कैश में ध्वज सेट करें)।

हालांकि मुझे यकीन नहीं है कि मैंने जिस समाधान का प्रस्ताव दिया है, वह स्मृति समस्याओं को संबोधित करता है (जो शायद कचरा संग्रह से संबंधित हैं - इसलिए process_new_requisitions के माध्यम से स्कॉइंग समस्या को हल कर सकता है)।

मैं किसी भी विचार और प्रतिक्रिया के लिए आभारी हूं।

+5

बस एक सोचा ... आप डीबग = settings.py में सच के साथ चल रहे हैं? डीबग मोड सभी प्रश्नों को बचाता है, जो निश्चित रूप से मेमोरी लीक की तरह दिख सकते हैं :) –

+0

हे हे। मैं वास्तव में था! मैं उसके बारे में भूल गया। मैंने इसे बंद कर दिया है (और कैश समाधान में स्थानांतरित हो गया है) और स्मृति रिसाव कम हो गया है। –

उत्तर

35

आपको नियमित रूप से प्रश्नों की एक सूची रीसेट करने की आवश्यकता है जो Django डीबगिंग उद्देश्यों के लिए रखता है। आम तौर पर यह हर अनुरोध के बाद मंजूरी दे दी है, लेकिन जब से आपके आवेदन के आधार का अनुरोध नहीं किया गया है, तो आप इस मैन्युअल रूप से कार्य करना होगा:

from django import db 

db.reset_queries() 

यह भी देखें:

  • "Debugging Django memory leak with TrackRefs and Guppy" मिक्को Ohtamaa द्वारा:

    Django डीबगिंग उद्देश्यों के लिए सभी प्रश्नों का ट्रैक रखता है (कनेक्शन .queries)। HTTP सूची के अंत में यह सूची रीसेट की गई है। लेकिन स्टैंडअलोन मोड में, अनुरोध नहीं हैं।तो अगर आप प्रत्येक काम कर चक्र

  • "Why is Django leaking memory?" in Django FAQ के बाद करने के लिए मैन्युअल रूप से रीसेट प्रश्नों के सूची की जरूरत है - यह False करने के लिए दोनों बात करती है DEBUG करने के बारे में है, जो हमेशा महत्वपूर्ण है, और क्वेरी की सूची समाशोधन db.reset_queries() उपयोग के बारे में, आपके जैसे अनुप्रयोगों में महत्वपूर्ण है।

+0

वे ठोस संदर्भ हैं - धन्यवाद। –

+0

ऐसा लगता है कि लिंक इस पर स्थानांतरित हो गया है: http://opensourcehacker.com/2008/03/07/debugging-django-memory-leak-with-trackrefs-and-guppy/ – joctee

+0

@joctee धन्यवाद, मैंने पोस्ट अपडेट किया है। –

5

क्या डिमन प्रक्रिया के लिए सेटिंग्स.py फ़ाइल DEBUG = True है? यदि ऐसा है, तो Django स्मृति में सभी एसक्यूएल का रिकॉर्ड रखता है जो अब तक चला है, जो स्मृति रिसाव का कारण बन सकता है।

+0

महान सुझाव! यह सक्षम था; मैंने इसे अक्षम कर दिया है। मैंने कैश-फ्लैग चेक पर भी स्विच किया है, इसलिए यह लगातार डेटाबेस को मतदान नहीं कर रहा है। देखेंगे कि क्या ये दो चीजें मदद करते हैं। धन्यवाद! –

1
db.reset_queries से

अलावा() और डीबग = झूठी चाल, यहाँ एक और तरीका है: बस एक और प्रक्रिया है कि Django क्वेरी का निष्पादन और कतार फ़ीड अंडे। यह प्रक्रिया अपने स्वयं के स्मृति संदर्भ में काम करेगी, और अपना कार्य करने के बाद यह आपकी याददाश्त को वापस छोड़ देगी।

मेरा मानना ​​है कि कभी-कभी (यदि हमेशा नहीं होता) तो यह लंबे समय तक चलने वाली प्रक्रिया के साथ स्मृति समस्याओं को नियंत्रित करने के लिए अनिवार्य है जो भारी django लेनदेन करता है।

2

मेरे पास बहुत सी डेटा क्रंचिंग थी, इसलिए, इस मुद्दे का मेरा समाधान मल्टीप्रोसेसिंग का उपयोग कर रहा था, और पूल का उपयोग करके जो भी मेमोरी ब्लोट हो रहा था।

इसे सरल रखने के लिए, मैंने कुछ चीजों को अचार बनाने में सक्षम होने के बजाय कुछ "वैश्विक" (शीर्ष स्तर, जो भी शब्द पाइथन में है) परिभाषित किया है।

यहाँ यह सार रूप में है:

import multiprocessing as mp 

WORKERS = 16 # I had 7 cores, allocated 16 because processing was I/O bound 

# this is a global function 
def worker(params): 
    # do stuff 
    return something_for_the_callback_to_analyze 

# this is a global function 
def worker_callback(worker_return_value): 
    # report stuff, or pass 

# My multiprocess_launch was inside of a class 
def multiprocess_launcher(params): 
    # somehow define a collection 
    while True: 
    if len(collection) == 0: 
     break 
    # Take a slice 
    pool_sub_batch = [] 
    for _ in range(WORKERS): 
     if collection: # as long as there's still something in the collection 
     pool_sub_batch.append(collection.pop()) 
    # Start a pool, limited to the slice 
    pool_size = WORKERS 
    if len(pool_sub_batch) < WORKERS: 
     pool_size = len(pool_sub_batch) 
    pool = mp.Pool(processes=pool_size) 
    for sub_batch in pool_sub_batch: 
     pool.apply_async(worker, args = (sub_batch), callback = worker_callback) 
    pool.close() 
    pool.join() 
    # Loop, more slices 
संबंधित मुद्दे