2009-12-07 3 views
6

मेरे पास एक पिलोन-आधारित वेब एप्लिकेशन है जो Sqlalchemy (v0.5) के माध्यम से पोस्टग्रेज़ डेटाबेस से कनेक्ट होता है। सुरक्षा के लिए, सरल वेब ऐप्स के सामान्य पैटर्न का पालन करने के बजाय (जैसा कि लगभग सभी ट्यूटोरियल में देखा गया है), मैं एक सामान्य पोस्टग्रेस उपयोगकर्ता (उदाहरण के लिए "वेबपैप") का उपयोग नहीं कर रहा हूं, लेकिन मुझे यह आवश्यक है कि उपयोगकर्ता अपने स्वयं के पोस्टग्रेस उपयोगकर्ता आईडी और पासवर्ड दर्ज करें , और कनेक्शन स्थापित करने के लिए इसका उपयोग कर रहा हूँ। इसका मतलब है कि हमें पोस्टग्रेस सुरक्षा का पूरा लाभ मिलता है।sqlalchemy के साथ प्रति-अनुरोध आधार पर डेटाबेस इंजन को गतिशील रूप से बाध्य करने के लिए

जटिल चीजों को अभी भी आगे बढ़ाने के लिए, कनेक्ट करने के लिए दो अलग-अलग डेटाबेस हैं। हालांकि वे वर्तमान में एक ही पोस्टग्रेस क्लस्टर में हैं, फिर भी उन्हें बाद की तारीख में अलग मेजबानों में स्थानांतरित करने में सक्षम होना चाहिए।

हम sqlalchemy के declarative पैकेज का उपयोग कर रहे हैं, हालांकि मैं नहीं देख सकता कि इस मामले पर इसका कोई असर नहीं है।

स्क्लेल्चेमी के अधिकांश उदाहरण एक सामान्य डेटाबेस उपयोगकर्ता और पासवर्ड के साथ, एप्लिकेशन स्टार्टअप पर, मेटाडाटा को एक बार सेट अप करने जैसे छोटे दृष्टिकोण दिखाते हैं, जिसका उपयोग वेब एप्लिकेशन के माध्यम से किया जाता है। यह आमतौर पर मेटाडेटा.बिंड = create_engine() के साथ किया जाता है, कभी-कभी डेटाबेस मॉडल फ़ाइलों में मॉड्यूल-स्तर पर भी।

मेरा सवाल यह है कि, उपयोगकर्ता द्वारा लॉग इन किए जाने तक कनेक्शन स्थापित करने में हम कैसे रोक सकते हैं, और फिर (निश्चित रूप से) उन कनेक्शनों का दोबारा उपयोग करें, या प्रत्येक बाद के अनुरोध के लिए, समान प्रमाण-पत्रों का उपयोग करके उन्हें पुनः स्थापित करें।

हमारे पास यह काम है - हमें लगता है - लेकिन मैं केवल इसकी सुरक्षा की निश्चित नहीं हूं, मुझे यह भी लगता है कि यह स्थिति के लिए अविश्वसनीय रूप से भारी वजन दिखता है।

BaseController हम उपयोगकर्ता आईडी और पासवर्ड वेब सत्र से पुनः प्राप्त, SQLAlchemy create_engine (फोन की __call__ विधि के अंदर) एक बार प्रत्येक डेटाबेस के लिए है, तो एक नियमित जो Session.bind_mapper (कॉल) प्रत्येक तालिका के लिए कॉल को बार-बार, एक बार है कि उन कनेक्शनों में से प्रत्येक पर संदर्भित किया जा सकता है, भले ही कोई भी अनुरोध आमतौर पर केवल एक या दो तालिकाओं का संदर्भ देता हो। यह इस तरह दिखता है:

# in lib/base.py on the BaseController class 
def __call__(self, environ, start_response): 

    # note: web session contains {'username': XXX, 'password': YYY} 
    url1 = 'postgres://%(username)s:%(password)[email protected]/finance' % session 
    url2 = 'postgres://%(username)s:%(password)[email protected]/staff' % session 

    finance = create_engine(url1) 
    staff = create_engine(url2) 
    db_configure(staff, finance) # see below 
    ... etc 

# in another file 

Session = scoped_session(sessionmaker()) 

def db_configure(staff, finance): 
    s = Session() 

    from db.finance import Employee, Customer, Invoice 
    for c in [ 
     Employee, 
     Customer, 
     Invoice, 
     ]: 
     s.bind_mapper(c, finance) 

    from db.staff import Project, Hour 
    for c in [ 
     Project, 
     Hour, 
     ]: 
     s.bind_mapper(c, staff) 

    s.close() # prevents leaking connections between sessions? 

