You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
use datafusion::common::{exec_datafusion_err, exec_err};
4
4
use datafusion::error::DataFusionError;
5
-
use std::time::Duration;
5
+
use std::ops::Add;
6
+
use std::sync::Arc;
7
+
use std::time::{Duration,SystemTime,UNIX_EPOCH};
6
8
use tokio::sync::oneshot;
9
+
use tokio::sync::Notify;
10
+
use tokio::time;
11
+
use tokio::time::Instant;
7
12
8
13
/// In each stage of the distributed plan, there will be N workers. All these workers
9
14
/// need to coordinate to pull data from the next stage, which will contain M workers.
@@ -31,20 +36,67 @@ use tokio::sync::oneshot;
31
36
/// [oneshot::Sender], and listen on the other end of the channel [oneshot::Receiver] for
32
37
/// the delegate to put something there.
33
38
pubstructStageDelegation{
34
-
stage_targets:DashMap<(String,usize),Oneof>,
39
+
stage_targets:Arc<DashMap<(String,usize),Value>>,
35
40
wait_timeout:Duration,
41
+
42
+
notify:Arc<Notify>,
36
43
}
37
44
38
45
implDefaultforStageDelegation{
39
46
fndefault() -> Self{
40
-
Self{
41
-
stage_targets:DashMap::default(),
47
+
let stage_targets = Arc::new(DashMap::default());
48
+
let notify = Arc::new(Notify::new());
49
+
50
+
let result = Self{
51
+
stage_targets: stage_targets.clone(),
42
52
wait_timeout:Duration::from_secs(5),
53
+
54
+
notify: notify.clone(),
55
+
};
56
+
57
+
tokio::spawn(run_gc_async(
58
+
stage_targets.clone(),
59
+
notify.clone(),
60
+
Duration::from_secs(30),/* gc period */
61
+
));
62
+
63
+
result
64
+
}
65
+
}
66
+
67
+
// gc_interval is the period over which gc runs to purge old stage_targets entries which will
68
+
// never be read. This may happen if the actor encounters an error before it can read
69
+
// the delagate info.
70
+
asyncfnrun_gc_async(
71
+
stage_targets:Arc<DashMap<(String,usize),Value>>,
72
+
shutdown:Arc<Notify>,
73
+
period:Duration,
74
+
){
75
+
loop{
76
+
tokio::select! {
77
+
_ = shutdown.notified() => {
78
+
break;
79
+
}
80
+
_ = tokio::time::sleep(period) => {
81
+
// PERF: This iterator is sharded, so it won't lock the whole map.
82
+
stage_targets.retain(|_key, value| {
83
+
value.expiry.gt(&Instant::now())
84
+
});
85
+
}
43
86
}
44
87
}
45
88
}
46
89
90
+
implDropforStageDelegation{
91
+
fndrop(&mutself){
92
+
self.notify.notify_one();
93
+
}
94
+
}
95
+
47
96
implStageDelegation{
97
+
fngc_ttl(&self) -> Duration{
98
+
self.wait_timeout*2
99
+
}
48
100
/// Puts the [StageContext] info so that an actor can pick it up with `wait_for_delegate_info`.
49
101
///
50
102
/// - If the actor was already waiting for this info, it just puts it on the
@@ -57,9 +109,13 @@ impl StageDelegation {
57
109
actor_idx:usize,
58
110
next_stage_context:StageContext,
59
111
) -> Result<(),DataFusionError>{
112
+
let now = SystemTime::now()
113
+
.duration_since(UNIX_EPOCH)
114
+
.unwrap()
115
+
.as_secs();
60
116
let tx = matchself.stage_targets.entry((stage_id, actor_idx)){
61
-
Entry::Occupied(entry) => match entry.get(){
62
-
Oneof::Sender(_) => match entry.remove(){
117
+
Entry::Occupied(entry) => match entry.get().value{
118
+
Oneof::Sender(_) => match entry.remove().value{
63
119
Oneof::Sender(tx) => tx,
64
120
Oneof::Receiver(_) => unreachable!(),
65
121
},
@@ -69,7 +125,11 @@ impl StageDelegation {
69
125
},
70
126
Entry::Vacant(entry) => {
71
127
let(tx, rx) = oneshot::channel();
72
-
entry.insert(Oneof::Receiver(rx));
128
+
entry.insert(Value{
129
+
// Use 2 * the waiter wait duration for now.
130
+
expiry:Instant::now().add(self.gc_ttl()),
131
+
value:Oneof::Receiver(rx),
132
+
});
73
133
tx
74
134
}
75
135
};
@@ -95,16 +155,25 @@ impl StageDelegation {
95
155
actor_idx:usize,
96
156
) -> Result<StageContext,DataFusionError>{
97
157
let rx = matchself.stage_targets.entry((stage_id.clone(), actor_idx)){
98
-
Entry::Occupied(entry) => match entry.get(){
99
-
Oneof::Sender(_) => returnexec_err!("Programming error: while waiting for delegate info the entry in the StageDelegation target map cannot be a Sender"),
100
-
Oneof::Receiver(_) => match entry.remove(){
158
+
Entry::Occupied(entry) => match entry.get().value{
159
+
Oneof::Sender(_) => {
160
+
returnexec_err!(
161
+
"Programming error: while waiting for delegate info the entry in the \
162
+
StageDelegation target map cannot be a Sender"
163
+
)
164
+
}
165
+
Oneof::Receiver(_) => match entry.remove().value{
101
166
Oneof::Sender(_) => unreachable!(),
102
-
Oneof::Receiver(rx) => rx
167
+
Oneof::Receiver(rx) => rx,
103
168
},
104
169
},
105
170
Entry::Vacant(entry) => {
106
171
let(tx, rx) = oneshot::channel();
107
-
entry.insert(Oneof::Sender(tx));
172
+
entry.insert(Value{
173
+
// Use 2 * the waiter wait duration for now.
174
+
expiry:Instant::now().add(self.gc_ttl()),
175
+
value:Oneof::Sender(tx),
176
+
});
108
177
rx
109
178
}
110
179
};
@@ -120,6 +189,11 @@ impl StageDelegation {
120
189
}
121
190
}
122
191
192
+
structValue{
193
+
expiry:Instant,
194
+
value:Oneof,
195
+
}
196
+
123
197
enumOneof{
124
198
Sender(oneshot::Sender<StageContext>),
125
199
Receiver(oneshot::Receiver<StageContext>),
@@ -129,6 +203,7 @@ enum Oneof {
129
203
mod tests {
130
204
usesuper::*;
131
205
usecrate::stage_delegation::StageContext;
206
+
use futures::TryFutureExt;
132
207
use std::sync::Arc;
133
208
use uuid::Uuid;
134
209
@@ -222,6 +297,7 @@ mod tests {
222
297
223
298
let received_context = wait_task1.await.unwrap().unwrap();
0 commit comments