featherbit/plugins/resources.rs
1//! Process-wide services shared by every plugin instance.
2//!
3//! `SharedState` owns one [`PluginResources`] for the process lifetime and
4//! threads it through `compile_policy` → `create_plugin` → each plugin's
5//! `from_config`, so plugins can hold handles to shared infrastructure
6//! (metrics registry, outbound HTTP client, consumer store, rate-limit
7//! counter backends) instead of constructing their own per node or — worse —
8//! per request.
9
10use std::sync::Arc;
11
12use arc_swap::ArcSwap;
13
14use crate::consumers::ConsumerStore;
15use crate::metrics::GatewayMetrics;
16use crate::outbound::OutboundClient;
17use crate::ratelimit::CounterStoreRegistry;
18use crate::traffic::TrafficRegistries;
19
20/// Handle to process-wide services, injected into plugins at construction.
21///
22/// Grows as shared infrastructure lands (counter backends); every field must
23/// be cheap to clone or shared behind its own `Arc`.
24pub struct PluginResources {
25 /// Shared Prometheus registry; `None` disables recording (unit tests).
26 pub metrics: Option<Arc<GatewayMetrics>>,
27 /// Pooled outbound HTTP client for upstream proxying and plugin callouts.
28 pub outbound: Arc<OutboundClient>,
29 /// Consumer store, swapped atomically on config reload so lookups on the
30 /// request path are lock-free. Plugins should `load()` per request, not
31 /// cache the inner Arc across requests.
32 pub consumers: ArcSwap<ConsumerStore>,
33 /// Rate-limit counter backends, resolved by `policy` name at config load.
34 pub counters: CounterStoreRegistry,
35 /// Shared state for traffic-control node pairs (concurrency, breakers, cache).
36 pub traffic: TrafficRegistries,
37 /// Named shared stores (redis/valkey), swapped on config (re)compile.
38 /// Plugins resolve a store by name at construction time and hold the
39 /// resulting `Arc` — nothing reads this on the request path.
40 pub stores: ArcSwap<crate::stores::StoreRegistry>,
41}
42
43impl PluginResources {
44 /// Creates the process-wide resources handle with an empty consumer
45 /// store; callers swap in the real store via [`PluginResources::consumers`].
46 pub fn new(metrics: Option<Arc<GatewayMetrics>>) -> Arc<Self> {
47 Arc::new(Self {
48 traffic: TrafficRegistries::new(metrics.clone()),
49 metrics,
50 outbound: Arc::new(OutboundClient::new()),
51 consumers: ArcSwap::from_pointee(ConsumerStore::default()),
52 counters: CounterStoreRegistry::default(),
53 stores: ArcSwap::from_pointee(crate::stores::StoreRegistry::default()),
54 })
55 }
56
57 /// Resources with optional services disabled — for unit tests.
58 #[cfg(test)]
59 pub fn empty() -> Arc<Self> {
60 Self::new(None)
61 }
62}