क्या यह संभव होगा कि आपस में स्विच करने के लिए रेलों में कई डेटाबेस कनेक्शन पूल हों?


12

थोड़ी पृष्ठभूमि

मैं वर्षों से मल्टी-टेनेंसी ऐप चलाने के लिए अपार्टमेंट रत्न का उपयोग कर रहा हूं । अब हाल ही में डेटाबेस को अलग-अलग होस्ट में स्केल करने की आवश्यकता आ गई है, db सर्वर केवल अधिक नहीं रख सकता है (दोनों पढ़ता है और लिखता है बहुत अधिक हो रहा है) - और हाँ, मैंने हार्डवेयर को अधिकतम तक बढ़ाया (समर्पित) हार्डवेयर, 64 कोर, छापे 10, 384 जीबी रैम आदि में 12 एनवीएम-ई ड्राइव)।

मैं इसे प्रति-किरायेदार (1 किरायेदार = 1 डेटाबेस कनेक्शन कॉन्फिगरेशन / पूल) करने पर विचार कर रहा था, क्योंकि यह एक "सरल" और कुशल तरीका होगा, जो कि number-of-tenantsएप्लिकेशन कोड परिवर्तनों के भार को किए बिना -times को अधिक क्षमता तक पहुंचाएगा।

अब, मैं ५.२ atm की रेल चला रहा हूँ, जल्द ही ५.२ पर अपग्रेड हो रहा है। मैं देख सकता हूं कि रेल 6 प्रति मॉडल कनेक्शन परिभाषाओं के लिए समर्थन जोड़ता है, हालांकि यह वास्तव में मुझे क्या चाहिए, जैसा कि मेरे 20 किरायेदारों में से प्रत्येक के लिए पूरी तरह से प्रतिबिंबित डेटाबेस स्कीमा नहीं है। आमतौर पर मैं "डेटाबेस" प्रति अनुरोध (मिडलवेयर में) या प्रति बैकग्राउंड जॉब (साइडकीक मिडलवेयर) पर स्विच करता हूं, हालांकि यह वर्तमान में अपार्टमेंट मणि के रूप में तुच्छ और संभाला है, क्योंकि यह सिर्फ search_pathपोस्टग्रैक्स्ल में सेट होता है और वास्तव में वास्तविक कनेक्शन को नहीं बदलता है। प्रति-किरायेदार होस्टिंग रणनीति पर स्विच करते समय मुझे अनुरोध के अनुसार पूरे कनेक्शन को स्विच करना होगा।

प्रशन:

  1. मैं समझता हूं कि मैं ActiveRecord::Base.establish_connection(config)प्रति अनुरोध / पृष्ठभूमि नौकरी कर सकता हूं - हालांकि, जैसा कि मैं भी समझता हूं, कि एक पूरी तरह से नया डेटाबेस कनेक्शन हैंडशेक को ट्रिगर किया जा सकता है और पटरियों में स्पॉन करने के लिए एक नया डीबी पूल - सही है? मुझे लगता है कि मेरे आवेदन के लिए हर एक अनुरोध पर उस तरह का ओवरहेड बनाने के लिए एक प्रदर्शन आत्महत्या होगी।
  2. इसलिए मैं सोच रहा हूं कि क्या कोई भी पहले से कई (कुल 20) डेटाबेस कनेक्शन / पूल की स्थापना की तरह रेल के साथ विकल्प देख सकता है (जैसे आवेदन के बूट पर), और फिर प्रति अनुरोध उन पूल के बीच स्विच करें? ताकि वह db कनेक्शन पहले से बना रहे हैं और उपयोग करने के लिए तैयार हैं।
  3. क्या यह सब सिर्फ एक घटिया विचार है, और क्या मुझे इसके बजाय एक अलग दृष्टिकोण की तलाश करनी चाहिए? जैसे 1 ऐप इंस्टेंट = एक विशिष्ट किरायेदार के लिए एक विशिष्ट कनेक्शन। या कुछ और।

