Mysql
 sql >> डेटाबेस >  >> RDS >> Mysql

लेन-देन को तोड़े बिना रेल में एकाधिक डेटाबेस के बीच स्विच करना

ActiveRecord . के अंदर तंग युग्मन के कारण यह एक मुश्किल समस्या है , लेकिन मैं अवधारणा के कुछ सबूत बनाने में कामयाब रहा जो काम करता है। या कम से कम ऐसा लगता है कि यह काम करता है।

कुछ पृष्ठभूमि

ActiveRecord एक ActiveRecord::ConnectionAdapters::ConnectionHandler . का उपयोग करता है वर्ग जो प्रति मॉडल कनेक्शन पूल संग्रहीत करने के लिए ज़िम्मेदार है। डिफ़ॉल्ट रूप से सभी मॉडलों के लिए केवल एक कनेक्शन पूल होता है, क्योंकि सामान्य रेल ऐप एक डेटाबेस से जुड़ा होता है।

establish_connection executing को क्रियान्वित करने के बाद विशेष मॉडल में विभिन्न डेटाबेस के लिए, उस मॉडल के लिए नया कनेक्शन पूल बनाया जाता है। और उन सभी मॉडलों के लिए भी जो इससे इनहेरिट हो सकते हैं।

किसी भी क्वेरी को निष्पादित करने से पहले, ActiveRecord पहले प्रासंगिक मॉडल के लिए कनेक्शन पूल पुनर्प्राप्त करता है और फिर पूल से कनेक्शन पुनर्प्राप्त करता है।

ध्यान दें कि उपरोक्त स्पष्टीकरण 100% सटीक नहीं हो सकता है, लेकिन यह करीब होना चाहिए।

समाधान

तो विचार यह है कि डिफ़ॉल्ट कनेक्शन हैंडलर को कस्टम एक के साथ बदलें जो प्रदान किए गए शार्प विवरण के आधार पर कनेक्शन पूल लौटाएगा।

इसे कई अलग-अलग तरीकों से लागू किया जा सकता है। मैंने इसे प्रॉक्सी ऑब्जेक्ट बनाकर किया है जो छद्म नामों को प्रच्छन्न के रूप में पास कर रहा है ActiveRecord कक्षाएं। कनेक्शन हैंडलर एआर मॉडल प्राप्त करने की उम्मीद कर रहा है और name . को देखता है संपत्ति और superclass . पर भी मॉडल की पदानुक्रम श्रृंखला चलने के लिए। मैंने DatabaseModel क्रियान्वित किया है वर्ग जो मूल रूप से शार्प नाम है, लेकिन यह एआर मॉडल की तरह व्यवहार कर रहा है।

कार्यान्वयन

यहां उदाहरण कार्यान्वयन है। मैंने सादगी के लिए sqlite डेटाबेस का उपयोग किया है, आप इस फ़ाइल को बिना किसी सेटअप के चला सकते हैं। आप इस सार पर भी एक नज़र डाल सकते हैं

# Define some required dependencies
require "bundler/inline"
gemfile(false) do
  source "https://rubygems.org"
  gem "activerecord", "~> 4.2.8"
  gem "sqlite3"
end

require "active_record"

class User < ActiveRecord::Base
end

DatabaseModel = Struct.new(:name) do
  def superclass
    ActiveRecord::Base
  end
end

# Setup database connections and create databases if not present
connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new({
  "users_shard_1" => { adapter: "sqlite3", database: "users_shard_1.sqlite3" },
  "users_shard_2" => { adapter: "sqlite3", database: "users_shard_2.sqlite3" }
})

databases = %w{users_shard_1 users_shard_2}
databases.each do |database|
  filename = "#{database}.sqlite3"

  ActiveRecord::Base.establish_connection({
    adapter: "sqlite3",
    database: filename
  })

  spec = resolver.spec(database.to_sym)
  connection_handler.establish_connection(DatabaseModel.new(database), spec)

  next if File.exists?(filename)

  ActiveRecord::Schema.define(version: 1) do
    create_table :users do |t|
      t.string :name
      t.string :email
    end
  end
end

# Create custom connection handler
class ShardHandler
  def initialize(original_handler)
    @original_handler = original_handler
  end

  def use_database(name)
    @model= DatabaseModel.new(name)
  end

  def retrieve_connection_pool(klass)
    @original_handler.retrieve_connection_pool(@model)
  end

  def retrieve_connection(klass)
    pool = retrieve_connection_pool(klass)
    raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool
    conn = pool.connection
    raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn
    puts "Using database \"#{conn.instance_variable_get("@config")[:database]}\" (##{conn.object_id})"
    conn
  end
end

User.connection_handler = ShardHandler.new(connection_handler)

User.connection_handler.use_database("users_shard_1")
User.create(name: "John Doe", email: "[email protected]")
puts User.count

User.connection_handler.use_database("users_shard_2")
User.create(name: "Jane Doe", email: "[email protected]")
puts User.count

User.connection_handler.use_database("users_shard_1")
puts User.count

मुझे लगता है कि यह एक विचार देना चाहिए कि उत्पादन तैयार समाधान को कैसे कार्यान्वित किया जाए। मुझे आशा है कि मुझे यहाँ कुछ भी स्पष्ट याद नहीं आया। मैं दो अलग-अलग तरीकों का सुझाव दे सकता हूं:

  1. उपवर्ग ActiveRecord::ConnectionAdapters::ConnectionHandler और उन तरीकों को अधिलेखित कर दें जो कनेक्शन पूल को पुनः प्राप्त करने के लिए जिम्मेदार हैं
  2. ConnectionHandler के समान एपीआई को लागू करते हुए पूरी तरह से नया वर्ग बनाएं
  3. मुझे लगता है कि retrieve_connection . को ओवरराइट करना भी संभव है तरीका। मुझे याद नहीं है कि इसे कहाँ परिभाषित किया गया है, लेकिन मुझे लगता है कि यह ActiveRecord::Core में है ।

मुझे लगता है कि दृष्टिकोण 1 और 2 जाने का रास्ता है और डेटाबेस के साथ काम करते समय सभी मामलों को कवर करना चाहिए।




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MySql में हेक्स मान डालें

  2. माई एसक्यूएल। सेल्फ जॉइन का उपयोग कैसे करें

  3. Php/mysql में खोज में टाइपो सुधार लागू करने का सबसे अच्छा तरीका क्या है?

  4. पीडीओ mysql_num_rows या mssql_num_rows के बराबर

  5. PHP में एक ही नाम का उपयोग करके एकाधिक पैरामीटर प्राप्त करने के लिए $_GET का उपयोग कैसे करें