Class: PgReports::Connection::Target

Inherits:
Object
  • Object
show all
Defined in:
lib/pg_reports/connection/target.rb

Overview

Represents a single named PostgreSQL target (host+credentials). Holds a memoized AR-subclass-backed connection per database it has been asked for, so switching between databases on the same target reuses pools.

The :primary target wraps ActiveRecord::Base directly when accessed at its default database — we don’t open a parallel pool to the host app’s DB. For non-default databases we create an isolated AR subclass that has its own pool, so the host application’s pool is never affected.

Defined Under Namespace

Classes: ConnectionFailed

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, spec) ⇒ Target

Returns a new instance of Target.



18
19
20
21
22
# File 'lib/pg_reports/connection/target.rb', line 18

def initialize(name, spec)
  @name = name.to_sym
  @spec = normalize_spec(spec)
  @pools = {} # database (string) => AR class
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



16
17
18
# File 'lib/pg_reports/connection/target.rb', line 16

def name
  @name
end

Instance Method Details

#ar_class_for(database = nil) ⇒ Object

Resolve the AR class backing the connection for ‘database` (nil = default). Returns a class responding to `.connection` (ActiveRecord::Base or subclass).

Raises:

  • (ArgumentError)


35
36
37
38
39
40
# File 'lib/pg_reports/connection/target.rb', line 35

def ar_class_for(database = nil)
  db = (database || default_database).to_s
  raise ArgumentError, "Cannot resolve connection: target #{name.inspect} has no default database and none was given" if db.empty?

  @pools[db] ||= build_pool_class(db)
end

#connection_for(database = nil) ⇒ Object

Returns the active PG connection for ‘database`, opening it if needed.



43
44
45
46
47
# File 'lib/pg_reports/connection/target.rb', line 43

def connection_for(database = nil)
  ar_class_for(database).connection
rescue ActiveRecord::NoDatabaseError, PG::ConnectionBad => e
  raise ConnectionFailed, "Cannot connect to #{name}/#{database || default_database}: #{e.message}"
end

#default_databaseObject



29
30
31
# File 'lib/pg_reports/connection/target.rb', line 29

def default_database
  @spec[:database]&.to_s
end

#disconnect!Object

Close all derived pools we own. The :primary AR::Base pool is never touched.



65
66
67
68
69
70
71
72
73
# File 'lib/pg_reports/connection/target.rb', line 65

def disconnect!
  @pools.each_value do |klass|
    next if klass.equal?(ActiveRecord::Base)
    klass.connection_pool.disconnect! if klass.connection_pool.connected?
  rescue
    # Best-effort cleanup
  end
  @pools.clear
end

#list_databases(current: nil) ⇒ Object

List all databases visible on this target’s cluster (using pg_database). Result rows: { “name” => String, “size” => String, “current” => Boolean }



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/pg_reports/connection/target.rb', line 51

def list_databases(current: nil)
  rows = connection_for.exec_query(<<~SQL).to_a
    SELECT datname AS name,
           pg_size_pretty(pg_database_size(datname)) AS size
    FROM pg_database
    WHERE datistemplate = false AND datallowconn = true
    ORDER BY datname
  SQL
  current_db = (current || default_database).to_s
  rows.each { |r| r["current"] = (r["name"].to_s == current_db) }
  rows
end

#specObject

Configuration hash used as a template for derived databases.



25
26
27
# File 'lib/pg_reports/connection/target.rb', line 25

def spec
  @spec.dup
end