Skip to main content

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}