Module: Ec::Pg::ShardManager

Defined in:
lib/ec/pg/shard_manager.rb

Overview

Manages ActiveRecord database shard switching.

Leverages ActiveRecord’s native multi-database support (AR 6.1+) via connected_to(shard:). Falls back to manual connection swapping for connection configurations that are not registered as named shards.

Configuration in database.yml (Rails multi-db style)

production:
  primary:
    <<: *default
    database: app_primary
  db_sharded_shard_1:
    <<: *default
    database: app_shard_one
    migrations_paths: db/migrate_shards

Usage

ShardManager.with_shard(:shard_one) { User.all }
ShardManager.with_shard(:shard_one, role: :reading) { User.all }

Defined Under Namespace

Classes: ShardNotFound, UnsupportedActiveRecordVersion

Constant Summary collapse

MinimumARVersion =
Gem::Version.new("7.1")

Class Method Summary collapse

Class Method Details

.active?Boolean

Returns true when the thread is operating inside a with_shard block (or when the shard was set via thread-local assignment).

Returns:

  • (Boolean)


59
60
61
# File 'lib/ec/pg/shard_manager.rb', line 59

def active?
  !Context.shard.nil?
end

.assert_ar_version!Object



82
83
84
85
86
87
88
89
# File 'lib/ec/pg/shard_manager.rb', line 82

def assert_ar_version!
  ar_version = Gem::Version.new(ActiveRecord.version.to_s)
  return if ar_version >= MinimumARVersion

  raise UnsupportedActiveRecordVersion,
        "activerecord-multi-tenant shard switching requires ActiveRecord >= 6.1. " \
        "Current version: #{ar_version}"
end

.current_shardObject

Returns the shard currently set in thread context, or :default when none is set (mirrors how AR treats the default shard).



65
66
67
# File 'lib/ec/pg/shard_manager.rb', line 65

def current_shard
  Context.shard || :default
end

.with_shard(shard_name, role: :writing, klass: ActiveRecord::Base) { ... } ⇒ Object

Executes block with the AR connection switched to shard_name.

Parameters:

  • shard_name (Symbol)

    the registered shard key

  • role (Symbol) (defaults to: :writing)

    :writing (default) or :reading

  • klass (Class) (defaults to: ActiveRecord::Base)

    the AR base class whose connection pool to switch (default: ActiveRecord::Base)

Yields:

  • block to run in shard context

Returns:

  • the return value of block



42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/ec/pg/shard_manager.rb', line 42

def with_shard(shard_name, role: :writing, klass: ActiveRecord::Base, &block)
  shard_name = shard_name.to_sym
  assert_ar_version!

  Context.with(shard: shard_name) do
    klass.prohibit_shard_swapping(true) do
      klass.connected_to(shard: shard_name, role: role, &block)
    end
  end
rescue ActiveRecord::ConnectionNotEstablished => e
  raise ShardNotFound,
        "Could not connect to shard #{shard_name.inspect}. " \
        "Make sure it is registered via connects_to. Original error: #{e.message}"
end