Revised sarcastic summary with hashtags
The Small Resource Server is a tiny, lightweight, lightweight microservice designed for handling resource locking and synchronization efficiently in high-load microservices environments. By centralizing resource access, it prevents race conditions, inconsistent state, and high contention. This allows long multi-processes and multi-server batches synchronization, ensuring critical operations like payments, reservations, and inventory updates remain reliable even under heavy parallel traffic. Additionally, it provides a language-agnostic HTTP API that allows developers to acquire, share, and release named locks with optional TTL, owner tokens, and expiration dates. The server runs on PHP with Swoole, a high-performance, event-driven, and concurrent server microkernel, offering a minimal REST API with the following features:
- Resource acquisition: Acquiring a named lock with optional TTL and owner token.
- Resource sharing: Sharing associated data through the
Symfony Client Bundle
with ergonomic, resilient access.
Architecture (high-level)
The Small Resource Server consists of a runtime layer that uses an in-memory map to store the lock data, including the owner, token, acquired
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 toacquire()
,renew()
, andrelease()
- Optional retry/backoff on transient errors and clock skew tolerance
- Registers a named HTTP client preconfigured with
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... }
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
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
Unlock resource
POST /resource/{resourceName}/{selector}/unlock
Headers:
x-api-key: <READ+LOCK>
x-ticket: <ticket>
200 OK
{ "unlocked": true }
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
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"]
And config file example:
MYSQL_HOST=database
MYSQL_USER=root
MYSQL_PASSWORD=secret
RESOURCE_READ=d8e8fca2dc0f896fd7cb4cb0031ba249
RESOURCE_READ_LOCK=ed4779d230afc18ca8df5213ba02b11e
RESOURCE_WRITE=eadd7e1bbc06f288417df096bbedfa61
Symfony Client Bundle
Add the bundle to your app:
composer require small/swoole-resource-client-bundle
Register the bundle if Flex is not used:
return [
Small\SwooleResourceClientBundle\SmallSwooleResourceClientBundle::class => ['all' => true],
];
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
Environment:
###> Small Resource Server ###
RESOURCE_SERVER_BASE_URI=http://localhost:8080
RESOURCE_SERVER_API_KEY=eadd7e1bbc06f288417df096bbedfa61
###< Small Resource Server ###
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();
}
}
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
- resource server : https://github.com/sebk69/small-resource-server.git
- Symfony client bundle : https://github.com/ebk69/small-swoole-resource-client-bundle.git
On Docker Hub (server)
On Packagist (Symfony client bundle)