Module: Familia::Connection
- Included in:
- Familia
- Defined in:
- lib/familia/connection.rb
Overview
The Connection module provides Database connection management for Familia. It allows easy setup and access to Database clients across different URIs with robust connection pooling for thread safety.
Instance Attribute Summary collapse
-
#connection_provider ⇒ Proc
A callable that provides Database connections.
-
#connection_required ⇒ Boolean
Whether to require external connections (no fallback).
-
#database_clients ⇒ Hash
readonly
A hash of Database clients, keyed by server ID.
-
#enable_database_counter ⇒ Boolean
Whether Database command counter is enabled.
-
#enable_database_logging ⇒ Boolean
Whether Database command logging is enabled.
-
#uri ⇒ URI
(also: #url)
The default URI for Database connections.
Instance Method Summary collapse
-
#connect(uri = nil) ⇒ Redis
Establishes a connection to a Database server.
-
#dbclient(uri = nil) ⇒ Redis
Retrieves a Database connection from the appropriate pool.
-
#pipeline {|Redis| ... } ⇒ Array
Executes Database commands in a pipeline for improved performance.
-
#reconnect(uri = nil) ⇒ Object
-
#transaction {|Redis| ... } ⇒ Array
(also: #multi)
Executes Database commands atomically within a transaction (MULTI/EXEC).
-
#with_connection {|Redis| ... } ⇒ Object
Provides explicit access to a Database connection.
Instance Attribute Details
#connection_provider ⇒ Proc
Returns A callable that provides Database connections.
31 32 33 |
# File 'lib/familia/connection.rb', line 31 def connection_provider @connection_provider end |
#connection_required ⇒ Boolean
Returns Whether to require external connections (no fallback).
34 35 36 |
# File 'lib/familia/connection.rb', line 34 def connection_required @connection_required end |
#database_clients ⇒ Hash (readonly)
Returns A hash of Database clients, keyed by server ID.
22 23 24 |
# File 'lib/familia/connection.rb', line 22 def database_clients @database_clients end |
#enable_database_counter ⇒ Boolean
Returns Whether Database command counter is enabled.
28 29 30 |
# File 'lib/familia/connection.rb', line 28 def enable_database_counter @enable_database_counter end |
#enable_database_logging ⇒ Boolean
Returns Whether Database command logging is enabled.
25 26 27 |
# File 'lib/familia/connection.rb', line 25 def enable_database_logging @enable_database_logging end |
#uri ⇒ URI Also known as: url
Returns The default URI for Database connections.
19 20 21 |
# File 'lib/familia/connection.rb', line 19 def uri @uri end |
Instance Method Details
#connect(uri = nil) ⇒ Redis
Establishes a connection to a Database server.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/familia/connection.rb', line 58 def connect(uri = nil) parsed_uri = normalize_uri(uri) serverid = parsed_uri.serverid if Familia.enable_database_logging DatabaseLogger.logger = Familia.logger RedisClient.register(DatabaseLogger) end if Familia.enable_database_counter # NOTE: This middleware uses AtommicFixnum from concurrent-ruby which is # less contentious than Mutex-based counters. Safe for RedisClient.register(DatabaseCommandCounter) end dbclient = Redis.new(parsed_uri.conf) if @database_clients.key?(serverid) msg = "Overriding existing connection for #{serverid}" Familia.warn(msg) end @database_clients[serverid] = dbclient end |
#dbclient(uri = nil) ⇒ Redis
Retrieves a Database connection from the appropriate pool. Handles DB selection automatically based on the URI.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/familia/connection.rb', line 100 def dbclient(uri = nil) # First priority: Thread-local connection (middleware pattern) return Thread.current[:familia_connection] if Thread.current.key?(:familia_connection) # Second priority: Connection provider if connection_provider # Always pass normalized URI with database to provider # Provider MUST return connection already on the correct database parsed_uri = normalize_uri(uri) client = connection_provider.call(parsed_uri.to_s) # In debug mode, verify the provider honored the contract if Familia.debug? && client.respond_to?(:client) current_db = client.connection[:db] expected_db = parsed_uri.db || 0 Familia.ld "Connection provider returned client on DB #{current_db}, expected #{expected_db}" if current_db != expected_db Familia.warn "Connection provider returned client on DB #{current_db}, expected #{expected_db}" end end return client end # Third priority: Fallback behavior or error raise Familia::NoConnectionAvailable, 'No connection available.' if connection_required # Legacy behavior: create connection parsed_uri = normalize_uri(uri) # Only cache when no specific URI/DB is requested to avoid DB conflicts if uri.nil? @dbclient ||= connect(parsed_uri) @dbclient.select(parsed_uri.db) if parsed_uri.db @dbclient else # When a specific DB is requested, create a new connection # to avoid conflicts with cached connections connection = connect(parsed_uri) connection.select(parsed_uri.db) if parsed_uri.db connection end end |
#pipeline {|Redis| ... } ⇒ Array
Executes Database commands in a pipeline for improved performance.
Pipelines send multiple commands without waiting for individual responses, reducing network round-trips. Commands execute independently and can succeed or fail without affecting other commands in the pipeline.
218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/familia/connection.rb', line 218 def pipeline(&) block_result = nil result = dbclient.pipelined do |conn| Fiber[:familia_pipeline] = conn begin block_result = yield(conn) # rubocop:disable Lint/UselessAssignment ensure Fiber[:familia_pipeline] = nil # cleanup reference end end # Return the pipeline result which contains the command results result end |
#reconnect(uri = nil) ⇒ Object
83 84 85 86 87 88 89 90 91 |
# File 'lib/familia/connection.rb', line 83 def reconnect(uri = nil) parsed_uri = normalize_uri(uri) serverid = parsed_uri.serverid # Close the existing connection if it exists @database_clients[serverid].close if @database_clients.key?(serverid) connect(parsed_uri) end |
#transaction {|Redis| ... } ⇒ Array Also known as: multi
Comparison of Database batch operations:
Feature | Multi/Exec | Pipeline |
---|---|---|
Atomicity | Yes | No |
Performance | Good | Better |
Error handling | All-or-nothing | Per-command |
Use case | Data consistency | Bulk operations |
Executes Database commands atomically within a transaction (MULTI/EXEC).
Database transactions queue commands and execute them atomically as a single unit. All commands succeed together or all fail together, ensuring data consistency.
169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/familia/connection.rb', line 169 def transaction(&) block_result = nil result = dbclient.multi do |conn| Fiber[:familia_transaction] = conn begin block_result = yield(conn) # rubocop:disable Lint/UselessAssignment ensure Fiber[:familia_transaction] = nil # cleanup reference end end # Return the multi result which contains the transaction results result end |
#with_connection {|Redis| ... } ⇒ Object
Provides explicit access to a Database connection.
This method is useful when you need direct access to a connection for operations not covered by other methods. The connection is properly managed and returned to the pool (if using connection_provider).
247 248 249 |
# File 'lib/familia/connection.rb', line 247 def with_connection(&block) yield dbclient end |