-
Notifications
You must be signed in to change notification settings - Fork 371
Expand file tree
/
Copy pathembedded.rs
More file actions
153 lines (137 loc) · 4.47 KB
/
Copy pathembedded.rs
File metadata and controls
153 lines (137 loc) · 4.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use crate::schedulers::{Scheduler, SchedulerError, StartPipelineReq};
use arroyo_rpc::grpc::rpc::{HeartbeatNodeReq, RegisterNodeReq, WorkerFinishedReq};
use arroyo_server_common::shutdown::{Shutdown, SignalBehavior};
use arroyo_types::{JobId, MachineId, WorkerId};
use arroyo_worker::WorkerServer;
use async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use tonic::Status;
use tracing::{error, info};
pub struct EmbeddedWorker {
job_id: JobId,
generation: u64,
shutdown: Shutdown,
handle: JoinHandle<()>,
}
/// The EmbedddedScheduler runs workers within the controller process
pub struct EmbeddedScheduler {
tasks: Arc<Mutex<HashMap<WorkerId, EmbeddedWorker>>>,
worker_counter: AtomicU64,
}
impl EmbeddedScheduler {
pub fn new() -> Self {
Self {
tasks: Arc::new(Default::default()),
worker_counter: AtomicU64::new(100),
}
}
async fn clear_finished(&self) {
self.tasks
.lock()
.await
.retain(|_, w| !w.handle.is_finished());
}
}
#[async_trait]
impl Scheduler for EmbeddedScheduler {
async fn start_workers(&self, req: StartPipelineReq) -> Result<(), SchedulerError> {
self.clear_finished().await;
let shutdown = Shutdown::new("embedded-worker", SignalBehavior::None);
let guard = shutdown.guard("embedded-worker");
let job_id = req.job_id.clone();
let pipeline_id = req.pipeline_id.clone();
let log_job_id = job_id.clone();
let generation = req.generation;
let worker_id = WorkerId(self.worker_counter.fetch_add(1, Ordering::SeqCst));
let handle = tokio::task::spawn(async move {
let server = WorkerServer::new(
MachineId(Arc::new("embedded".to_string())),
worker_id,
req.pipeline_id.clone(),
req.job_id.clone(),
req.generation,
guard,
);
let worker_job_id = log_job_id.clone();
let worker_pipeline_id = pipeline_id.clone();
match tokio::task::spawn(async move {
if let Err(e) = server.start_async().await {
error!(
job_id = %worker_job_id,
pipeline_id = *worker_pipeline_id,
"Failed to start worker {:?}: {:?}",
worker_id,
e
);
}
})
.await
{
Ok(_) => {
info!(
job_id = %log_job_id,
pipeline_id = *pipeline_id,
"Worker {:?} finished",
worker_id
);
}
Err(err) => {
error!(
job_id = %log_job_id,
pipeline_id = *pipeline_id,
"Worker {:?} panicked: {:?}",
worker_id,
err
);
}
}
});
self.tasks.lock().await.insert(
worker_id,
EmbeddedWorker {
job_id,
generation,
shutdown,
handle,
},
);
Ok(())
}
async fn register_node(&self, _: RegisterNodeReq) {}
async fn heartbeat_node(&self, _: HeartbeatNodeReq) -> Result<(), Status> {
Ok(())
}
async fn worker_finished(&self, _: WorkerFinishedReq) {}
async fn stop_workers(
&self,
job_id: &str,
generation: Option<u64>,
_: bool,
) -> anyhow::Result<()> {
for w in self.workers_for_job(job_id, generation).await? {
let state = self.tasks.lock().await;
if let Some(worker) = state.get(&w) {
worker.shutdown.token().cancel();
}
}
Ok(())
}
async fn workers_for_job(
&self,
job_id: &str,
generation: Option<u64>,
) -> anyhow::Result<Vec<WorkerId>> {
let state = self.tasks.lock().await;
Ok(state
.iter()
.filter(|(_, t)| {
*t.job_id == job_id && (generation.is_none() || generation.unwrap() == t.generation)
})
.map(|(k, _)| *k)
.collect())
}
}