VOOZH about

URL: https://deepwiki.com/stefanak-michal/php-bolt-driver/4.2-persistent-connections-with-pstreamsocket

⇱ Persistent Connections with PStreamSocket | stefanak-michal/php-bolt-driver | DeepWiki


Loading...
Last indexed: 14 February 2026 (a283bd)
Menu

Persistent Connections with PStreamSocket

Purpose and Scope

This document explains the persistent connection implementation provided by the PStreamSocket class, which extends StreamSocket to maintain long-lived TCP connections across multiple PHP requests. Persistent connections optimize performance by caching negotiated protocol versions and reusing existing connections, avoiding repeated handshake overhead.

For general connection management concepts, see Connection Management. For SSL/TLS configuration specifics, see SSL/TLS Configuration and Security. For timeout and error handling, see Timeout Configuration and Error Handling.

Sources: src/Bolt.php1-262


Architecture Overview

The PStreamSocket class sits at the top of the connection hierarchy, extending StreamSocket which in turn extends AConnection. It leverages PHP's persistent stream functionality combined with a PSR-16 cache layer to store connection metadata.


Sources: src/connection/StreamSocket.php1-145 src/connection/AConnection.php1-61 src/connection/IConnection.php1-42


Connection Lifecycle with Persistence

The persistent connection lifecycle differs significantly from standard connections by attempting to reuse existing connections before falling back to full handshake negotiation.


Sources: src/Bolt.php120-185


Cache-Based Protocol Version Storage

The PStreamSocket implementation uses the PSR-16 cache interface to store negotiated protocol versions, eliminating redundant handshakes on subsequent connections.

Cache Key Generation

Each persistent connection has a unique identifier returned by PStreamSocket.getIdentifier(), which serves as the cache key. This identifier distinguishes connections with different configurations (IP, port, SSL settings).

Cache Write Operation

After successful connection establishment (either through cached reuse or new handshake), the protocol version is stored:

src/Bolt.php138-140

if ($this->connection instanceof \Bolt\connection\PStreamSocket) {
 CacheProvider::get()->set($this->connection->getIdentifier(), $protocol->getVersion(), strtotime('+15 minutes'));
}

Cache Read Operation

When building a persistent connection, the cached version is retrieved:

src/Bolt.php161-165

if ($this->connection instanceof \Bolt\connection\PStreamSocket) {
 $version = CacheProvider::get()->get($this->connection->getIdentifier());
 if (empty($version)) {
 return null;
 }
 ...
}

Sources: src/Bolt.php138-143 src/Bolt.php159-185


Reset Validation

When reusing a persistent connection, the library must verify that the connection is still valid and the server is responsive. This is accomplished through the RESET message.


The critical validation logic:

src/Bolt.php176-180

if ($protocol->reset()->getResponse()->signature != Signature::SUCCESS) {
 $this->connection->disconnect();
 $this->connection->connect();
 return null;
}

Sources: src/Bolt.php174-180


ServerState Management for Persistent Connections

The INTERRUPTED server state is specifically used for persistent connections that are being revalidated:

src/Bolt.php174

$protocol->serverState = ServerState::INTERRUPTED;

This state indicates that:

  • The protocol instance exists but has not been authenticated
  • The connection may have active but unknown state
  • A RESET is required before normal operations
  • The server state will transition to a valid operational state upon successful RESET
Server StateContextNext Possible States
INTERRUPTEDPersistent connection being validatedREADY (reset success), DEFUNCT (reset failure)
NEGOTIATIONNew connection in V5.1+READY (after logon)
CONNECTEDNew connection in V1-V5.0READY (after hello/init)

Sources: src/Bolt.php174 src/protocol/AProtocol.php29


TTL and Expiration

Cached protocol versions have a 15-minute time-to-live (TTL) to balance performance with freshness:


The 15-minute duration is specified using PHP's strtotime():

src/Bolt.php139

CacheProvider::get()->set($this->connection->getIdentifier(), $protocol->getVersion(), strtotime('+15 minutes'));

The FileCache implementation checks expiration automatically in its has() method:

src/helpers/FileCache.php177-183