2
guide.rubyonrails.org/active_record_multiple_dat डेटाबेस.html मुझे लगता है कि यह आपकी मदद कर सकता है
एलेक्स

1
आपको इस PR में रेल्स 'GitHub रिपॉजिटरी' में रुचि हो सकती है, जिसने हाल ही में वर्तमान रेल लाइन masterशाखा में आपके लिए आवश्यक सुविधा जोड़ी है । क्या रेलिंग रनिंग एगडे एक विकल्प या बैक-प्रेटिंग होगी जो आपके वर्तमान रेल संस्करण के लिए सुविधा होगी?
स्पाइकरमैन

@spickermann का ActiveRecord::Base.connected_to(shard: :shard_one) do ... endमतलब है कि पूल (पुनः) का उपयोग किया जाएगा, बजाय हर बार एक नया कनेक्शन बनाने के?
बेन

जवाबों:


4

जैसा कि मैं समझता हूं, मल्टी-टेनेंसी ऐप के लिए 4 पैटर्न हैं:

1. समर्पित मॉडल / एकाधिक उत्पादन वातावरण

प्रत्येक उदाहरण या डेटाबेस उदाहरण पूरी तरह से अलग किरायेदार आवेदन की मेजबानी करते हैं और किरायेदारों के बीच कुछ भी साझा नहीं किया जाता है।

यह 1 किरायेदार के लिए 1 इंस्टेंस ऐप और 1 डेटाबेस है। विकास आसान होगा जैसे कि आप केवल 1 किरायेदार की सेवा करते हैं। लेकिन अगर आपके पास 100 किरायेदार हैं, तो आप देवों के लिए दुःस्वप्न होंगे।

2. किरायेदारों का शारीरिक अलगाव

सभी किरायेदार के लिए 1 उदाहरण ऐप लेकिन 1 किरायेदार के लिए 1 डेटाबेस। यह वही है जिसे आप खोज रहे हैं। आप ActiveRecord::Base.establish_connection(config)रत्नों का उपयोग या उपयोग कर सकते हैं , या अन्य सुझावों के अनुसार रेल 6 को अपडेट कर सकते हैं । नीचे (2) के लिए उत्तर देखें।

3. पृथक स्कीमा मॉडल / तार्किक पृथक्करण

पृथक स्कीमा में, किरायेदार टेबल या डेटाबेस घटक एक तार्किक स्कीमा या नाम-स्थान के तहत समूह होते हैं और अन्य किरायेदार स्कीमा से अलग होते हैं, हालांकि स्कीमा को एक ही डेटाबेस उदाहरण में होस्ट किया जाता है।

सभी किरायेदार के लिए 1 इंस्टेंस ऐप और 1 डेटाबेस, जैसे आप अपार्टमेंट रत्न के साथ करते हैं।

4. आंशिक रूप से पृथक घटक

इस मॉडल में, सामान्य कार्यात्मकता वाले घटकों को किरायेदारों के बीच साझा किया जाता है जबकि अद्वितीय या असंबंधित कार्यों वाले घटकों को अलग किया जाता है। डेटा लेयर में, सामान्य डेटा जैसे कि डेटा, जो किरायेदारों की पहचान करते हैं, उन्हें समूहीकृत या एकल तालिका में रखा जाता है, जबकि किरायेदार विशिष्ट डेटा को टेबल या इंस्टेंस लेयर पर अलग किया जाता है।


(1) के रूप में, ActiveRecord::Base.establish_connection(config)यदि आप इसे सही ढंग से उपयोग करते हैं, तो अनुरोध के अनुसार db के लिए हैंडशेकिंग नहीं। आप यहां देख सकते हैं और यहां सभी टिप्पणी पढ़ सकते हैं

