Module: Familia::Connection::Operations

Included in:
Familia::Connection
Defined in:
lib/familia/connection/operations.rb

Instance Method Summary collapse

Instance Method Details

#pipelined(&block) {|Redis| ... } ⇒ Array, MultiResult Also known as: pipeline

Note:

Pipeline vs Transaction Differences:

  • Pipeline: Commands executed independently, some may succeed while others fail
  • Transaction: All-or-nothing execution, commands are atomic as a group
  • Pipeline: Better performance for independent operations
  • Transaction: Better consistency for related operations
Note:

Connection Handler Compatibility:

  • ProviderConnectionHandler: Full pipeline support
  • CreateConnectionHandler: Full pipeline support
  • FiberTransactionHandler: Blocked (raises OperationModeError)
  • FiberConnectionHandler: Blocked (raises OperationModeError)
  • DefaultConnectionHandler: Blocked (raises OperationModeError)
Note:

Thread Safety: Uses Fiber-local storage to maintain pipeline context across nested calls and ensure proper cleanup even when exceptions occur.

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.

Executes Redis commands in a pipeline for improved performance.

Batches multiple Redis commands together and sends them in a single network round-trip, improving performance for multiple independent operations. Returns a MultiResult object containing both success status and command results.

Examples:

Basic pipeline usage

Familia.pipelined do |pipe|
  pipe.set("key1", "value1")
  pipe.incr("counter")
  pipe.lpush("list", "item")
end
# Returns: ["OK", 2, 1] - results of all commands

Error handling - commands succeed/fail independently

results = Familia.pipelined do |conn|
  conn.set("valid_key", "value")     # This will succeed
  conn.incr("string_key")            # This will fail (wrong type)
  conn.set("another_key", "value2")  # This will still succeed
end
# Returns: ["OK", Redis::CommandError, "OK"]
# Notice how the error doesn't prevent other commands from executing

Contrast with transaction behavior

results = Familia.transaction do |conn|
  conn.set("inventory:item1", 100)
  conn.incr("invalid_key")        # Fails, rolls back everything
  conn.set("inventory:item2", 200) # Won't be applied
end
# Result: neither item1 nor item2 are set due to the error

Basic pipeline usage

result = Familia.pipelined do |conn|
  conn.set('key1', 'value1')
  conn.set('key2', 'value2')
  conn.get('key1')
  conn.incr('counter')
end
result.successful?    # => true (if all commands succeeded)
result.results        # => ["OK", "OK", "value1", 1]
result.results.length # => 4

Performance optimization with pipeline

# Instead of multiple round-trips:
# value1 = redis.get('key1')  # Round-trip 1
# value2 = redis.get('key2')  # Round-trip 2
# value3 = redis.get('key3')  # Round-trip 3

# Use pipeline for single round-trip:
result = Familia.pipelined do |conn|
  conn.get('key1')
  conn.get('key2')
  conn.get('key3')
end
values = result.results  # => ["value1", "value2", "value3"]

Checking pipeline success

result = Familia.pipelined do |conn|
  conn.set('temp_key', 'temp_value')
  conn.expire('temp_key', 60)
  conn.get('temp_key')
end

if result.successful?
  puts "Pipeline completed: #{result.results}"
else
  puts "Some operations failed: #{result.results}"
end

Nested pipelines (reentrant behavior)

result = Familia.pipelined do |outer_conn|
  outer_conn.set('outer', 'value')

  # Nested pipeline reuses the same connection
  inner_result = Familia.pipelined do |inner_conn|
    inner_conn.get('outer')  # Returns Redis::Future in nested context
  end

  outer_conn.get('outer')
end

Parameters:

  • block (Proc)

    The block containing Redis commands to execute in pipeline

Yields:

  • (Redis)

    The Database pipeline connection

  • (Redis)

    conn The Redis connection configured for pipelined mode

Returns:

  • (Array)

    Results of all commands executed in the pipeline

  • (MultiResult)

    Result object with success status and command results

Raises:

  • (Familia::OperationModeError)

    When called with incompatible connection handlers (e.g., FiberConnectionHandler or DefaultConnectionHandler that don't support pipelines)

See Also:



221
222
223
# File 'lib/familia/connection/operations.rb', line 221

def pipelined(&block)
  PipelineCore.execute_pipeline(-> { dbclient }, &block)
end

#transaction {|Redis| ... } ⇒ Array, MultiResult Also known as: multi

Note:

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
Note:

Connection Handler Compatibility:

  • FiberTransactionHandler: Supports reentrant transactions
  • ProviderConnectionHandler: Full transaction support
  • CreateConnectionHandler: Full transaction support
  • FiberConnectionHandler: Blocked (raises OperationModeError)
  • DefaultConnectionHandler: Blocked (raises OperationModeError)
Note:

Thread Safety: Uses Fiber-local storage to maintain transaction context across nested calls and ensure proper cleanup even when exceptions occur.

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.

Executes a Redis transaction (MULTI/EXEC) with proper connection handling.

Provides atomic execution of multiple Redis commands with automatic connection management and operation mode enforcement. Returns a MultiResult object containing both success status and command results.

Examples:

Basic transaction usage

Familia.transaction do |trans|
  trans.set("key1", "value1")
  trans.incr("counter")
  trans.lpush("list", "item")
end
# Returns: ["OK", 2, 1] - results of all commands

Basic transaction usage

result = Familia.transaction do |conn|
  conn.set('key1', 'value1')
  conn.set('key2', 'value2')
  conn.get('key1')
end
result.successful?    # => true (if all commands succeeded)
result.results        # => ["OK", "OK", "value1"]
result.results.first  # => "OK"

Checking transaction success

result = Familia.transaction do |conn|
  conn.incr('counter')
  conn.decr('other_counter')
end

if result.successful?
  puts "All commands succeeded: #{result.results}"
else
  puts "Some commands failed: #{result.results}"
end

Nested transactions (reentrant behavior)

result = Familia.transaction do |outer_conn|
  outer_conn.set('outer', 'value')

  # Nested transaction reuses the same connection
  inner_result = Familia.transaction do |inner_conn|
    inner_conn.set('inner', 'value')
    inner_conn.get('inner')  # Returns the value directly in nested context
  end

  [outer_result, inner_result]
end

Parameters:

  • block (Proc)

    The block containing Redis commands to execute atomically

Yields:

  • (Redis)

    The Database transaction connection

  • (Redis)

    conn The Redis connection configured for transaction mode

Returns:

  • (Array)

    Results of all commands executed in the transaction

  • (MultiResult)

    Result object with success status and command results

Raises:

  • (Familia::OperationModeError)

    When called with incompatible connection handlers (e.g., FiberConnectionHandler or DefaultConnectionHandler that don't support transactions)

See Also:



99
100
101
# File 'lib/familia/connection/operations.rb', line 99

def transaction(&)
  Familia::Connection::TransactionCore.execute_transaction(-> { dbclient }, &)
end

#with_dbclient {|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).

Examples:

Using with_dbclient for custom operations

Familia.with_dbclient do |conn|
  conn.set("custom_key", "value")
  conn.expire("custom_key", 3600)
end

Yields:

  • (Redis)

    A Database connection

Returns:

  • The result of the block



241
242
243
# File 'lib/familia/connection/operations.rb', line 241

def with_dbclient(&)
  yield dbclient
end

#with_isolated_dbclient(uri = nil) {|Redis| ... } ⇒ Object

Provides explicit access to an isolated Database connection for temporary operations.

This method creates a new connection that won't interfere with the cached connection pool, executes the given block with that connection, and ensures the connection is properly closed afterward.

Perfect for database scanning, inspection, or migration operations where you need to access different databases without affecting your models' normal connections.

Examples:

Safely scanning for legacy data

Familia.with_isolated_dbclient(5) do |conn|
  conn.keys("session:*")
end

Performing migration tasks

Familia.with_isolated_dbclient(1) do |conn|
  conn.scan_each(match: "user:*") { |key| puts key }
end

Parameters:

  • uri (String, URI, Integer, nil) (defaults to: nil)

    The URI or database number to connect to.

Yields:

  • (Redis)

    An isolated Database connection

Returns:

  • The result of the block



269
270
271
272
273
274
275
276
# File 'lib/familia/connection/operations.rb', line 269

def with_isolated_dbclient(uri = nil, &)
  client = isolated_dbclient(uri)
  begin
    yield client
  ensure
    client&.close
  end
end