if (
 file_exists($this->tempDir . '.ttl' . DIRECTORY_SEPARATOR . $key)
 && intval(file_get_contents($this->tempDir . '.ttl' . DIRECTORY_SEPARATOR . $key)) < time()
) {
 @unlink($this->tempDir . '.ttl' . DIRECTORY_SEPARATOR . $key);
 @unlink($this->tempDir . $key);
}

Sources: src/Bolt.php139 src/helpers/FileCache.php172-186


FileCache Implementation Details

The FileCache class provides the PSR-16 Simple Cache implementation used by persistent connections. It stores cache entries as serialized files in the system temporary directory.

Directory Structure

/tmp/php-bolt-filecache/
├── {identifier} # Serialized protocol version
├── {identifier2} # Another connection's version
├── .ttl/
│ ├── {identifier} # TTL timestamp for identifier
│ └── {identifier2} # TTL timestamp for identifier2

File Operations

OperationFile PathContents
Cache version/tmp/php-bolt-filecache/{identifier}serialize($version)
Store TTL/tmp/php-bolt-filecache/.ttl/{identifier}Unix timestamp
Check expirationRead TTL fileCompare with time()

Locking Mechanism

The FileCache provides optional file locking for concurrent access:

src/helpers/FileCache.php191-199

public function lock(string $key): bool
{
 $this->validateKey($key);
 $handle = @fopen($this->tempDir . $key, 'c+');
 if ($handle === false) return false;
 $this->handles[$key] = $handle;
 return flock($this->handles[$key], LOCK_EX);
}

This ensures that multiple PHP processes don't corrupt cache entries during simultaneous updates.

Sources: src/helpers/FileCache.php1-219


Fallback Behavior

The persistent connection implementation is designed with robust fallback mechanisms to ensure reliability even when cache or connection reuse fails.


Fallback Triggers

The system falls back to normal handshake when:

  1. Connection is not persistent: !(connection instanceof PStreamSocket) src/Bolt.php161
  2. No cached version: empty($version) src/Bolt.php163-165
  3. Protocol class doesn't exist: !class_exists($protocolClass) src/Bolt.php168
  4. Reset validation fails: signature != Signature::SUCCESS src/Bolt.php176

Error Handling Flow

src/Bolt.php124-136

try {
 if (!$this->connection->connect()) {
 throw new ConnectException('Connection failed');
 }
 $protocol = $this->persistentBuild();
 if (empty($protocol)) {
 $protocol = $this->normalBuild();
 }
} catch (BoltException $e) {
 $this->connection->disconnect();
 throw $e;
}

Sources: src/Bolt.php120-143 src/Bolt.php159-185


Performance Characteristics

Persistent connections provide significant performance benefits by eliminating handshake overhead:

Connection TypeOperations per RequestTypical Overhead
StandardTCP connect + Handshake (4 bytes × 2) + Auth~50-100ms
Persistent (cached)Reset (1 message × 2)~5-10ms
Persistent (expired)TCP connect + Handshake + Auth~50-100ms

Handshake Overhead Elimination

The normal handshake involves:

  1. Sending protocol version candidates (16 bytes)
  2. Receiving selected version (4 bytes)
  3. Network round-trip latency

With persistent connections and cached versions:

  1. Skip handshake entirely
  2. Send single RESET message
  3. Receive SUCCESS response
  4. Total: 1 round-trip instead of multiple

Cache Hit Rate Considerations

For optimal performance:

  • High cache hit rate: Applications with sustained activity within 15-minute windows
  • Low cache hit rate: Short-lived scripts or infrequent connections
  • Optimal use case: Web applications with multiple requests per session
  • Suboptimal use case: CLI scripts that run once per hour

Sources: src/Bolt.php205-220 (handshake implementation)


Integration with CacheProvider

The CacheProvider class provides a static accessor to the cache instance used throughout the library:


The cache is initialized once and reused across all connections in the same PHP process. Applications can provide custom PSR-16 implementations to use alternative backends (Redis, Memcached, etc.) for distributed caching scenarios.

Sources: src/Bolt.php42 src/Bolt.php139 src/Bolt.php162 src/protocol/AProtocol.php176


Usage Example

The persistent connection setup is transparent to the application code. Simply instantiate PStreamSocket instead of StreamSocket:


The Bolt.build() method automatically detects PStreamSocket and enables caching behavior without additional configuration.

Sources: src/Bolt.php28 src/Bolt.php120-143