Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Connection Manager Extra Laravel Package

clue/connection-manager-extra

Extra connector decorators for ReactPHP Socket. Wrap ConnectorInterface to add retries, timeouts, delays, rejection rules, swapping, consecutive/random selection, concurrency limits and selective routing—without changing your async connect() code.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require clue/connection-manager-extra
    
  2. Initialize a base connector (using ReactPHP's Socket):
    use React\Socket\Connector;
    use React\EventLoop\Factory;
    
    $loop = Factory::create();
    $connector = new Connector($loop);
    
  3. Wrap it with a decorator (e.g., ConnectionManagerTimeout):
    use ConnectionManager\Extra\ConnectionManagerTimeout;
    
    $timeoutConnector = new ConnectionManagerTimeout($connector, 3.0);
    
  4. Use it in a Laravel async job or event listener:
    $timeoutConnector->connect('example.com:80')->then(
        function ($stream) {
            $stream->write("GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");
            $stream->end();
        },
        function ($exception) {
            Log::error("Connection failed: " . $exception->getMessage());
        }
    );
    

First Use Case: Retry Failed Connections

use ConnectionManager\Extra\ConnectionManagerRepeat;

$retryConnector = new ConnectionManagerRepeat($connector, 3); // 3 total tries
$retryConnector->connect('unreliable-service:80')->then(...);

Implementation Patterns

1. Decorating Connectors for Resilience

Wrap base connectors with decorators to handle edge cases:

// Timeout + Retry
$resilientConnector = new ConnectionManagerRepeat(
    new ConnectionManagerTimeout($connector, 2.0), // 2s timeout
    3 // 3 total tries
);

// Delay + Timeout
$delayedConnector = new ConnectionManagerTimeout(
    new ConnectionManagerDelay($connector, 1.0), // 1s delay
    5.0 // 5s total timeout
);

2. Conditional Routing with Selective

Route connections based on URI patterns (e.g., block ads, delay HTTP):

$rejectAds = new ConnectionManagerReject('Ads blocked');
$delayHttp = new ConnectionManagerDelay($connector, 2.0);
$selective = new ConnectionManagerSelective([
    'ads.example.com' => $rejectAds,
    '*.example.com:80' => $delayHttp, // Delay HTTP only
    '*' => $connector // Default fallback
]);

3. Parallel Connection Attempts

Use Concurrent to try multiple connectors simultaneously:

$primary = new ConnectionManagerTimeout($connector, 1.0);
$fallback = new ConnectionManagerTimeout($fallbackConnector, 3.0);
$concurrent = new ConnectionManagerConcurrent([$primary, $fallback]);

4. Integration with Laravel Queues

Wrap connectors in a Laravel job for async processing:

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;

class AsyncConnectionJob implements ShouldQueue
{
    use Queueable;

    public function handle()
    {
        $this->connectWithRetry();
    }

    protected function connectWithRetry()
    {
        $retryConnector = new ConnectionManagerRepeat($this->connector, 3);
        $retryConnector->connect('api.example.com:443')->then(
            fn($stream) => $this->processStream($stream),
            fn($e) => Log::error($e->getMessage())
        );
    }
}

5. Dynamic Connector Swapping

Use Swappable to change connectors at runtime (e.g., for A/B testing):

$swappable = new ConnectionManagerSwappable($connector);
$swappable->setConnectionManager($newConnector); // Swap dynamically

Gotchas and Tips

Pitfalls

  1. URI Matching in Selective:

    • Wildcards (*) match exact hostnames (e.g., *.youtube.com won’t match www.youtube.com).
    • Fix: Add explicit rules for subdomains:
      $selective = new ConnectionManagerSelective([
          'youtube.com' => $connector,
          '*.youtube.com' => $connector,
          '*' => $fallback
      ]);
      
    • Gotcha: URIs with schemes (e.g., https://example.com) are stripped before matching. Use raw host:port (e.g., example.com:443).
  2. Timeout vs. Delay:

    • ConnectionManagerTimeout sets a hard limit (e.g., fail after 3s).
    • ConnectionManagerDelay adds a pre-connection wait (e.g., delay by 1s).
    • Mistake: Using Delay when you meant Timeout can cause silent hangs.
  3. Loop Dependency:

    • Decorators like Timeout/Delay require a ReactPHP loop if not using the default.
    • Fix: Pass $loop explicitly or rely on the default:
      // Old (explicit loop)
      new ConnectionManagerTimeout($connector, 3.0, $loop);
      
      // New (default loop)
      new ConnectionManagerTimeout($connector, 3.0);
      
  4. Retry Logic:

    • ConnectionManagerRepeat counts total tries (not retries). A value of 3 means:
      • 1 initial attempt + 2 retries.
    • Gotcha: Misconfigured retries can lead to exponential backoff issues.
  5. Concurrent Connections:

    • ConnectionManagerConcurrent fires all connectors at once and resolves on the first success.
    • Warning: Can overwhelm resources if misused. Limit concurrent attempts:
      $concurrent = new ConnectionManagerConcurrent([$conn1, $conn2], 2); // Max 2 parallel
      

Debugging Tips

  1. Log Connection Attempts: Wrap decorators to log activity:

    $debugConnector = new class($connector) implements ConnectorInterface {
        public function connect($uri) {
            Log::debug("Attempting to connect to: {$uri}");
            return $this->connector->connect($uri);
        }
    };
    
  2. Simulate Failures: Use ConnectionManagerReject for testing:

    $testConnector = new ConnectionManagerReject('Simulated failure');
    
  3. Check for Hanging Connections:

    • Use ConnectionManagerTimeout with a short duration (e.g., 0.1) to catch hangs early.
    • Tool: strace or tcpdump to verify if connections are attempted.
  4. Validate URI Patterns:

    • Test Selective rules with edge cases:
      $selective = new ConnectionManagerSelective(['example.com:80-81' => $connector]);
      $selective->connect('example.com:80'); // Should match
      $selective->connect('example.com:82'); // Should fail
      

Extension Points

  1. Custom Rejection Logic: Extend ConnectionManagerReject to add domain-specific rules:

    $customReject = new ConnectionManagerReject(function ($uri) {
        if (str_contains($uri, 'malicious.com')) {
            throw new \RuntimeException("Malicious domain detected");
        }
    });
    
  2. Dynamic Decorator Chaining: Build a DSL for connector composition:

    class ConnectorBuilder {
        public function withTimeout($seconds) { ... }
        public function withRetry($tries) { ... }
        public function build() { ... }
    }
    
  3. Monitor Connection Metrics: Decorate connectors to track success/failure rates:

    $metricsConnector = new class($connector) implements ConnectorInterface {
        private $success = 0;
        private $failures = 0;
    
        public function connect($uri) {
            return $this->connector->connect($uri)
                ->then(fn() => $this->success++, fn() => $this->failures++);
        }
    };
    
  4. Integrate with Laravel’s HTTP Client: Use decorators in custom HttpClient transports:

    $client = new \GuzzleHttp\Client([
        'handler' => new class extends \GuzzleHttp\HandlerStack {
            public function __construct() {
                $this->push(new \GuzzleHttp\Handler\RetryHandler());
                $this->push(new CustomConnectorHandler($retryConnector));
            }
        }
    ]);
    

Config Quirks

  1. Default Loop Behavior:

    • Decorators like Timeout/Delay auto-detect the loop from the wrapped connector.
    • Edge Case: If the base connector uses a custom loop, ensure consistency.
  2. PHP 8+ Compatibility:

    • Uses named arguments and strict types. Ensure your Laravel app targets PHP 8+ for full features.

3

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope