feat: Rewrite edge-proxy in rust#1
Conversation
5ff0371 to
8fc4662
Compare
High-performance Axum-based edge proxy with: - Full API compatibility with Python version - Environment document caching with polling - LRU endpoint response caching - Health check endpoints - Server/client key validation Architecture: - routes/ - HTTP handlers - services/ - Business logic - cache/ - Caching layer - models/ - Domain types - config/ - Settings
khvn26
left a comment
There was a problem hiding this comment.
No blocking comments, I love it. Makes me wish to write some Rust as well 👍
| - name: Set up QEMU | ||
| uses: docker/setup-qemu-action@v3 |
There was a problem hiding this comment.
I suggest using Depot to save ourselves time and hassle.
| - name: Build and push | ||
| uses: docker/build-push-action@v6 | ||
| id: build | ||
| with: | ||
| push: true | ||
| platforms: linux/amd64,linux/arm64 | ||
| file: Dockerfile | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max |
There was a problem hiding this comment.
I think we should target quay.io as well.
| match settings.log_format.as_str() { | ||
| "json" => { | ||
| let fmt_layer = fmt::layer().json(); | ||
| subscriber.with(fmt_layer).init(); | ||
| } | ||
| _ => { | ||
| // "generic" format | ||
| let fmt_layer = fmt::layer() | ||
| .with_ansi(settings.use_colors) | ||
| .with_target(false); | ||
| subscriber.with(fmt_layer).init(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Note: the amount of Python code required to achieve the same result makes me sad.
| flags_cache: Option<Arc<RwLock<LruCache<CacheKey, Value>>>>, | ||
| identities_cache: Option<Arc<RwLock<LruCache<CacheKey, Value>>>>, | ||
| /// Cache for pre-serialized environment document bytes (zero serialization per request) | ||
| environment_document_cache: Option<DocumentCache>, |
There was a problem hiding this comment.
I'm interested in stress-testing this to find out how many environments these bad boys can fit before RwLock<LruCache> becomes a bottleneck. Probably quite a lot, I assume, so there's no immediate need to look into alternative caches like threadsafe-lru or locallru (not sure if you ever considered it?)
There was a problem hiding this comment.
Did some stress testing on this. Results show RwLock handles the load well:
┌──────────────┬────────────┬───────────────┬────────────────┐
│ Environments │ Cache Size │ Single-thread │ 256 concurrent │
├──────────────┼────────────┼───────────────┼────────────────┤
│ 10 │ 1,000 │ 4.7M ops/sec │ 3.3M ops/sec │
├──────────────┼────────────┼───────────────┼────────────────┤
│ 100 │ 10,000 │ 6.1M ops/sec │ 3.3M ops/sec │
├──────────────┼────────────┼───────────────┼────────────────┤
│ 1000 │ 100,000 │ 4.9M ops/sec │ 3.1M ops/sec │
└──────────────┴────────────┴───────────────┴────────────────┘
Scaling to more environments doesn't degrade performance (LRU ops are O(1)). The lock is held for microseconds, so even though get() requires a write lock (LRU mutation), it's not a bottleneck in
practice.
There was a problem hiding this comment.
nit: Consider stratch/distroless for a truly slim image — see e.g. https://oneuptime.com/blog/post/2026-01-07-rust-minimal-docker-images/view
I'll create an issue if you choose not to pursue this.
There was a problem hiding this comment.
Is it difficult to implement the enable_access_log setting from the get go?
| feature: APIFeature { | ||
| id: flag_result.metadata.feature_id as i64, | ||
| name: flag_result.name.clone(), | ||
| feature_type: Cow::Borrowed("STANDARD"), |
There was a problem hiding this comment.
tiny little inconsequential nit: feature_type could be part of engine metadata as well as id?
I've no idea who uses this field but it's not hard to support it IMO.
Summary
High-performance Rust implementation of the Flagsmith Edge Proxy using Axum web framework.
Features
API Endpoints
GET /api/v1/flags- Get all flags (with optional?feature=filter)GET/POST /api/v1/identities/- Get flags for identityGET /api/v1/environment-document- Get environment document (server key only)GET /health,/proxy/health/*- Health checksConfiguration