Module: MixedGauge::Model::ClassMethods

Defined in:
lib/mixed_gauge/model.rb

Overview

ClassMethods

Instance Method Summary collapse

Instance Method Details

#all_shardsArray<Class>

Returns all generated shard model class. Useful to query to all shards.

Examples:

User.all_shards.flat_map {|m| m.find_by(name: 'alice') }.compact

Returns:

  • (Array<Class>)

    An array of shard models



128
129
130
# File 'lib/mixed_gauge/model.rb', line 128

def all_shards
  shard_repository.all
end

#all_shards_in_parallelMixedgauge::AllShardsInParallel Also known as: parallel

Examples:

User.all_shards_in_parallel.map {|m| m.where.find_by(name: 'Alice') }.compact

Returns:

  • (Mixedgauge::AllShardsInParallel)


135
136
137
# File 'lib/mixed_gauge/model.rb', line 135

def all_shards_in_parallel
  AllShardsInParallel.new(all_shards, service: service)
end

#before_put(&block) ⇒ Object

Register hook to assign auto-generated distkey or something. Sometimes you want to generates distkey value before validation. Since mixed_gauge generates sub class of your models, AR’s callback is not usesless for this usecase, so mixed_gauge offers its own callback method.

Examples:

class User
  include MixedGauge::Model
  use_cluster :user
  def_distkey :name
  before_put do |attributes|
    attributes[:name] = generate_name unless attributes[:name]
  end
end


111
112
113
# File 'lib/mixed_gauge/model.rb', line 111

def before_put(&block)
  @before_put_callback = block
end

#def_distkey(column) ⇒ Object

Distkey is a column. mixed_gauge hashes that value and determine which shard to store.

Parameters:

  • column (Symbol)


53
54
55
# File 'lib/mixed_gauge/model.rb', line 53

def def_distkey(column)
  self.distkey = column.to_sym
end

#get(key) ⇒ ActiveRecord::Base?

Returns nil when not found. Except that, is same as ‘.get!`.

Parameters:

  • key (String)

Returns:

  • (ActiveRecord::Base, nil)

    A shard model instance



83
84
85
86
# File 'lib/mixed_gauge/model.rb', line 83

def get(key)
  raise 'key must be a String' unless key.is_a?(String)
  shard_for(key.to_s).find_by(distkey => key)
end

#get!(key) ⇒ ActiveRecord::Base

‘.get!` raises MixedGauge::RecordNotFound which is child class of `ActiveRecord::RecordNotFound` so you can rescue that exception as same as AR’s RecordNotFound.

Parameters:

  • key (String)

Returns:

  • (ActiveRecord::Base)

    A shard model instance

Raises:



94
95
96
# File 'lib/mixed_gauge/model.rb', line 94

def get!(key)
  get(key) || raise(MixedGauge::RecordNotFound)
end

#parent_methods(&block) ⇒ Object

Define utility methods which uses all shards or specific shard. These methods can be called from included model class.

Examples:

class User
  include MixedGauge::Model
  use_cluster :user
  def_distkey :name
  parent_methods do
    def all_count
      parallel.map {|m| m.count }.reduce(&:+)
    end

    def find_from_all_by(condition)
      parallel.flat_map {|m m.find_by(condition) }.compact.first
    end
  end
end

User.put!(email: '[email protected]', name: 'a')
User.put!(email: '[email protected]', name: 'b')
User.all_count #=> 2
User.find_from_all_by(name: 'b') #=> User b


177
178
179
# File 'lib/mixed_gauge/model.rb', line 177

def parent_methods(&block)
  instance_eval(&block)
end

#put!(attributes) ⇒ ActiveRecord::Base

Create new record with given attributes in proper shard for given key. When distkey value is empty, raises MixedGauge::MissingDistkeyAttribute error.

Parameters:

  • attributes (Hash)

Returns:

  • (ActiveRecord::Base)

    A shard model instance

Raises:



69
70
71
72
73
74
75
76
77
78
# File 'lib/mixed_gauge/model.rb', line 69

def put!(attributes)
  raise '`distkey` is not defined. Use `def_distkey`.' unless distkey
  @before_put_callback.call(attributes) if @before_put_callback

  if (key = attributes[distkey]) || attributes[distkey.to_s]
    shard_for(key).create!(attributes)
  else
    raise MixedGauge::MissingDistkeyAttribute
  end
end

#replicates_with(mapping) ⇒ Object

Parameters:

  • mapping (Hash{Symbol => Symbol})

    A pairs of role name and AR model class name.



59
60
61
# File 'lib/mixed_gauge/model.rb', line 59

def replicates_with(mapping)
  self.replication_mapping = MixedGauge::ReplicationMapping.new(mapping)
end

#shard_for(key) ⇒ Class

Returns a generated model class of included model class which has proper connection config for the shard for given key.

Parameters:

  • key (String)

    A value of distkey

Returns:

  • (Class)

    A generated model class for given distkey value



119
120
121
122
# File 'lib/mixed_gauge/model.rb', line 119

def shard_for(key)
  connection_name = cluster_routing.route(key.to_s)
  shard_repository.fetch(connection_name)
end

#switch(role_name, &block) ⇒ Class, Object

See example definitions in ‘spec/models.rb`.

Examples:

UserReadonly.all_shards.each do |m|
  target_ids = m.where(age: 1).pluck(:id)
  m.switch(:master) do |master|
    master.where(id: target_ids).delete_all
  end
end

Parameters:

  • A (Symbol)

    role name of target cluster.

Returns:

  • (Class, Object)

    if block given then yielded result else target shard model.



151
152
153
# File 'lib/mixed_gauge/model.rb', line 151

def switch(role_name, &block)
  replication_mapping.switch(self, role_name, &block)
end

#use_cluster(name, thread_pool_size_base: 3) ⇒ Object

The cluster config must be defined before ‘use_cluster`.

Parameters:

  • name (Symbol)

    A cluster name which is set by MixedGauge.configure



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/mixed_gauge/model.rb', line 34

def use_cluster(name, thread_pool_size_base: 3)
  config = MixedGauge.config.fetch_cluster_config(name)
  self.cluster_routing = MixedGauge::Routing.new(config)
  self.shard_repository = MixedGauge::ShardRepository.new(config, self)
  thread_size = (shard_repository.all.size * thread_pool_size_base)
  self.service = Expeditor::Service.new(
    executor: Concurrent::ThreadPoolExecutor.new(
      min_threads: thread_size,
      max_threads: thread_size,
      max_queue: shard_repository.all.size,
      fallback_policy: :abort
    )
  )
  self.abstract_class = true
end