我可以編寫一個 activerecord 範圍,在查詢時將結果包裝在一個塊中嗎? (Can I write an activerecord scope which wraps the result in a block at query time?)


問題描述

我可以編寫一個 activerecord 範圍,在查詢時將結果包裝在一個塊中嗎? (Can I write an activerecord scope which wraps the result in a block at query time?)

我們的 rails 應用有一個主數據庫和一個副本數據庫。我們已經定義了一個接受塊的方法,並且在塊內部時,將對副本進行查詢,如下所示:

on_replica do
  @my_things = MyModel.where(my_attr: "my value").to_a
end

這在大多數情況下都很有效,但如果你實際上沒有在塊中執行查詢,它不起作用:

on_replica do
  @my_things = MyModel.where(my_attr: "my value")
end

# this will run its query against the primary DB
@my_things.to_a 

我真的很希望能夠定義一個範圍(或類似範圍的東西),以便在該點之後發生的任何查詢方法鏈發生在副本上:

@my_things = MyModel.where(my_attr: "my value").eventually_on_replica

# this runs its query against the replica DB:
@my_things.to_a

是否可以使用現有的 on_replica 方法定義一個能夠以這種方式工作的 eventually_on_replica 方法?如果 MyModel.eventually_on_replica.where(my_attr: "my value") 加分


參考解法

方法 1:

my idea is to wrap your on_replica into a relation then we can append it at the end of query chains.

i don't know whether your on_replica will work on this proposal or not, i just demo with ActiveRecord::Base.connected_to

# lib/active_record/base.rb
module ActiveRecord
  module Querying
    delegate :eventually_on_replica, to: :all
  end

  class Relation
    def eventually_on_replica(database: nil, role: nil, shard: nil, prevent_writes: false)
      ActiveRecord::Base.connected_to(
        database: database,
        role: role,
        shard: shard,
        prevent_writes: prevent_writes
      ) do
        self.load
      end
    end
  end
end

Demo

# config/application.rb
config.active_record.reading_role = :dev
config.active_record.reading_role = :test

# models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  connects_to database: { dev: :development, test: :test }
end

# database development
result = Requirement.where(id: [1,2,3]).eventually_on_replica(role: :dev)
result.to_a # have data

# database test
result = Requirement.where(id: [1,2,3]).eventually_on_replica(role: :test)
result.to_a # [] no data

So you can try replace ActiveRecord::Base.connected_to by your on_relica.

In case of Model.eventually_on_replica.where, i need to set a flag to mark that is running on replica then try to cache all query chains in order to execute later (on connected_to block) and it turns out a very complex case and maybe effect other queries in normal case so i don't go further.

方法 2:

For the eventually_on_replica method at the end of the chain, I have a simple method.

In your application_record.rb file (if you have it, otherwise you may need to patch ActiveRecord::Base

# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base

  def self.eventually_on_replica
    on_replica do
      self.all
    end
  end

end

However, as soon as you call eventually_on_replica, it will run the DB query and load the results. It doesn't support lazy loading but after all, you only need to call eventually_on_replica when you really need the results so maybe this will work for your use case.

(by SimonLam PhanKashif Umair Liaqat)

參考文件

  1. Can I write an activerecord scope which wraps the result in a block at query time? (CC BY‑SA 2.5/3.0/4.0)

#database-replication #rails-activerecord #ruby-on-rails






相關問題

C# 移動設備應用程序中的 SQL 複製 (SQL Replication in C# mobile device application)

在主文件日誌中獲得最後位置(MySQL 複製)? (Getting last position in master-file-log (MySQL replication)?)

複製集同步時發生Mongod慢查詢 (Mongod slow query be happend when replicate set sync)

sql server 2005 複製文章衝突 (sql server 2005 replication article conflict)

SQL 合併複製中“邏輯記錄”的未來 (Future of "Logical Records" in SQL Merge Replication)

GAE:關於高複制數據的使用 (GAE: About the Usage of High Replication Data)

在兩個不同的 PostgreSQL 數據庫服務器中更新數據 (Update data in two differents PostgreSQL database servers)

刪除 Datastax Advanced Replication 生成的重新日誌 (Deleting replogs generated by Datastax Advanced Replication)

我們可以在不同版本的 marklogic 之間進行森林或數據複製嗎? (Can we have forests or data replication between different versions of marklogic?)

MongoDB Atlas 輔助分片保持空閒 (MongoDB Atlas secondary shards remain idle)

升級過程中從 5.6 到 5.7 的 AWS RDS MySQL 複製 (AWS RDS MySQL replication from 5.6 to 5.7 during the upgrade process)

我可以編寫一個 activerecord 範圍,在查詢時將結果包裝在一個塊中嗎? (Can I write an activerecord scope which wraps the result in a block at query time?)







留言討論