DEV Community

Cover image for Small Resource Server & Symfony Client Bundle
sebk69
sebk69

Posted on • Edited on

Small Resource Server & Symfony Client Bundle

The Small Resource Server is designed to handle resource locking and synchronization efficiently in high-load microservices environments. By centralizing resource access, it prevents race conditions and inconsistent state across distributed services, while remaining lightweight and fast thanks to its Swoole-based concurrency model. This reduces contention, improves throughput, and ensures that critical operations—like payments, reservations, or inventory updates—remain reliable even under heavy parallel traffic.

It also allowing you a good way for long multi processes / muti servers batches synchronization.

Small Resource Server offers a tiny, language‑agnostic HTTP API for just that:

  • Acquire a named lock with optional TTL & owner token
  • Sharing associated data

The Symfony Client Bundle gives you ergonomic, resilient access from Symfony with

  • HttpClientInterface integration
  • DI configuration (base_uri, API key, timeouts)
  • Straightforward Resource facade/service APIs

Architecture (high level)

  • Small Resource Server

    • Runtime: PHP + Swoole/OpenSwoole HTTP server
    • In‑memory map of resourceName -> lease with optional pluggable storage (Redis/DB) if enabled
    • Lease data: owner, token, acquiredAt, ttl, expiresAt
    • Background janitor to evict expired leases
    • Minimal REST endpoints
  • Client Bundle (Symfony)

    • Registers a named HTTP client preconfigured with base_uri, headers (x-api-key), and default timeouts
    • Exposes a ResourceFactory / ResourceClient service to acquire(), renew(), and release()
    • Optional retry/backoff on transient errors and clock skew tolerance

HTTP API (server)

Create resource

POST /resource Headers: x-api-key: <WRITE key> Body (JSON): { "name": "printer", "timeout": 300 // optional server-side semantics } 201 Created Content-Type: application/json { ...resource... } 
Enter fullscreen mode Exit fullscreen mode

Get resource data (with optional lock)

GET /resource/{resourceName}/{selector}?lock=1 Headers: x-api-key: <READ or READ+LOCK> x-ticket: <existing ticket | optional> Query: lock=1 (default) → attempt to acquire/keep lock lock=0 → read without locking Responses: - 200 OK + JSON body → resource data is available + Header: x-ticket: <ticket> - 202 Accepted → not available yet (locked by someone else), body: { "unavailable": true } + Header: x-ticket: <ticket> (your ticket to retry with) - 404 Not Found → resource/selector unknown - 401 Unauthorized → missing/invalid x-api-key / missing LOCK when lock=1 
Enter fullscreen mode Exit fullscreen mode

Update resource data

PUT /resource/{resourceName}/{selector} Headers: x-api-key: <WRITE key> x-ticket: <ticket from GET> Body: raw JSON payload to store 204 No Content 
Enter fullscreen mode Exit fullscreen mode

Unlock resource

POST /resource/{resourceName}/{selector}/unlock Headers: x-api-key: <READ+LOCK> x-ticket: <ticket> 200 OK { "unlocked": true } 
Enter fullscreen mode Exit fullscreen mode

Security: Accept an x-api-key header; prefer TLS; rate‑limit abusive clients.


📦 Installation

Server (Docker Hub image)

The easiest way to run the server is via Docker:

# Pull the latest image from Docker Hub docker pull smallphp/small-resource-server:latest # Run it (adjust API_KEY and port as needed) docker run -d \ --name resource-server \ --volume "./conf/small-swoole-resource-server.conf:/etc/small-swoole-resource-server.conf" \ -p 8080:9501 \ smallphp/small-resource-server:latest 
Enter fullscreen mode Exit fullscreen mode

You can now access the server on http://localhost:8080.

Docker Compose example:

version: '3.8' services: resource-server: image: smallphp/small-resource-server:latest restart: unless-stopped ports: - "8080:9501" volumes: - ./conf/small-swoole-resource-server.conf:/etc/small-swoole-resource-server.conf database: container_name: small-swoole-resource-db image: mysql:8 environment: MYSQL_ROOT_PASSWORD: secret command: ["mysqld", "--mysql-native-password=FORCE"] 
Enter fullscreen mode Exit fullscreen mode

And config file example:

MYSQL_HOST=database MYSQL_USER=root MYSQL_PASSWORD=secret RESOURCE_READ=d8e8fca2dc0f896fd7cb4cb0031ba249 RESOURCE_READ_LOCK=ed4779d230afc18ca8df5213ba02b11e RESOURCE_WRITE=eadd7e1bbc06f288417df096bbedfa61 
Enter fullscreen mode Exit fullscreen mode

Symfony Client Bundle

Add the bundle to your app:

composer require small/swoole-resource-client-bundle 
Enter fullscreen mode Exit fullscreen mode

Register the bundle if Flex is not used:

return [ Small\SwooleResourceClientBundle\SmallSwooleResourceClientBundle::class => ['all' => true], ]; 
Enter fullscreen mode Exit fullscreen mode

Configure the client:

# config/packages/small_resource_client.yaml small_swoole_resource_client: base_uri: '%env(RESOURCE_SERVER_BASE_URI)%' api_key: '%env(RESOURCE_SERVER_API_KEY)%' timeout: 10 
Enter fullscreen mode Exit fullscreen mode

Environment:

###> Small Resource Server ### RESOURCE_SERVER_BASE_URI=http://localhost:8080 RESOURCE_SERVER_API_KEY=eadd7e1bbc06f288417df096bbedfa61 ###< Small Resource Server ### 
Enter fullscreen mode Exit fullscreen mode

Quick start from Symfony

$lock = $this->resources->create('shop:42:inventory'); if ($lock->acquire('sync-worker', 30)) { $guard = $lock->autoRenew(every: 20, ttl: 30); try { $this->syncInventory(); } finally { $guard->stop(); $lock->release(); } } 
Enter fullscreen mode Exit fullscreen mode

Error handling & edge cases

  • 409 Conflict → someone else holds the lock
  • 403 Forbidden → token mismatch/expired
  • Timeouts → handle with retry/backoff
  • Crash safety → expired leases are automatically evicted

Security

  • All traffic over HTTPS in production
  • Protect with API keys (env var API_KEY)
  • Rotate secrets regularly

sources

On github

On Docker Hub (server)

On Packagist (Symfony client bundle)

Top comments (0)