Skip to content

xangcastle/ferrisrbe

Repository files navigation

FerrisRBE Logo

FerrisRBE

A lean, predictable, and blazingly fast Remote Build Execution (RBE) server for Bazel, written in Rust.

CI Status License REAPI Compliant Memory


Why FerrisRBE?

Most RBE solutions are built on the JVM, requiring constant GC tuning and 4GB+ memory just to idle. When your build cache server needs its own dedicated node, something is wrong.

FerrisRBE takes a different approach:

  • Zero GC Pauses: Rust's ownership model eliminates garbage collection. Predictable p99 latencies without JVM tuning.
  • O(1) Memory CAS Streaming: Stream 10GB artifacts with constant ~50MB RAM usage. No more OOM kills during large uploads.
  • 12-Factor by Default: No XML, no YAML, no properties files. Just environment variables that operators already know how to manage.
  • Adaptive Resilience: Workers auto-tune keepalive intervals based on network conditions. Transient failures don't fail builds.

🚀 Quick Start

Option 1: Podman Compose (Full RBE - Local)

Complete RBE stack with workers, cache, BES, and execution on your machine.

git clone https://github.com/xangcastle/ferrisrbe.git
cd ferrisrbe

# Build and load images with Bazel
bazel run //oci:server.amd64.image.load
bazel run //oci:worker.amd64.image.load
bazel run //oci:cache.amd64.image.load
bazel run //oci:bes.amd64.image.load

# Start the stack with Podman Compose
podman-compose -f podman-compose.yml up -d

# Verify the containers are running and healthy
curl -I http://localhost:9092 || echo "Server is up"
curl -I http://localhost:9096 || echo "BES UI is up"

# Configure Bazel
echo 'build:remote --remote_cache=grpc://localhost:9094' >> ~/.bazelrc
echo 'build:remote --remote_upload_local_results=true' >> ~/.bazelrc
echo 'build:remote --remote_executor=grpc://localhost:9092' >> ~/.bazelrc
echo 'build:remote --remote_default_exec_properties=OSFamily=linux' >> ~/.bazelrc
bazel build --config=remote //...

Option 2: Kubernetes (Production)

# Create namespace
kubectl create namespace rbe

# Helm install from local chart
helm install ferrisrbe ./charts/ferrisrbe \
  --namespace rbe --create-namespace

# Or with NodePort for local testing
helm install ferrisrbe ./charts/ferrisrbe \
  --namespace rbe --create-namespace \
  --set server.service.type=NodePort \
  --set server.service.nodePort=30092 \
  --set cache.service.type=NodePort \
  --set cache.service.nodePortGrpc=30094 \
  --set bes.service.type=NodePort \
  --set bes.service.nodePortUi=30096

For advanced deployments requiring extensive configuration, view the direct Helm deployment values.yaml.

Architecture Highlights

FerrisRBE isn't a toy implementation; it's designed to handle the thundering herd of a massive monorepo CI pipeline.

  • Multi-Level Queuing: Fast, medium, and slow queues automatically determined by action size. No more head-of-line blocking. Fast actions (<1s) never wait behind slow ones (>10s).
  • Lock-Free Concurrency: Leveraging DashMap with 64 shards for L1 action cache and in-flight operations, ensuring high throughput without lock contention. Action cache reads in microseconds, not milliseconds.
  • Event-Driven Workers: Eliminates busy-waiting CPU cycles using tokio::sync::Notify. Your cluster's CPU is for building, not polling.
  • Smart Materialization: Automatically degrades from zero-copy hardlinks to standard copies on EXDEV cross-device volume mounts (perfect for containerized executors).
  • Zero-GC Runtime: Rust's ownership model eliminates garbage collection pauses. Predictable p99 latencies under any load.

Cache Architecture (L1/L2)

FerrisRBE implements a tiered caching strategy:

┌─────────────────────────────────────────────────────────────┐
│  L1 Cache: DashMap (in-memory)                              │
│  - 64 shards, lock-free concurrent access                   │
│  - Microsecond-level reads (~50-100μs)                      │
│  - Lost on server restart                                   │
├─────────────────────────────────────────────────────────────┤
│  L2 Cache: rbe-cache DiskActionCache                        │
│  - Persistent across restarts                               │
│  - Shared across server replicas                            │
│  - Millisecond-level reads (~1-3ms)                         │
├─────────────────────────────────────────────────────────────┤
│  L3 Storage: CAS (rbe-cache)                                │
│  - Persistent blob storage                                  │
│  - Gigabyte-scale artifacts                                 │
└─────────────────────────────────────────────────────────────┘

Current Status: L1 (DashMap) is implemented and provides exceptional performance for action cache hits. L2 integration is planned to provide persistence and horizontal scalability.

📊 Benchmarks

The following metrics were obtained through reproducible comparative benchmarks against other RBE solutions (Buildfarm, Buildbarn, BuildBuddy). Benchmark scripts are available in benchmark/.

Memory Footprint Comparison (Idle State)

Solution Language Idle Memory Relative to FerrisRBE GC Pauses
FerrisRBE 🦀 Rust 6.7 MB 1x (baseline) None
Buildbarn Go ~120-200 MB ~18-30x Minimal
Buildfarm Java ~800-1200 MB ~120-180x Yes (G1GC) ⚠️
BuildBuddy Java/Go ~1.2-2 GB ~180-300x Yes (JVM) ⚠️

Note: FerrisRBE is 120-300x more memory efficient than JVM-based solutions, and 18-30x more efficient than Go.

Real-World Impact: Resource Costs

For a 20-node RBE cluster:

Solution Total Memory (Idle) Est. Monthly Cost*
FerrisRBE ~134 MB $50-100
Buildbarn ~2.4-4 GB $150-250
Buildfarm ~16-24 GB $800-1,200
BuildBuddy ~24-40 GB $1,200-2,000

*AWS/GCP estimate for instances required to support baseline memory.

Resource Footprint - FerrisRBE Details

Component Memory (Idle) Memory (Peak) CPU (Idle)
Server ~5-10 MB ~50-150 MB ~0.01 cores
Worker ~10-15 MB ~100-200 MB ~0.01 cores
Total ~15-25 MB ~150-350 MB ~0.02 cores

Compare to Java-based alternatives that idle at 500MB+ and spike to 4GB+ during GC.

Why Zero-GC Matters

Metric FerrisRBE JVM Solutions
p50 Latency 12 ms 45 ms
p99 Latency 18 ms 180-500 ms*
p99.9 Latency 25 ms 500-2000 ms*
Large File Streaming O(1) memory O(n) memory, OOM risk
Connection Cleanup Immediate Delayed (zombie threads)
Cache Stampede Coalesced Backend overload
Consistency ✅ High ⚠️ Variable

* Latency spikes caused by GC pauses

Advanced Stress Tests

Test FerrisRBE JVM Solutions
O(1) Streaming Constant memory regardless of file size Memory scales with file size
Connection Churn Immediate task cancellation Zombie threads, resource leaks
Cache Stampede Request coalescing prevents overload Backend overwhelmed
Multi-level Scheduling Fast actions never blocked Head-of-line blocking (FIFO)

Running Benchmarks

The benchmark suite tests eight critical dimensions of RBE performance:

cd benchmark

# 1. Memory footprint (baseline)
./scripts/benchmark.sh

# 2. Execution API throughput (Zero-GC advantage)
./scripts/execution-load-test.py --actions 1000 --concurrent 50

# 3. Action Cache performance (L1 DashMap + L2 disk-backed)
./scripts/action-cache-test.py --operations 10000 --concurrent 100

# 4. Multi-level scheduler (no head-of-line blocking)
./scripts/noisy-neighbor-test.py --slow 10 --fast 50

# 5. O(1) Streaming (constant memory with large files)
./scripts/o1-streaming-test.py --large-sizes 5 10 --small-count 1000

# 6. Connection churn (resource cleanup)
./scripts/connection-churn-test.py --connections 1000 --disconnect-rate 0.3

# 7. Cache stampede (thundering herd protection)
./scripts/cache-stampede-test.py --requests 10000 --concurrent 100

# 8. Cold start time (<100ms vs 5-30s JVM)
./scripts/cold-start-test.sh

See benchmark/README.md for detailed benchmark documentation and benchmark/results/BENCHMARK_RESULTS.md for results.

Quick Start

1. Configure Bazel

Add to your .bazelrc:

# Remote Cache (works from any OS)
build:remote-cache --remote_cache=grpc://localhost:9094
build:remote-cache --remote_upload_local_results=true

# Remote Execution (requires Linux toolchains)
build:remote-exec --config=remote-cache
build:remote-exec --remote_executor=grpc://localhost:9092
build:remote-exec --remote_default_exec_properties=OSFamily=linux

2. Build

# Cache only
bazel build --config=remote-cache //...

# Full remote execution
bazel build --config=remote-exec //...

3. Verify

# You should see "remote cache hit" and "remote" execution in the output
bazel build --config=remote //... 2>&1 | grep -E "(remote cache hit|processes)"

Configuration

FerrisRBE strictly follows 12-Factor App methodology. No cryptic XML or YAML files required.

Env Variable Default Description
RBE_PORT 9092 Server listening port
RBE_L1_CACHE_CAPACITY 100000 Max entries in the in-memory action cache
RBE_INLINE_OUTPUT_THRESHOLD 1048576 Size (bytes) below which outputs are sent inline
RBE_MAX_CONCURRENT_DOWNLOADS 10 Concurrency limit for materializing execroots

See docs/configuration.md for the complete reference.

Documentation

Project Structure

ferrisrbe/
├── src/
│   ├── server/          # gRPC services (REAPI v2.4)
│   ├── execution/       # Scheduler, state machine, results
│   ├── worker/          # Worker registry and management
│   ├── cas/             # Content Addressable Storage backends
│   └── cache/           # L1 Action Cache (DashMap)
├── charts/              # Helm charts for Kubernetes
├── k8s/                 # Raw Kubernetes manifests
├── examples/            # Test projects (Bazel 7.4, 8.x, 9.x)
└── docs/                # Full documentation

Roadmap

Completed ✅

  • REAPI v2.4 Capabilities Service
  • Action merging (deduplication of identical in-flight actions)
  • HTTP/2 adaptive keepalive for resilient worker connections
  • Multi-level scheduler (Fast/Medium/Slow queues)
  • DashMap-based L1 Action Cache (lock-free, 64 shards)
  • O(1) CAS streaming with async I/O
  • Comprehensive benchmark suite (8 dimensions)

In Progress 🚧

  • Persistent L2 Cache (disk-backed ActionCache in rbe-cache)
    • Implemented: DiskActionCache provides persistence across server restarts and cache sharing across multiple server replicas.
  • Prometheus / OpenTelemetry metrics exposition
    • Why: Production observability for SRE teams

Planned 📋

  • Web UI for build monitoring
    • Real-time build queue visualization
    • Worker status and health
    • Cache hit/miss analytics
  • Remote Build Without the Bytes (BwoB) support
    • Skip downloading outputs when not needed
  • Compressed CAS transfers (zstd)
    • Reduce network bandwidth for large artifacts

Contributing

PRs are welcome. We value:

  • Clean abstractions
  • Explicit error handling
  • Comprehensive documentation
  • Avoiding unwrap() in critical paths

See docs/project-structure.md for codebase orientation.

License

MIT


Built with 🦀 for engineers who value predictability.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages