VOOZH about

URL: https://deepwiki.com/hypervel/redis/4.2-context-management

⇱ Context Management | hypervel/redis | DeepWiki


Loading...
Menu

Context Management

Purpose and Scope

This document explains how the Redis client manages connections within Hyperf's coroutine contexts. Context management enables stateful Redis operations such as transactions (multi), pipelining (pipeline), and database selection (select) by ensuring that related commands execute on the same connection. This is critical for maintaining operation atomicity and consistency in a coroutine-based environment where multiple coroutines may be running concurrently.

For information about the overall Redis client architecture, see Redis Client. For details on basic command execution without stateful operations, see Basic Operations.

Sources: src/Redis.php1-143


Why Context Management is Required

In traditional synchronous PHP applications, a single Redis connection persists throughout a request's lifecycle. However, in Hyperf's coroutine-based architecture, connection pooling is used for efficiency—connections are acquired from a pool, used for a command, and immediately released back to the pool.

This creates a problem for stateful Redis operations:

OperationWhy Same Connection is Required
multi / execTransaction commands must execute on the same connection to maintain atomicity. After multi, all subsequent commands until exec must use the same connection.
pipelinePipelined commands are batched together on a single connection to reduce network round-trips. Commands in the pipeline must use the same connection.
selectDatabase selection changes the active database on a specific connection. Subsequent commands must use the same connection to operate on the selected database.

The context management system solves this by storing connections in the coroutine context—a thread-local storage mechanism that is isolated per coroutine. This ensures that stateful operations within a coroutine reuse the same connection without affecting other coroutines.

Sources: src/Redis.php97-104


Context Key Structure

Each connection stored in the coroutine context is identified by a unique key constructed from the pool name:

redis.connection.{poolName}

The getContextKey() method generates this key:

src/Redis.php128-131

For example:

  • Default pool: redis.connection.default
  • Cache pool: redis.connection.cache
  • Session pool: redis.connection.session

This naming scheme ensures that different Redis pools maintain separate connection contexts, preventing cross-pool contamination.

Sources: src/Redis.php128-131 src/Redis.php19


Connection Retrieval Flow

Diagram: Connection Acquisition Logic


The getConnection() method implements this two-stage retrieval:

src/Redis.php109-123

  1. Check Context First: If hasContextConnection is true, retrieve the connection from Context::get($this->getContextKey())
  2. Fallback to Pool: Otherwise, acquire a fresh connection from the pool via $this->factory->getPool($this->poolName)->get()
  3. Validation: Ensure the connection is a RedisConnection instance
  4. Enable Transformation: Call shouldTransform(true) to enable Laravel compatibility transformations

Sources: src/Redis.php28-29 src/Redis.php109-123


Stateful Command Detection

Diagram: Command Routing Based on State Requirements


The shouldUseSameConnection() method defines which commands require context storage:

src/Redis.php97-104

CommandPurposeWhy Context Storage is Needed
multiBegin transactionSubsequent commands until exec must execute on same connection
pipelineBegin pipelineBatched commands must be sent on same connection
selectSwitch databaseDatabase selection is connection-specific state

Sources: src/Redis.php97-104


Connection Lifecycle in Context

Diagram: Full Lifecycle with Context Management


The lifecycle consists of four phases:

Phase 1: Initial Stateful Command

src/Redis.php55-65

When a stateful command executes successfully:

  1. The connection is stored in context via Context::set($this->getContextKey(), $connection)
  2. For select commands, the database number is tracked: $connection->setDatabase((int) $db)
  3. A deferred cleanup is registered: defer(function () { $this->releaseContextConnection(); })

Phase 2: Subsequent Commands

src/Redis.php28-29 src/Redis.php55-56

Subsequent commands in the same coroutine:

  1. $hasContextConnection is true
  2. Connection is retrieved from context instead of pool
  3. Connection is NOT released after command execution (line 55-56 comments)

Phase 3: Defer Execution

src/Redis.php63-65

The defer() function registers a callback that executes when the coroutine ends. This ensures cleanup even if exceptions occur or early returns happen.

Phase 4: Context Cleanup

src/Redis.php82-91

The releaseContextConnection() method:

  1. Retrieves the connection from context
  2. Removes it from context by setting to null
  3. Calls $connection->release() to return it to the pool

Sources: src/Redis.php28-29 src/Redis.php55-69 src/Redis.php82-91


Special Handling for Select Command

The select command receives special treatment because it changes connection state that must be tracked:

src/Redis.php59-61

When select is executed:

  1. The database number is extracted from arguments: $db = $arguments[0]
  2. It's stored on the connection object: $connection->setDatabase((int) $db)
  3. The connection is stored in context like other stateful commands

This tracking allows the connection to maintain awareness of which database it's currently using, which is important for connection pooling and reuse semantics.

Sources: src/Redis.php59-61


Error Handling and Context Cleanup

Diagram: Exception Handling Flow


The finally block ensures proper connection handling regardless of success or failure:

src/Redis.php55-70

ConditionActionRationale
$hasContextConnection === trueNo action (connection stays in context)Connection was already stored before this command, don't release it yet
$exception === null && shouldUseSameConnection($name)Store in context + defer cleanupSuccessful stateful command, store for reuse
Otherwise$connection->release()Normal command or failed stateful command, release immediately

After cleanup logic, exceptions are re-thrown:

src/Redis.php72-74

This ensures that:

  1. Connections are never leaked
  2. Context is maintained for successful stateful operations
  3. Failed stateful operations don't pollute the context
  4. Exceptions propagate to the caller as expected

Sources: src/Redis.php35-77


Usage Example

Transaction with Context Management


Database Selection with Context Management


Concurrent Coroutines with Isolated Contexts


Sources: src/Redis.php1-143


Implementation Details

Context Storage Mechanism

The context storage relies on Hyperf's Context class:

src/Redis.php11

The Context class provides coroutine-local storage, ensuring that:

  • Each coroutine has its own isolated context
  • Context data is automatically cleaned up when a coroutine ends
  • Context access is thread-safe in the coroutine model

Key Methods Summary

MethodLocationPurpose
getContextKey()src/Redis.php128-131Generate unique context key from pool name
getConnection()src/Redis.php109-123Retrieve connection from context or pool
shouldUseSameConnection()src/Redis.php97-104Determine if command requires context storage
releaseContextConnection()src/Redis.php82-91Clean up connection from context

Defer Mechanism

The defer() function is a Hyperf/Swoole feature that registers a callback to execute when the current coroutine exits:

src/Redis.php63-65

Key characteristics:

  • Executes regardless of how the coroutine exits (normal return, exception, etc.)
  • Multiple defer callbacks execute in LIFO order (last registered, first executed)
  • Ensures cleanup even if application code doesn't explicitly release connections

Sources: src/Redis.php63-65 src/Redis.php82-91


Summary Table: Context Management Decision Tree

ScenarioContext CheckActionResult
First multi commandNo connection in contextGet from pool → Store in context → Defer cleanupConnection stored for transaction
Second command in transactionConnection exists in contextReuse from context → Skip releaseSame connection used
exec commandConnection exists in contextReuse from context → Skip releaseTransaction completes on same connection
Coroutine endsDefer callback executesRemove from context → Release to poolConnection returned
Non-stateful commandNo connection in contextGet from pool → Release immediatelyNo context storage
Failed multi commandNo connection in contextGet from pool → Release immediatelyConnection not stored

Sources: src/Redis.php28-29 src/Redis.php55-69 src/Redis.php82-91

Refresh this wiki

On this page