तो create_engine() कॉल हर अनुरोध पर पाए जाते हैं ... मैं देख सकते हैं कि जरूरत किया जा रहा है, और कनेक्शन पूल शायद उन्हें समझदारी से कैश और करता है बातें।

लेकिन Session.bind_mapper() प्रत्येक तालिका के लिए एक बार बुला, हर अनुरोध पर? ऐसा लगता है कि एक बेहतर तरीका होना चाहिए।

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

उत्तर

3

उपयोगकर्ता-विशिष्ट कनेक्शन के लिए बाध्यकारी वैश्विक ऑब्जेक्ट्स (मैपर, मेटाडाटा) अच्छा तरीका नहीं है। साथ ही स्कोप्ड सत्र का उपयोग करना। मैं प्रत्येक अनुरोध के लिए नया सत्र बनाने और इसे उपयोगकर्ता-विशिष्ट कनेक्शन का उपयोग करने के लिए कॉन्फ़िगर करने का सुझाव देता हूं। निम्न नमूना मानता है कि आप प्रत्येक डेटाबेस के लिए अलग मेटाडाटा वस्तुओं का उपयोग करें:

अलग metadatas, दो डेटाबेस से प्रत्येक के लिए एक
binds = {} 

finance_engine = create_engine(url1) 
binds.update(dict.fromkeys(finance_metadata.sorted_tables, finance_engine)) 
# The following line is required when mappings to joint tables are used (e.g. 
# in joint table inheritance) due to bug (or misfeature) in SQLAlchemy 0.5.4. 
# This issue might be fixed in newer versions. 
binds.update(dict.fromkeys([Employee, Customer, Invoice], finance_engine)) 

staff_engine = create_engine(url2) 
binds.update(dict.fromkeys(staff_metadata.sorted_tables, staff_engine)) 
# See comment above. 
binds.update(dict.fromkeys([Project, Hour], staff_engine)) 

session = sessionmaker(binds=binds)() 
+0

@Denis, हमारे पास है। यह देखते हुए, दोनों बांधते हैं।प्रत्येक डेटाबेस के लिए आवश्यक अपडेट() कॉल, जैसा कि आपके उदाहरण में है, या क्या हम केवल उन लोगों के साथ प्राप्त कर सकते हैं जो xxx_metadata.sorted_tables का उपयोग करते हैं? मुझे लगता है कि मैं ऐसा कुछ ढूंढने की उम्मीद कर रहा हूं जो मेटाडेटा से बांधता है, लेकिन एक प्रति-अनुरोध के आधार पर ... मुझे इसका उल्लेख करना चाहिए था। –

+0

मेटाडाटा वैश्विक है। एक वैश्विक बाध्यकारी हमेशा इतना अच्छा नहीं होता है, लेकिन यह इंजन की स्थिरता के दौरान समस्याओं का कारण नहीं बनता है। परिवर्तनीय वैश्विक इंजन का उपयोग करने के लिए 'threading.local()' के समान बदसूरत हैक की आवश्यकता होगी जो बहुत खराब और त्रुटि प्रवण हैं। अनुरोध से बचने वाले सत्र का उपयोग संभावित रूप से एक उपयोगकर्ता से दूसरे में कुछ वस्तुओं को रिसाव कर सकता है। जबकि आप इसे बचाने के लिए कुछ कोड लिख सकते हैं, ऐसे तरीके से जाना बेहतर है जिनके पास डिज़ाइन द्वारा ऐसा छेद नहीं है। –

+0

मेरा मतलब यह नहीं है कि मेटाडाटा.बिंद को निश्चित रूप से सेट करें, क्योंकि आप कहते हैं कि यह वैश्विक है। मेरा मतलब यह है कि मेटाडाटा ऑब्जेक्ट पहले से ही सभी संबंधित तालिकाओं के बारे में जानता है। हमें केवल [स्यूडो-कोड) "सत्र निर्माता (बाइंड्स = {finance_मैटडाटा: फाइनेंस_एन्गिन, स्टाफ_मैटडाटा: स्टाफ_एंडिंग}) की बजाय, [कर्मचारी, ग्राहक, चालान] आदि को स्पष्ट रूप से सूचीबद्ध क्यों करना है)"। मैंने सोचा होगा कि प्रत्येक तालिका को व्यक्तिगत रूप से निर्दिष्ट करने के बजाय, उस स्तर के संकेत का उपयोग करने के लिए कुछ समर्थन होगा। –

-1

मैं कनेक्शन पूलिंग देखता हूं और देखता हूं कि क्या आपको प्रति उपयोगकर्ता एक पूल रखने का कोई तरीका नहीं मिल रहा है। जब उपयोगकर्ता का सत्र समाप्त हो गया है तो dispose() पूल हो सकता है

संबंधित मुद्दे