Skip to main content

spin_factor_outbound_mysql/
lib.rs

1pub mod client;
2mod host;
3pub mod runtime_config;
4
5use std::sync::Arc;
6
7use client::Client;
8use mysql_async::Conn as MysqlClient;
9use runtime_config::RuntimeConfig;
10use spin_factor_otel::OtelFactorState;
11use spin_factor_outbound_networking::{
12    ConnectionPermit, ConnectionSemaphore, OutboundNetworkingFactor, build_connection_semaphore,
13    config::allowed_hosts::OutboundAllowedHosts,
14};
15use spin_factors::{Factor, FactorData, InitContext, RuntimeFactors, SelfInstanceBuilder};
16use spin_world::spin::mysql::mysql as v3;
17use spin_world::v1::mysql as v1;
18use spin_world::v2::mysql as v2;
19use tokio::sync::Mutex;
20
21pub struct OutboundMysqlFactor<C = MysqlClient> {
22    _phantom: std::marker::PhantomData<C>,
23}
24
25pub struct AppState {
26    /// Semaphore to limit concurrent outbound MySQL connections.
27    pub semaphore: ConnectionSemaphore,
28}
29
30impl<C: Send + Sync + Client + 'static> Factor for OutboundMysqlFactor<C> {
31    type RuntimeConfig = RuntimeConfig;
32    type AppState = AppState;
33    type InstanceBuilder = InstanceState<C>;
34
35    fn init(&mut self, ctx: &mut impl InitContext<Self>) -> anyhow::Result<()> {
36        ctx.link_bindings(v1::add_to_linker::<_, FactorData<Self>>)?;
37        ctx.link_bindings(v2::add_to_linker::<_, FactorData<Self>>)?;
38        ctx.link_bindings(v3::add_to_linker::<_, MysqlFactorData<C>>)?;
39        Ok(())
40    }
41
42    fn configure_app<T: RuntimeFactors>(
43        &self,
44        mut ctx: spin_factors::ConfigureAppContext<T, Self>,
45    ) -> anyhow::Result<Self::AppState> {
46        let config = ctx.take_runtime_config().unwrap_or_default();
47
48        Ok(AppState {
49            semaphore: build_connection_semaphore(
50                ctx.app_state::<OutboundNetworkingFactor>().ok(),
51                "mysql",
52                config.max_connections,
53                config.wait_timeout,
54            ),
55        })
56    }
57
58    fn prepare<T: spin_factors::RuntimeFactors>(
59        &self,
60        mut ctx: spin_factors::PrepareContext<T, Self>,
61    ) -> anyhow::Result<Self::InstanceBuilder> {
62        let allowed_hosts = ctx
63            .instance_builder::<OutboundNetworkingFactor>()?
64            .allowed_hosts();
65        let otel = OtelFactorState::from_prepare_context(&mut ctx)?;
66
67        Ok(InstanceState {
68            inner: Arc::new(Mutex::new(InstanceStateInner {
69                allowed_hosts,
70                connections: Default::default(),
71                otel,
72            })),
73            semaphore: ctx.app_state().semaphore.clone(),
74        })
75    }
76}
77
78impl<C> Default for OutboundMysqlFactor<C> {
79    fn default() -> Self {
80        Self {
81            _phantom: Default::default(),
82        }
83    }
84}
85
86impl<C> OutboundMysqlFactor<C> {
87    pub fn new() -> Self {
88        Self::default()
89    }
90}
91
92pub struct InstanceStateInner<C> {
93    allowed_hosts: OutboundAllowedHosts,
94    connections: spin_resource_table::Table<(Arc<Mutex<C>>, ConnectionPermit)>,
95    otel: OtelFactorState,
96}
97
98pub struct InstanceState<C> {
99    pub(crate) inner: Arc<Mutex<InstanceStateInner<C>>>,
100    pub semaphore: ConnectionSemaphore,
101}
102
103impl<C: Send + 'static> SelfInstanceBuilder for InstanceState<C> {}
104
105pub struct MysqlFactorData<C: Client>(OutboundMysqlFactor<C>);
106
107impl<C: Client> spin_core::wasmtime::component::HasData for MysqlFactorData<C> {
108    type Data<'a> = &'a mut InstanceState<C>;
109}
110
111impl<C: Client> spin_core::wasmtime::component::HasData for InstanceState<C> {
112    type Data<'a> = &'a mut InstanceState<C>;
113}