Service Architect is a visual designer and code generator for distributed systems. You draw a graph of services, typed pipelines, and connections; the designer generates the C++ workspace, userver components, transport adapters, configuration, observability wiring, and runtime graph.
This repository is the C++20 runtime used by that generated code. It preserves the observable stream, cancellation, lifecycle, configuration, and connector semantics of the Go reference implementation while using userver coroutines and transports.
The graph is the architecture. It runs the system and remains accurate by definition.
ServiceLib is normally consumed through generated projects rather than assembled by hand.
- Strongly typed stream graphs generated from the Service Architect model
- C++20 interfaces with userver coroutine-based execution
- Function-call, task-pool, priority-pool, and parallel call semantics
- HTTP, gRPC, Kafka, and local in-process connectors
- Deadlines, cancellation propagation, delay pools, and coordinated shutdown
- In-memory join state with rotating retention maps
- userver-backed metrics, structured logging, tracing, and status endpoints
- Layered YAML configuration with environment substitution and hot reload
Graph wiring and immutable runtime relationships are established once during startup. Request processing uses pre-resolved consumers and callers rather than rebuilding or looking up graph edges on the hot path.
| Semantics | Behavior |
|---|---|
FunctionCall |
Invoke the next consumer directly; optionally dispatch asynchronously |
TaskPool |
Schedule work on a named bounded task pool |
PriorityTaskPool |
Schedule work using request priority |
Parallel |
Dispatch independently through userver's task processor |
The runtime keeps payload ownership explicit, propagates cancellation and deadlines across supported transports, and executes each delay callback exactly once whether its timer or cancellation wins.
| Operator | Purpose |
|---|---|
Input |
Receive data from an external system |
Sink |
Send data to an external system |
Map / Filter |
Transform or conditionally pass values |
FlatMap / FlatMapIterable |
Expand a value into multiple outputs |
Process / Error |
Produce normal and error output streams |
KeyBy |
Partition values by key |
Join / MultiJoin |
Combine keyed streams |
Merge / Split |
Merge or fan out stream branches |
Case / When |
Route values conditionally |
Delay |
Deliver later or immediately on cancellation |
CycleLink |
Feed values back into the graph |
Sources — HTTP · gRPC (unary and streaming) · Kafka · local/custom
Sinks — HTTP · gRPC (unary and streaming) · Kafka · local/custom
HTTP and gRPC propagate stream identity, trace context, baggage, and remaining deadlines where supported. Process-local scheduling metadata, such as priority, stays local.
Configuration is assembled from generated defaults, a base YAML document, an overrides YAML document, and environment substitutions. The C++ loader uses userver's periodic file watcher rather than filesystem notifications.
Reload is transactional: parsing, generated-config adaptation, runtime indexing, validation, and the service callback must succeed before the new snapshot is published. Failed candidates are logged and counted while the last valid configuration remains active. Task-pool settings can be updated without rebuilding the graph.
The public runtime interfaces are backend-independent:
servicelib::metrics::Metricsservicelib::log::Loggerservicelib::tracing::Tracing
Production services use adapters from servicelib/runtime/telemetry/userver: metrics are registered in userver's statistics storage and exposed in Prometheus format, logs use userver's structured logger, and spans use userver tracing. Registering userver's OTLP logger component exports logs and traces through OTLP.
Request tracing is enabled by a non-empty X-Trace/x-trace marker or an already sampled W3C remote parent. Without either signal, ServiceLib avoids creating recording request, operator, pool, source, or sink spans, even if userver has an ambient transport span.
Tests can inject TestMetrics, TestLog, and TestTracing. No-op implementations remain available when observability is intentionally disabled.
This repository contains the runtime framework:
- public headers in
include/servicelib - framework tests in
tests - task-pool benchmarks in
benchmarks - CMake package metadata in
cmake
userver is an external dependency, not a submodule. Generated services pin and build the userver version they require.
When userver targets already exist:
add_subdirectory(path/to/cppservicelib)
target_link_libraries(my_service PRIVATE
servicelib::servicelib
userver-core)An installed package can be consumed with:
find_package(servicelib CONFIG REQUIRED)
find_package(userver CONFIG REQUIRED COMPONENTS core)
target_link_libraries(my_service PRIVATE
servicelib::servicelib
userver::core)Tests require userver core, gRPC, Kafka, and utest targets. The reproducible Docker-first command accepts userver as an independent BuildKit context:
USERVER_SOURCE_CONTEXT=../userver ./scripts/test.shSet SERVICELIB_BUILD_BENCHMARKS=ON to build the task-pool benchmark.
| Project | Description |
|---|---|
| Service Architect | Visual graph designer and code generator |
| Go runtime | Semantic reference implementation |
| C++ example | Generated, runnable C++ services |
| Service generator | Cross-language code generator |
Consumers depend on immutable semantic-version tags. A release tag must point to the exact runtime revision selected by servicegen; generated projects must not track main.
BSD-3-Clause. See LICENSE.
| Website | gorundebug.com |
| serlex777@gmail.com | |
| Telegram | t.me/+31qMliw-DeI3M2M6 |
