octoshark, Octoshark是一个ActiveRecord连接管理器

分享于 

12分钟阅读

GitHub

  繁體 雙語
Octoshark is an ActiveRecord connection switcher
  • 源代码名称:octoshark
  • 源代码网址:http://www.github.com/dalibor/octoshark
  • octoshark源代码文档
  • octoshark源代码下载
  • Git URL:
    git://www.github.com/dalibor/octoshark.git
    Git Clone代码到本地:
    git clone http://www.github.com/dalibor/octoshark
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/dalibor/octoshark
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    

    Octoshark logo

    Travis status

    Octoshark是一个简单的ActiveRecord连接管理器。 它提供了可以在主 slave。分片或者多租户架构等各种场景中使用的连接交换机制。

    安装

    将此行添加到你的应用程序的Gemfile中:

     
    gem 'octoshark'
    
    
    
     

    然后执行:

     
    $ bundle
    
    
    
     

    或者将它的自己安装为:

    
    $ gem install octoshark
    
    
    
    

    用法

    Octoshark有两个连接管理器: 用于管理使用持久连接和 ConnectionManager 管理非持久连接的连接池的ConnectionPoolsManager。 这取决于你的应用程序性能和扩展需求。

    • 如果你有有限的消费者(。应用程序和工作服务器),ConnectionPoolsManager 将是首选选项。 标准 Rails 应用程序具有单个连接池,而 ConnectionPoolsManager 只允许应用程序模型使用多个连接池。

    • 如果你的基础设施非常庞大,有很多消费者,你在数据库服务器上点击最大连接限制,那么你需要水平扩展,ConnectionManager 是使用的选项。 因为它使用非持久连接,所以会产生性能损失,因为连接是通过反复建立的。 一些与 Having 一起依赖于活动数据库连接的vxml插件可以能需要一个更改来处理不持久连接。

    可以将 ConnectionPoolsManagerConnectionManager 组合在一起,同时也可以同时使用其中的许多。

    下面是创建连接池管理器的方法:

    CONN_MANAGER=Octoshark::ConnectionPoolsManager.new({ c1: config1, c2: config2 })

    config1config2 是标准的ActiveRecord数据库配置:

    config = {
     adapter:'mysql2',
     host:'localhost',
     port:3306,
     database:'database',
     username:'root',
     password:'pass',
     pool:1,
     encoding:'utf8',
     reconnect:false}

    若要使用特定池进行连接,请执行下列操作:

    CONN_MANAGER.with_connection(:c1) do |connection|
     connection.execute("SELECT 1")end

    可以嵌套多个 with_connection 块:

    CONN_MANAGER.with_connection(config1) do# run queries on connection specified with config1CONN_MANAGER.with_connection(config2) do# run queries on connection specified with config2end# run queries on connection specified with config1end

    在数据库服务器上建立一个连接,而不是数据库,那么可以使用第二个可选参数 database_name 告诉连接管理器在同一个连接内的连接中。 当你想要拥有更少的连接池和控制每个数据库服务器的活动连接数时,这很有用。 这里选项现在只是特定于 USE database_name的,它使用语句来对连接进行 switch。

    CONN_MANAGER.with_connection(:c1, database_name) do |connection|
     connection.execute("SELECT 1")end

    使用与 Octoshark::ConnectionManager的非持久连接具有相同的API:

    CONN_MANAGER=Octoshark::ConnectionManager.new

    打开一个新连接,执行查询并关闭连接:

    CONN_MANAGER.with_connection(config) do |connection|
     connection.execute("SELECT 1")end

    使用Octoshark和ActiveRecord模型

    要告诉ActiveRecord模型使用Octoshark连接,我们可以重写 Model.connection 方法。

    classPost <ActiveRecord::Basedefself.connectionCONN_MANAGER.current_connection
     endend

    或者,我们可以将它的作为 MODULE 进行提取,并包含在多个模型中。

    moduleShardingModelextendActiveSupport::ConcernmoduleClassMethodsdefconnectionCONN_MANAGER.current_connection
     endendend

    要使用特定的数据库连接,请执行以下操作:

    CONN_MANAGER.with_connection(:c1) do# run queries on c1Post.firstend

    在 Rails 应用程序中这种连接切换通常由控制器在 around_filter 中完成,并以类似于后台作业之类的其他应用程序"入口点"的方式进行:

    around_filter :select_sharddefselect_shard(&block)
     CONN_MANAGER.with_connection(current_user.shard, &block)end

    当执行处于 with_connection 块或者引发时,CONN_MANAGER.current_connection 返回活动连接 Octoshark::Error::NoCurrentConnectionwith_connection 块之外。 在某些情况下,返回到 Rails 应用程序的默认数据库连接可能比较好,可以使用 CONN_MANAGER.current_or_default_connection

    octoshark:: connectionpoolsmanager。reset_connection_managers !

    在使用 Octoshark::ConnectionPoolsManager 时,只要 ActiveRecord::Base 调用 establish_connection ( 通常是一个祖先进程,随后必须有分支), Octoshark.reset_connection_managers! 被自动调用以建立Octoshark连接。 它阻止 ActiveRecord::ConnectionNotEstablished 在以下场景中:

    • fork 之前/之后的Unicorn
    • spring prefork/服务器
    • 一些rake任务如 rake db:test:prepare

    清洗测试数据库

    对于 Octoshark::ConnectionPoolsManager,我们可以使用 DatabaseCleaner插件和 RSpec,例如:

    config.before(:suite) do setup_database_cleaner
     DatabaseCleaner.clean_with(:truncation)endconfig.before(:each) do setup_database_cleaner
     DatabaseCleaner.startendconfig.after(:each) doDatabaseCleaner.clean_with(:transaction)enddefsetup_database_cleanerDatabaseCleaner[:active_record, {connection:ActiveRecord::Base.connection_pool}]
     Octoshark::ConnectionPoolsManager.connection_managers.each do |manager|
     manager.connection_pools.each_pair do |connection_name, connection_pool|
     DatabaseCleaner[:active_record, {connection: connection_pool}]
     endendend

    对于动态创建连接和数据库且不能在测试设置中配置的Octoshark::ConnectionManager,可以编写自定义数据库清理器( 由 DatabaseRewinder 激发)。 下面的示例为一个多租户测试设置和一个主( 核心层) 数据库和测试套件中 CURRENT_USER的租户数据库。 在测试套件之前 CustomDatabaseCleaner.clean_all 清理所有核心数据库表,CustomDatabaseCleaner.clean 在每次测试后清除核心数据库和租户数据库中的。

    moduleCustomDatabaseCleanerINSERT_REGEX=/AINSERT(?:s+IGNORE)?s+INTOs+(?:.*[`"]?(?<table>[^.s`"]+)[`"]?)*/i@@tables_with_inserts= []
     class <<selfdefrecord_inserted_table(connection, sql)
     match = sql.match(INSERT_REGEX)
     if match && match[:table] && tables_with_inserts.exclude?(match[:table])
     tables_with_inserts << match[:table]
     endenddefclean_all with_core_db_connection do |connection|
     clean_tables(connection)
     end reset_tables_with_inserts
     enddefclean with_core_db_connection do |connection|
     clean_tables(connection, { 'users' => [CURRENT_USER.id] })
     endCURRENT_USER.with_tenant do |connection|
     clean_tables(connection)
     end reset_tables_with_inserts
     endprivatedefwith_core_db_connection(&block)
     CoreDBManager.with_connection(ActiveRecord::Base.configurations[Rails.env].symbolize_keys, &block)
     enddefclean_tables(connection, keep_data= {})
     tables_to_clean = connection.tables.reject { |t| t ==ActiveRecord::Migrator.schema_migrations_table_name }
     tables_to_clean = tables_to_clean & tables_with_inserts if tables_with_inserts.present?
     tables_to_clean.each do |table|
     connection.disable_referential_integrity do table_name = connection.quote_table_name(table)
     keep_ids = keep_data[table]
     if keep_ids
     connection.execute("DELETE FROM #{table_name} WHERE id NOT IN (#{keep_ids.join(',')});")
     else connection.execute("DELETE FROM #{table_name};")
     endendendenddefreset_tables_with_inserts@@tables_with_inserts= []
     enddeftables_with_inserts@@tables_with_insertsendendendmoduleCustomDatabaseCleanermoduleInsertRecorderdefexecute(sql, *)
     CustomDatabaseCleaner.record_inserted_table(self, sql)
     superenddefexec_query(sql, *)
     CustomDatabaseCleaner.record_inserted_table(self, sql)
     superendendendrequire'active_record/connection_adapters/abstract_mysql_adapter'ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.send(:prepend, CustomDatabaseCleaner::InsertRecorder)

    插件开发设置

    安装数据库配置并创建数据库:

    cp spec/support/config.yml.template spec/support/config.yml
    rake db:create

    运行说明:

    bundle exec rspec spec

    安装在 Appraisals 中定义的不同 Active Record 版本,并为所有的版本运行规范:

    bundle exec appraisal
    bundle exec appraisal rspec spec

    logo

    感谢 @saschamt Octoshark logo 设计。 : )

    • fork ( http://github.com/dalibor/octoshark/fork )
    • 创建你的特征分支( git checkout -b my-new-feature )
    • 提交你的更改( git commit -am 'Add some feature' )
    • 推送到分支( git push origin my-new-feature )
    • 创建新的拉请求

    相关文章