Skip to content

Commit e1b4eed

Browse files
committed
Attempt to recreate UDS stream if not available, rather than crash
This commit adjusts the UDS generator spin implementation to be more like the UDP implementation in that we no longer crash a subtask if the UDS socket is not available for opening yet. In instances where Datadog Agent is concerned the socket can be available relatively late, harming reliable inspection. Signed-off-by: Brian L. Troutwine <brian.troutwine@datadoghq.com>
1 parent b1105c3 commit e1b4eed

2 files changed

Lines changed: 76 additions & 58 deletions

File tree

src/generator/udp.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,14 @@ impl Udp {
152152
let mut blocks = self.block_cache.iter().cycle();
153153
let mut connection = Option::<UdpSocket>::None;
154154
loop {
155-
let blk = blocks.next().unwrap();
156-
let total_bytes = blk.total_bytes;
157-
assert!(
158-
total_bytes.get() <= 65507,
159-
"UDP packet too large (over 65507 B)"
160-
);
161155
if let Some(sock) = &connection {
156+
let blk = blocks.next().unwrap();
157+
let total_bytes = blk.total_bytes;
158+
assert!(
159+
total_bytes.get() <= 65507,
160+
"UDP packet too large (over 65507 B)"
161+
);
162+
162163
tokio::task::yield_now().await;
163164
self.rate_limiter.until_n_ready(total_bytes).await.unwrap();
164165

src/generator/uds.rs

Lines changed: 69 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use std::{
1818
num::{NonZeroU32, NonZeroUsize},
1919
path::PathBuf,
2020
};
21-
use tokio::{io::Interest, net::UnixStream};
22-
use tracing::{debug, info};
21+
use tokio::{io::Interest, net::UnixStream, task::JoinError};
22+
use tracing::{debug, error, info, trace};
2323

2424
#[derive(Debug, Deserialize, PartialEq, Eq)]
2525
/// Configuration of this generator.
@@ -51,6 +51,9 @@ pub enum Error {
5151
/// Generic IO error
5252
#[error("IO error: {0}")]
5353
Io(#[from] std::io::Error),
54+
/// Subtask error
55+
#[error("Subtask failure: {0}")]
56+
Subtask(#[from] JoinError),
5457
}
5558

5659
#[derive(Debug)]
@@ -142,62 +145,76 @@ impl Uds {
142145

143146
let send_task = tokio::spawn(async move {
144147
let mut blocks = self.block_cache.iter().cycle();
148+
let mut unix_stream = Option::<UnixStream>::None;
149+
145150
loop {
146-
// Connect to the stream interior to the spawn allows us to
147-
// re-connect within the same task, does require that we unwrap
148-
// failures, catastrophically failing lading.
149-
let stream = UnixStream::connect(&self.path)
150-
.await
151-
.map_err(Error::Io)
152-
.unwrap();
153-
let blk = blocks.next().unwrap();
154-
let total_bytes = blk.total_bytes;
155-
156-
tokio::task::yield_now().await;
157-
self.rate_limiter
158-
.until_n_ready(total_bytes)
159-
.await
160-
.map_err(Error::Governor)
161-
.unwrap();
162-
163-
// NOTE When we write into a unix stream it may be that only
164-
// some of the written bytes make it through in which case we
165-
// must cycle back around and try to write the remainder of the
166-
// buffer.
167-
let blk_max: usize = total_bytes.get() as usize;
168-
let mut blk_offset = 0;
169-
while blk_offset < blk_max {
170-
let ready = stream
171-
.ready(Interest::WRITABLE)
151+
if let Some(stream) = &unix_stream {
152+
let blk = blocks.next().unwrap();
153+
let total_bytes = blk.total_bytes;
154+
155+
tokio::task::yield_now().await;
156+
self.rate_limiter
157+
.until_n_ready(total_bytes)
172158
.await
173-
.map_err(Error::Io)
174-
.unwrap(); // Cannot ? in a spawned task :<. Mimics UDP generator.
175-
if ready.is_writable() {
176-
// Try to write data, this may still fail with `WouldBlock`
177-
// if the readiness event is a false positive.
178-
match stream.try_write(&blk.bytes[blk_offset..]) {
179-
Ok(bytes) => {
180-
counter!("bytes_written", bytes as u64, &labels);
181-
blk_offset = bytes;
182-
}
183-
Err(ref e) if e.kind() == tokio::io::ErrorKind::WouldBlock => {
184-
// If the read side has hung up we will never
185-
// know and will keep attempting to write into
186-
// the stream. This yield means we won't hog the
187-
// whole CPU.
188-
tokio::task::yield_now().await;
189-
continue;
190-
}
191-
Err(err) => {
192-
debug!("write failed: {}", err);
159+
.map_err(Error::Governor)
160+
.unwrap();
161+
162+
// NOTE When we write into a unix stream it may be that only
163+
// some of the written bytes make it through in which case we
164+
// must cycle back around and try to write the remainder of the
165+
// buffer.
166+
let blk_max: usize = total_bytes.get() as usize;
167+
let mut blk_offset = 0;
168+
while blk_offset < blk_max {
169+
let ready = stream
170+
.ready(Interest::WRITABLE)
171+
.await
172+
.map_err(Error::Io)
173+
.unwrap(); // Cannot ? in a spawned task :<. Mimics UDP generator.
174+
if ready.is_writable() {
175+
// Try to write data, this may still fail with `WouldBlock`
176+
// if the readiness event is a false positive.
177+
match stream.try_write(&blk.bytes[blk_offset..]) {
178+
Ok(bytes) => {
179+
counter!("bytes_written", bytes as u64, &labels);
180+
blk_offset = bytes;
181+
}
182+
Err(ref e) if e.kind() == tokio::io::ErrorKind::WouldBlock => {
183+
// If the read side has hung up we will never
184+
// know and will keep attempting to write into
185+
// the stream. This yield means we won't hog the
186+
// whole CPU.
187+
tokio::task::yield_now().await;
188+
continue;
189+
}
190+
Err(err) => {
191+
debug!("write failed: {}", err);
193192

194-
let mut error_labels = labels.clone();
195-
error_labels.push(("error".to_string(), err.to_string()));
196-
counter!("request_failure", 1, &error_labels);
197-
break; // while blk_offset
193+
let mut error_labels = labels.clone();
194+
error_labels.push(("error".to_string(), err.to_string()));
195+
counter!("request_failure", 1, &error_labels);
196+
break; // while blk_offset
197+
}
198198
}
199199
}
200200
}
201+
} else {
202+
// Connect to the stream interior to the spawn allows us to
203+
// re-connect within the same task, does require that we unwrap
204+
// failures, catastrophically failing lading.
205+
match UnixStream::connect(&self.path).await {
206+
Ok(sock) => {
207+
debug!("UDS socket opened for writing.");
208+
unix_stream = Some(sock);
209+
}
210+
Err(err) => {
211+
trace!("opening UDS path failed: {}", err);
212+
213+
let mut error_labels = labels.clone();
214+
error_labels.push(("error".to_string(), err.to_string()));
215+
counter!("connection_failure", 1, &error_labels);
216+
}
217+
}
201218
}
202219
}
203220
});

0 commit comments

Comments
 (0)