123456789_123456789_123456789_123456789_123456789_

Module: ActiveRecord::ConnectionAdapters::RactorConnectionHandler::Proxy

Do not use. This module is for internal use only.

Overview

The boundary between worker Ractors and the main Ractor, which owns the real connection handler, pools, and connections.

Class Attribute Summary

Class Method Summary

Class Attribute Details

.connections (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 187

attr_reader :connections

Class Method Details

.boundary_safe_bind(bind) (mod_func)

Attribute types may close over procs, so the database value is resolved locally

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 123

def boundary_safe_bind(bind)
  return bind unless bind.is_a?(ActiveModel::Attribute)
  return bind if bind.value_before_type_cast.is_a?(StatementCache::Substitute)

  safe = Relation::QueryAttribute.new(bind.name, bind.value_for_database, ActiveModel::Type.default_value)
  safe.value_for_database # resolve the memo so a frozen copy never mutates
  safe
end

.capture_transport_errors

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 189

def capture_transport_errors
  yield
rescue => error
  ErrorResponse.new(error)
end

.checkin_all_connections (mod_func)

For supervisors tearing down worker Ractors, which cannot release their own tokens when they die.

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 162

def checkin_all_connections
  main_operation do
    @connections_lock.synchronize do
      @connections.each_value do |connection|
        reclaim(connection)
        connection.pool.checkin(connection) if connection.in_use?
      end
      @connections.clear
    end
    nil
  end
end

.checkin_token(connection_token) (mod_func)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 132

def checkin_token(connection_token)
  main_operation do
    if connection = take_back_connection(connection_token)
      connection.pool.checkin(connection) if connection.in_use?
    end
    nil
  end
end

.connection_pinned?(connection_token) ⇒ Boolean (mod_func)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 175

def connection_pinned?(connection_token)
  main_operation do
    connection = @connections_lock.synchronize { @connections[connection_token] }
    !!(connection && connection.in_use?)
  end
end

.discard_token(connection_token) (mod_func)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 151

def discard_token(connection_token)
  main_operation do
    if connection = take_back_connection(connection_token)
      connection.pool.remove(connection)
      connection.discard!
    end
    nil
  end
end

.dump_binds(binds) (mod_func)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 92

def dump_binds(binds)
  return nil if binds.nil? || binds.empty?

  dump_object(binds.map { |bind| boundary_safe_bind(bind) }, "bind parameters")
end

.dump_column_types(result) (mod_func)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 98

def dump_column_types(result)
  types = result.columns.map { |name| result.column_types[name] }
  return nil if types.all?(&:nil?)

  begin
    Marshal.dump(types).freeze
  rescue TypeError
    # Result falls back to Type.default_value for nil entries.
    safe_types = types.map do |type|
      Marshal.dump(type)
      type
    rescue TypeError
      nil
    end
    Marshal.dump(safe_types).freeze
  end
end

.dump_object(value, description) (mod_func)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 116

def dump_object(value, description)
  Marshal.dump(value).freeze
rescue TypeError => error
  raise ActiveRecordError, "Cannot send #{description} across the Ractor boundary: #{error.message}"
end

.fetch_connection(connection_token)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 217

def fetch_connection(connection_token)
  connection = @connections_lock.synchronize { @connections[connection_token] }
  unless connection
    raise ConnectionNotEstablished, "The Ractor-pinned connection for token #{connection_token.inspect} has been released"
  end
  connection
end

.fetch_transaction_connection(connection_token, transaction_manager_id)

As fetch_connection, but raises if the connection's transaction manager has been replaced since transaction_manager_id was recorded: work belonging to the lost transactions would otherwise run outside any transaction.

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 229

def fetch_transaction_connection(connection_token, transaction_manager_id)
  connection = fetch_connection(connection_token)
  if transaction_manager_id && connection.transaction_manager.object_id != transaction_manager_id
    raise ConnectionNotEstablished, "The Ractor-pinned connection was reset while a transaction was open, and the server rolled it back"
  end
  connection
end

.forget_all_connections!

After a fork, the inherited tokens name discarded connections and must not be checked back in.

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 238

def forget_all_connections!
  @connections_lock.synchronize { @connections.clear }
end

.main_connection_handler

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 195

def main_connection_handler
  ActiveRecord::Base.default_connection_handler
end

.main_operation(connection_pool: nil, &block) (mod_func)

Proxy work to the main ractor. A worker's block may only capture shareable objects.

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 83

def main_operation(connection_pool: nil, &block)
  operation = ActiveSupport::Ractors.shareable_proc(self: Proxy, &block)
  outcome = ActiveSupport::Ractors.on_main do
    Proxy.capture_transport_errors { operation.call }
  end
  raise outcome.exception(connection_pool: connection_pool) if outcome.is_a?(ErrorResponse)
  outcome
end

.main_pool(connection_name, role, shard)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 199

def main_pool(connection_name, role, shard)
  main_connection_handler.retrieve_connection_pool(
    connection_name,
    role: role,
    shard: shard,
    strict: true,
  )
end

.reclaim(connection) (private)

Token-pinned connections are leased on the dispatch thread

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 251

def reclaim(connection)
  connection.steal! if connection.in_use?
end

.register_connection(connection)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 208

def register_connection(connection)
  connection.proxied = true
  @connections_lock.synchronize do
    token = (@next_token += 1)
    @connections[token] = connection
    token
  end
end

.remove_token(connection_token) (mod_func)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 141

def remove_token(connection_token)
  main_operation do
    if connection = take_back_connection(connection_token)
      connection.pool.remove(connection)
      connection.disconnect!
    end
    nil
  end
end

.take_back_connection(connection_token) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/ractor_connection_handler/proxy.rb', line 243

def take_back_connection(connection_token)
  if connection = @connections_lock.synchronize { @connections.delete(connection_token) }
    reclaim(connection)
    connection
  end
end