Concurrency Architecture in Lioran S3
Rust does not automatically make concurrent infrastructure correct.
It gives you better tools for making ownership and synchronization explicit.
Lioran S3 uses a relatively clear set of primitives:
Arc
dyn Trait
Tokio async I/O
Semaphore
Mutex
Atomics
I’m Swaraj Puppalwar, Founder & CTO of Lioran Group and Lioran Developer Solutions. This article looks at how those pieces fit together.
Shared server state
The server has a cloneable ServerState.
Major components are stored behind Arc:
pub metadata: Arc<dyn MetadataStore>,
pub object_engine: Arc<dyn ObjectEngine>,
pub config: Arc<ServerConfig>,
pub image_optimizer: Arc<ImageOptimizer>,
pub video_upload_manager: Arc<MultipartUploadManager>,
pub video_pipeline: Arc<VideoPipeline>,
pub video_worker_pool: Arc<VideoWorkerPool>,
pub metrics: Arc<ServerMetrics>,
Cloning request state therefore does not clone the database or object engine.
It clones reference-counted handles.
Trait boundaries
The server depends on:
dyn MetadataStore
dyn ObjectEngine
rather than hard-wiring every caller to one concrete implementation.
This separates:
HTTP/server orchestration
from:
physical object implementation
metadata implementation
That is useful for testing and future engine changes.
Async file I/O
Normal object paths use Tokio:
tokio::fs::File
AsyncRead
AsyncReadExt
AsyncWriteExt
The request does not need to synchronously block a worker thread while waiting for every network read.
Backpressure
Async I/O does not mean “accept infinite work”.
Multipart explicitly adds a global semaphore:
global_limiter: Arc<Semaphore>
Each active part upload must acquire a permit.
That converts unlimited demand into bounded active work plus waiting.
This is backpressure.
Why backpressure matters
Suppose each active part uses:
128 KiB explicit stream buffer
and there are 64 active part streams.
The explicit copy buffers are roughly:
64 × 128 KiB = 8 MiB
before accounting for HTTP buffers, Tokio, filesystem state, metadata, TLS, and other allocations.
Without a concurrency ceiling, memory and descriptor usage can grow with incoming demand.
Mutex where state converges
Multipart also owns:
session_update_lock: Arc<Mutex<()>>
Parts may stream concurrently, but they eventually converge on shared upload-session state.
That is a different problem from bandwidth concurrency.
The engine can keep expensive data transfer parallel while serializing the small critical section that updates shared session metadata.
Atomic shutdown state
The server uses:
Arc<AtomicBool>
for the graceful shutdown flag.
Checking whether shutdown has begun does not require a mutex around a boolean.
The implementation uses sequentially consistent ordering for this flag.
Avoid one global lock
The architecture document explicitly warns against global locks on hot paths.
That is important in storage systems because one innocent global mutex can turn:
64 concurrent requests
into:
1 request doing work
63 requests admiring the mutex
Shared metadata store
Arc<dyn MetadataStore> allows multiple request paths and subsystems to share the embedded metadata engine.
RocksDB itself is configured with background parallelism and an internal shared block cache.
The Rust abstraction does not wrap the entire metadata engine in one application-level global mutex.
Where concurrency is intentionally bounded
Examples include:
- multipart global active-part count
- maximum active upload sessions
- RocksDB background jobs
- media worker behavior
- bounded stream buffers
This is a more useful performance model than simply spawning tasks until the host files a resignation letter.
The design principle
Concurrency should be:
parallel where work is independent
serialized where state must be coherent
bounded where resources are finite
That is the shape Lioran S3 is moving toward in V1.
Not “async everywhere”.
Not “mutex everything”.
Not “spawn until benchmark graph goes vertical”.
Infrastructure needs controlled concurrency.
Top comments (0)