1use std::sync::Arc;
22
23use helix_driver_host::engine::{self, EngineDeps, TransportLifecycleEvent, TransportTraceSink};
24use helix_driver_host::{AsyncMetricSink, NoopMetricSink, SharedFileUploader, TraceHooks};
25use helix_driver_host::{TickIngressReceiver, TickIngressSender};
26pub use helix_driver_host::{rows_from_reply_bytes, rows_to_reply_bytes};
28
29use helix_core::effect::TransportId;
30use helix_core::{ExecutionShell, Tick};
31use std::collections::HashMap;
32use tokio::sync::{mpsc, oneshot};
33
34use crate::clock::NativeClock;
35use crate::event_sink::NativeEventSink;
36use crate::http::NativeHttp;
37use crate::storage::NativeStorage;
38use crate::transport::NativeTransport;
39
40mod reconnect;
41use reconnect::{start_native_reconnect, NativeReconnectRuntime};
42
43pub type TransportTable = HashMap<TransportId, Arc<NativeTransport>>;
50
51pub struct EngineConfig {
56 pub storage: NativeStorage,
57 pub http: NativeHttp,
58 pub uploader: SharedFileUploader,
59 pub event_sink: NativeEventSink,
60 pub metrics: Arc<dyn AsyncMetricSink>,
62 pub max_http_inflight: usize,
69}
70
71impl EngineConfig {
72 pub fn new(
73 storage: NativeStorage,
74 http: NativeHttp,
75 uploader: SharedFileUploader,
76 event_sink: NativeEventSink,
77 max_http_inflight: usize,
78 ) -> Self {
79 Self {
80 storage,
81 http,
82 uploader,
83 event_sink,
84 metrics: Arc::new(NoopMetricSink),
85 max_http_inflight,
86 }
87 }
88
89 pub fn with_metric_sink(mut self, metrics: Arc<dyn AsyncMetricSink>) -> Self {
90 self.metrics = metrics;
91 self
92 }
93
94 fn into_deps(
96 self,
97 trace: TraceHooks,
98 transport_lifecycle_tx: Option<mpsc::UnboundedSender<TransportLifecycleEvent>>,
99 transport_trace_tx: Option<TransportTraceSink>,
100 ) -> EngineDeps<NativeStorage, NativeHttp, SharedFileUploader, NativeEventSink, NativeClock>
101 {
102 EngineDeps {
103 storage: Arc::new(self.storage),
104 http: Arc::new(self.http),
105 uploader: Arc::new(self.uploader),
106 event_sink: Arc::new(self.event_sink),
107 clock: NativeClock,
108 trace,
109 metrics: self.metrics,
110 max_http_inflight: self.max_http_inflight,
111 transport_lifecycle_tx,
112 transport_trace_tx,
113 }
114 }
115}
116
117pub async fn run_engine_loop(
141 shell: ExecutionShell,
142 tick_rx: mpsc::Receiver<Tick>,
143 tick_tx: mpsc::Sender<Tick>,
144 config: EngineConfig,
145 shutdown_rx: oneshot::Receiver<()>,
146) {
147 run_engine_loop_with_transports(
148 shell,
149 tick_rx,
150 tick_tx,
151 config,
152 shutdown_rx,
153 TransportTable::new(),
154 )
155 .await
156}
157
158pub async fn run_engine_loop_stamped(
160 shell: ExecutionShell,
161 tick_rx: TickIngressReceiver,
162 tick_tx: TickIngressSender,
163 config: EngineConfig,
164 shutdown_rx: oneshot::Receiver<()>,
165) {
166 run_engine_loop_with_transports_and_trace_stamped(
167 shell,
168 tick_rx,
169 tick_tx,
170 config,
171 TraceHooks::noop(),
172 shutdown_rx,
173 TransportTable::new(),
174 )
175 .await;
176}
177
178pub async fn run_engine_loop_with_transports(
185 shell: ExecutionShell,
186 tick_rx: mpsc::Receiver<Tick>,
187 tick_tx: mpsc::Sender<Tick>,
188 config: EngineConfig,
189 shutdown_rx: oneshot::Receiver<()>,
190 transports: TransportTable,
191) {
192 run_engine_loop_with_transports_and_trace(
193 shell,
194 tick_rx,
195 tick_tx,
196 config,
197 TraceHooks::noop(),
198 shutdown_rx,
199 transports,
200 )
201 .await;
202}
203
204pub async fn run_engine_loop_with_transports_and_trace(
206 shell: ExecutionShell,
207 tick_rx: mpsc::Receiver<Tick>,
208 tick_tx: mpsc::Sender<Tick>,
209 config: EngineConfig,
210 trace: TraceHooks,
211 shutdown_rx: oneshot::Receiver<()>,
212 transports: TransportTable,
213) {
214 let NativeReconnectRuntime {
215 transport_rx,
216 lifecycle_tx,
217 trace_sink,
218 trace_stats,
219 tasks,
220 shutdown_tx: reconnect_shutdown_tx,
221 } = start_native_reconnect(&transports);
222 engine::run_engine_loop(
223 shell,
224 tick_rx,
225 tick_tx,
226 config.into_deps(trace, lifecycle_tx, trace_sink),
227 shutdown_rx,
228 transports,
229 transport_rx,
230 )
231 .await;
232 if let Some(stats) = trace_stats {
233 let dropped = stats.dropped_count();
234 if dropped > 0 {
235 tracing::warn!(dropped, "native transport trace 有界队列发生丢弃");
236 }
237 }
238 reconnect_shutdown_tx.send_replace(true);
239 for task in tasks {
240 task.abort();
241 }
242}
243
244pub async fn run_engine_loop_with_transports_and_trace_stamped(
245 shell: ExecutionShell,
246 tick_rx: TickIngressReceiver,
247 tick_tx: TickIngressSender,
248 config: EngineConfig,
249 trace: TraceHooks,
250 shutdown_rx: oneshot::Receiver<()>,
251 transports: TransportTable,
252) {
253 let NativeReconnectRuntime {
254 transport_rx,
255 lifecycle_tx,
256 trace_sink,
257 trace_stats,
258 tasks,
259 shutdown_tx: reconnect_shutdown_tx,
260 } = start_native_reconnect(&transports);
261 engine::run_engine_loop_stamped(
262 shell,
263 tick_rx,
264 tick_tx,
265 config.into_deps(trace, lifecycle_tx, trace_sink),
266 shutdown_rx,
267 transports,
268 transport_rx,
269 )
270 .await;
271 if let Some(stats) = trace_stats {
272 let dropped = stats.dropped_count();
273 if dropped > 0 {
274 tracing::warn!(dropped, "native transport trace 有界队列发生丢弃");
275 }
276 }
277 reconnect_shutdown_tx.send_replace(true);
278 for task in tasks {
279 task.abort();
280 }
281}
282
283#[cfg(test)]
284#[path = "engine_loop_tests.rs"]
285mod tests;