Implementing Pyvorin in Microservices
Compile CPU-bound services at build time, ship the artefacts in the image, and skip the compiler sidecar. Which service shapes benefit, which do not, and why.
Published Jun 9, 2026
A microservice estate usually has a handful of services whose cost is dominated by one CPU-bound Python loop, and a long tail of services that wait on sockets. Pyvorin Native 1.0.9 is for the first group. The working pattern is unglamorous: compile the hot service locally during image build, ship the compiled artefacts inside the container image, and let every replica start warm with no compiler in the request path and no source leaving the build pipeline. This page sets out that pattern, names the service shapes it suits and the ones it does not, and explains why the compiler-sidecar design keeps resurfacing in architecture reviews — and keeps failing.
One number to hold throughout: across the canonical 71-workload benchmark run, the suite median speedup was 1.35x and seventeen workloads ran slower than CPython. Compilation is a targeted instrument, not an estate-wide upgrade. The sections below show how to aim it.
The pattern: compile at build, run warm
Compilation is local and in-process. A build step runs python -m pyvorin compile against the service's own files, which writes native shared objects into <site-packages>/.pyvorin_cache/disk_compile, indexed by cache_index.json. Each entry is keyed by a SHA256 hash of the source, the function name, the compilation options and the modification times of the runtime and compiler; entries are checksum-validated, and the cache holds up to 500 of them with least-recently-used eviction. Copying that directory into the image alongside the package makes every container warm from its first call. There is no separate export step because there is no separate artefact format — the disk cache is the deployment unit.
Two constraints follow from the keying scheme. First, the artefacts are tied to the exact package build and compiler flags that produced them, so the image must carry the same package version and options as the build host. Pin the Pyvorin version in the dependency manifest and do not let a floating resolve change it between compile and runtime. Second, application source must be byte-identical between build and run; an edit on either side silently invalidates the entry and the function recompiles. In a container that is harmless but slow, so the rule is simple: compile as the last build step, after the source is final.
Which services benefit
The service shapes that benefit share one property: their request latency or throughput is dominated by interpreted Python execution over data, not by waiting. The suite evidence, from the canonical 2026-09-13 run:
| Service shape | Suite evidence | Expectation |
|---|---|---|
| Data transformation / ETL | etl category geomean 4.62x; best result 134.62x (windowed aggregate) | Strong where loops dominate |
| Scoring and feature computation | ml category 3.46x; financial calculations 13.53x | Good fit |
| Numerical services | numerical category 10.91x geomean across seven workloads | Strong |
| Object-heavy business logic | object category 42.28x geomean across two workloads | Strongest, on limited evidence |
Treat these as maps of where to look, not promises. The etl category contains a regression too — a merge-join workload at 0.64x — because its cost lives in library calls and allocation rather than interpretation. The deciding datum is a benchmark of the actual service on the actual hardware, produced with the speed-proof workflow and gated in CI. Anything less is a guess with a logo on it.
Which services do not
Just as important is the do-not-compile list. The suite's losing categories are the archetypes of I/O-facing microservices: web request handling (category geomean 0.97x, worst 0.38x), string manipulation (0.85x), parsing (0.86x) and compression (0.19x). That code spends its time inside C library routines or building and discarding small objects, where bytecode dispatch was never the bottleneck and the compiled path's fixed overheads can show through. An API gateway, a cookie-and-header shim, a proxy that mostly shuttles bytes — leave these on plain CPython. The honest position is that Pyvorin will not help them and may cost a few percent.
A build pipeline that works
The release pipeline for a candidate service has three commands, all run locally in CI:
# 1. See which functions the compiler can take
python -m pyvorin support services/pricing/transform.py
# 2. Compile; fail the build if anything falls back
python -m pyvorin compile services/pricing/transform.py --fail-on-fallback
# 3. Measure against CPython on the same host, same inputs
python -m pyvorin run services/pricing/transform.py --function run --compare --runs 5
The --fail-on-fallback flag turns compilation into a release gate: if any function declines, the build fails and a human looks at why, rather than shipping an image that silently runs the interpreter. The --compare run executes the same warm-up and timed loop under both CPython and Pyvorin and reports the ratio, which is the number your capacity plan should cite — not a suite figure. For machine-readable output in CI, both compile --json and run --json exist, and bench offers the same flags plus OpenMetrics output for dashboards. The cache-key scheme for shipping the populated cache between CI and image build is documented on the CI artefact caching page, and the full pre-compilation and image-warming procedure on the warm-up and caching strategies page.
Runtime behaviour across replicas
Inside the running service, compilation is invisible to callers. Each function either runs natively or runs as its original Python through the recorded fallback path; one function declining does not affect its neighbours, and nothing crashes. For a fleet this property matters more than any single speedup: an unexpected dependency or an unsupported construct degrades that function to interpreter speed, and the fallback log says so, rather than producing a 2 a.m. incident. The fallback mechanics are documented on the unsupported code and fallback page. The same page covers the strict-mode option for teams that prefer a compile error to a silent interpreter path outside the build gate.
One operational habit worth adopting early: scrape the fallback log. It is the earliest signal that a dependency upgrade changed what the compiler can take, and it turns the fallback from an invisible degradation into a metric with an alert attached. Teams that do this catch coverage regressions in the release after the upgrade; teams that do not find them in a profiling session a quarter later, usually while investigating a capacity anomaly.
The sidecar temptation, and why it fails
Every architecture review we have sat in on this topic eventually produces the same sketch: a "compile service" deployed next to the application containers, so that hot code can be sent off for compilation on demand. It is an appealing drawing. It is also wrong for this product, for four reasons that compound.
First, there is nothing to wrap. The compiler is a local, in-process library call with no network surface; a sidecar would invent an RPC layer around a function call and then own that layer's availability. Second, version skew becomes a distributed-systems problem: the sidecar and every client must agree on package version and compiler flags or cache keys miss and functions recompile anyway — the failure mode is silent and fleet-wide. Third, the licence model counts devices, and a sidecar multiplies licence surfaces: activation binds to a device fingerprint derived from hostname, user and machine identity, so every sidecar-client pair is a new activation to manage with zero added capability. Fourth, it smuggles compilation back into the operational path through a rear door, along with the tail latency that the build-time pattern exists to avoid.
The judgement call, stated plainly: we have seen this design proposed in three separate platform migrations and cut in all three. The build-time pattern costs one CI step and zero moving parts at runtime. Keep it.
Licensing across a fleet of containers
Activation is pyvorin activate <KEY>, which makes a single POST to api.pyvorin.com carrying the key and the device fingerprint — no source, no code payloads. After activation, a signed lease validates locally, and if the lease lapses offline grace runs to 24 hours on demo and trial tiers, 72 hours on basic and professional, and 90 days on enterprise, extendable with a server-issued offline permit. The operational consequence for containers: each container presents its own hostname and machine identity, so plan activations per deployed unit and activate at container start, not during image build. Baking an activation into the image both bakes a secret into a layer and binds the licence to a device identity that no longer exists at runtime. The full activation and entitlement semantics are on the activation and entitlement page, and the complete endpoint list — two URLs in total — is on the network requirements page.
Where to go next
- Warm-up and caching strategies — pre-compilation and shipping a warm cache inside an image.
- How to cache artefacts in CI — the cache-key scheme for reuse between pipeline and image build.
- How to run a speed proof — produce the measured number your capacity plan should cite.
- Unsupported code and the fallback path — what a declined function does at runtime, in detail.
Last reviewed 9 June 2026 against pyvorin-native 1.0.9 installed at /root/pvfinal (verified build of 12 September 2026). Category and workload figures are from the canonical 2026-09-13 benchmark artefact. All CLI commands on this page were run against the installed product. No remote compilation capability exists in 1.0.9 and none is claimed here.