(2) के रूप में, यदि आप उपयोग नहीं करना चाहते हैं establish_connection, तो आप मणि मल्टीवर्स का उपयोग कर सकते हैं (यह रेल के लिए काम करता है 4.2), या अन्य रत्नों। या, अन्य सुझावों के अनुसार, आप रेल 6 पर अपडेट कर सकते हैं।

संपादित करें: मल्टीवर्स मणि का उपयोग कर रहा है establish_connection। यह आधार को जोड़ेगा database.yml, और एक बेस क्लास बनाएगा ताकि प्रत्येक उपवर्ग समान कनेक्शन / पूल साझा करे। मूल रूप से, यह उपयोग करने के हमारे प्रयास को कम करता है establish_connection

(3) के रूप में, जवाब:

यदि आपके पास इतने किराएदार नहीं हैं, और आपका आवेदन बहुत जटिल है, तो मेरा सुझाव है कि आप समर्पित मॉडल पैटर्न का उपयोग करें। तो आप 1 ऐप इंस्टेंट = एक विशिष्ट किरायेदार के लिए एक विशिष्ट कनेक्शन के लिए जाते हैं। आपको कई डेटाबेस कनेक्शन जोड़कर अपने ऐप्स को अधिक जटिल बनाने की आवश्यकता नहीं है।

लेकिन अगर आपके पास कई किरायेदार हैं, तो मेरा सुझाव है कि आप किरायेदारों के भौतिक अलगाव का उपयोग करें या आंशिक रूप से पृथक घटक आपके व्यवसाय की प्रक्रिया पर निर्भर करता है।

किसी भी तरह से, आपको नए आर्किटेक्चर के अनुपालन के लिए अपने एप्लिकेशन को अपडेट / फिर से लिखना होगा।


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

मेरे पास 1 और 2 के संबंध में कुछ प्रश्न हैं। 1: मुझे यकीन नहीं है कि मैं आपके संदर्भों को समझता हूं। क्या आपका कहना है, कि मैं db हैंडशेक / db पोल को फिर से करने के बिना .establish_connection (config) कॉल कर सकता हूं? उस मामले में, मुझे यकीन नहीं है कि दोनों लिंक कैसे बताते हैं? 2: मल्टीवर्स के लिए, क्या ऐसा नहीं है कि एक प्रति-मॉडल डेटाबेस पूरे ऐप के लिए पूरे db स्विच के बजाय स्विच कर रहा है? मुझे लगता है कि उनका प्रलेखन बहुत अस्पष्ट है
नील्स क्रिस्टियन

मुझे लगता है कि मुझे गलतफहमी है। क्या आपको इन वाक्यों को विस्तार से बताने का मन है? मैं समझता हूँ कि मैं प्रति अनुरोध / पृष्ठभूमि काम एक ActiveRecord :: Base.establish_connection (config) कर सकता है कि - हालांकि, के रूप में मैं यह भी समझता, रेल में अंडे करने के लिए कि चलाता पूरी तरह से नया डेटाबेस कनेक्शन हाथ मिलाना किए जाने के लिए और एक नया डाटाबेस पूल यह सुझाव है कि एक अनुरोध एक DB पूल बनाएँ?
केएसडी पुत्र

मेरा मतलब है: (1) मैं ActiveRecord :: बेस.establish_connection (config) को कॉल करने के लिए प्रदर्शन / नेटवर्क ओवरहेड के बारे में चिंतित हूं, बस अलग-अलग डेटाबेस / देशों के बीच स्विच करने के लिए
नील्स क्रिस्टियन

