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<T: InitContext<Self>>(&mut self, ctx: &mut T) -> 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        let networking = ctx.app_state::<OutboundNetworkingFactor>().ok();
48
49        Ok(AppState {
50            semaphore: build_connection_semaphore(
51                networking,
52                "mysql",
53                config.max_connections,
54                config.wait_timeout,
55            ),
56        })
57    }
58
59    fn prepare<T: spin_factors::RuntimeFactors>(
60        &self,
61        mut ctx: spin_factors::PrepareContext<T, Self>,
62    ) -> anyhow::Result<Self::InstanceBuilder> {
63        let allowed_hosts = ctx
64            .instance_builder::<OutboundNetworkingFactor>()?
65            .allowed_hosts();
66        let otel = OtelFactorState::from_prepare_context(&mut ctx)?;
67
68        Ok(InstanceState {
69            inner: Arc::new(Mutex::new(InstanceStateInner {
70                allowed_hosts,
71                connections: Default::default(),
72                otel,
73            })),
74            semaphore: ctx.app_state().semaphore.clone(),
75        })
76    }
77}
78
79impl<C> Default for OutboundMysqlFactor<C> {
80    fn default() -> Self {
81        Self {
82            _phantom: Default::default(),
83        }
84    }
85}
86
87impl<C> OutboundMysqlFactor<C> {
88    pub fn new() -> Self {
89        Self::default()
90    }
91}
92
93pub struct InstanceStateInner<C> {
94    allowed_hosts: OutboundAllowedHosts,
95    connections: spin_resource_table::Table<(Arc<Mutex<C>>, ConnectionPermit)>,
96    otel: OtelFactorState,
97}
98
99pub struct InstanceState<C> {
100    pub(crate) inner: Arc<Mutex<InstanceStateInner<C>>>,
101    pub semaphore: ConnectionSemaphore,
102}
103
104impl<C: Send + 'static> SelfInstanceBuilder for InstanceState<C> {}
105
106pub struct MysqlFactorData<C: Client>(OutboundMysqlFactor<C>);
107
108impl<C: Client> spin_core::wasmtime::component::HasData for MysqlFactorData<C> {
109    type Data<'a> = &'a mut InstanceState<C>;
110}
111
112impl<C: Client> spin_core::wasmtime::component::HasData for InstanceState<C> {
113    type Data<'a> = &'a mut InstanceState<C>;
114}