आपको ओवरहेड के बारे में चिंता करने की ज़रूरत नहीं है। अब, यदि आप सिंगल DB का उपयोग करते हैं, तो आपके पास एक कनेक्शन पूल है (आप ऊपर (1) के उत्तर में कनेक्शन के बारे में लिंक की जांच कर सकते हैं)। यदि आप establish_connectionइस तरह के मॉडल का उपयोग करते हैं : class SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); endऔर कहते हैं कि आपके पास 5 मॉडल हैं, तो आप DB_SECOND_TATANT के लिए 5 कनेक्शन पूल बनाते हैं। और प्रत्येक पूल को समान रूप से व्यवहार किया जाता है। इसलिए, आप प्रति अनुरोध, लेकिन प्रति पूल नहीं बनाते हैं establish_connection
केएसडी पुत्रा

3

जो मैं समझता हूं, (2) रेल कनेक्शन 6 में मैनुअल कनेक्शन स्विचिंग के साथ संभव होना चाहिए ।


धन्यवाद हालांकि यह मेरे उपयोग के मामले से बहुत दूर लगता है। यह हर जगह इस प्रक्रिया का उपयोग करने के लिए पूरे ऐप को फिर से लिखना होगा।
नील्स क्रिस्टियन

3

कुछ ही दिनों पहले गिटहब पर रूबी की रेल्स masterशाखा पर क्षैतिज शार्डिंग को जोड़ा गया था । वर्तमान में, यह सुविधा आधिकारिक रूप से जारी नहीं की गई है, लेकिन आपके एप्लिकेशन के रेल संस्करण के आधार पर आप इसे masterअपने साथ जोड़कर रेल का उपयोग करने पर विचार कर सकते हैं Gemfile:

gem "rails", github: "rails/rails", branch: "master"

इस नई सुविधा के साथ, आप रेल के डेटाबेस कनेक्शन पूल का लाभ उठा सकते हैं और शर्तों के आधार पर डेटाबेस को स्विच कर सकते हैं।

मैंने इस नई सुविधा का उपयोग नहीं किया है, लेकिन यह बहुत सीधा-सीधा लगता है:

# in your config/database.yml
production:
  primary:
    database: my_database
    # other config: user, password, etc
  primary_tenant_1:
    database: tenant_1_database
    # other config: user, password, etc

# in your controller for example when updating a tenant
ActiveRecord::Base.connected_to(shard: "primary_tenant_#{tenant.database_shard_number}") do
  tenant.save
end

आपने किरायेदार की संख्या कैसे निर्धारित की या आपके आवेदन में प्राधिकरण कैसे किया जाता है, इसके बारे में आपने अधिक विवरण नहीं जोड़ा। लेकिन मैं application_controllerएक में जितनी जल्दी हो सके किरायेदार संख्या निर्धारित करने की कोशिश करूंगा around_action। कुछ इस तरह से एक प्रारंभिक बिंदु हो सकता है:

around_filter :determine_database_connection

private

def determine_database_connection
  # assuming you have a method to determine the current_tenant and that tenant
  # has a method that returns the number of the shard to use or even the 
  # full shard identifier
  shard = current_tenant.database_shard # returns for example `:primary_tenant_1` 

  ActiveRecord::Base.connected_to(shard: shard) do
    yield
  end
end

क्या उस मामले में भी डिफ़ॉल्ट कनेक्शन पर वापस जाने का यही अर्थ है? github.com/influitive/apartment#middleware-considerations
बेन

1
एक बार जब आप ActiveRecord::Base.connected_to ... doब्लॉक छोड़ देते हैं तो यह फिर से डिफ़ॉल्ट कनेक्शन का उपयोग करेगा।
स्पिकमैन

@spickermann मैं इस रत्न को पढ़ रहा था, क्या केवल rails6 के लिए ही नहीं?
7urkm3n

@ 7urkm3n यह वर्तमान रेल masterशाखा में शामिल है ।
स्पिकमैन

नमस्ते उत्तर के लिए धन्यवाद। इससे पहले कि वे अच्छे समाधान हों, मैं जवाबों में से किसी एक को इनाम देने से पहले वास्तव में सुझाव का परीक्षण करने के लिए थोड़ा समय की आवश्यकता होगी।
नील्स क्रिस्टियन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.