Skip to content

Commit 10df625

Browse files
committed
payload union
Signed-off-by: Lee Benson <lee@leebenson.com>
1 parent 36b297f commit 10df625

File tree

7 files changed

+111
-98
lines changed

7 files changed

+111
-98
lines changed

src/api/schema/events/encoding.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use async_graphql::Enum;
2+
3+
#[derive(Enum, Copy, Clone, PartialEq, Eq)]
4+
/// Encoding format for the event
5+
pub enum EventEncodingType {
6+
Json,
7+
Yaml,
8+
}

src/api/schema/events/event.rs

-68
This file was deleted.

src/api/schema/events/log_event.rs src/api/schema/events/log.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
use super::event::EventEncodingType;
1+
use super::EventEncodingType;
22
use crate::event;
3+
34
use async_graphql::Object;
45
use chrono::{DateTime, Utc};
56

67
#[derive(Debug)]
7-
pub struct LogEvent {
8+
pub struct Log {
89
component_name: String,
910
event: event::LogEvent,
1011
}
1112

12-
impl LogEvent {
13+
impl Log {
1314
pub fn new(component_name: &str, event: event::LogEvent) -> Self {
1415
Self {
1516
component_name: component_name.to_string(),
@@ -28,7 +29,7 @@ impl LogEvent {
2829

2930
#[Object]
3031
/// Log event with fields for querying log data
31-
impl LogEvent {
32+
impl Log {
3233
/// Name of the component associated with the log event
3334
async fn component_name(&self) -> &str {
3435
&self.component_name

src/api/schema/events/mod.rs

+27-18
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
1-
mod event;
2-
mod log_event;
1+
mod encoding;
2+
mod log;
3+
mod notification;
4+
mod output;
35

4-
use event::Event;
6+
use output::OutputEventsPayload;
57

68
use crate::{api::tap::TapSink, topology::WatchRx};
79

8-
use async_graphql::{validators::IntRange, Context, Subscription};
10+
use async_graphql::{validators::IntRange, Context, Enum, Subscription};
911
use futures::StreamExt;
1012
use itertools::Itertools;
1113
use rand::{rngs::SmallRng, Rng, SeedableRng};
1214
use tokio::{select, stream::Stream, sync::mpsc, time};
1315

16+
#[derive(Enum, Copy, Clone, PartialEq, Eq)]
17+
/// Encoding format for the event
18+
pub enum EventEncodingType {
19+
Json,
20+
Yaml,
21+
}
22+
1423
#[derive(Debug, Default)]
1524
pub struct EventsSubscription;
1625

@@ -23,7 +32,7 @@ impl EventsSubscription {
2332
component_names: Vec<String>,
2433
#[graphql(default = 500)] interval: u32,
2534
#[graphql(default = 100, validator(IntRange(min = "1", max = "10_000")))] limit: u32,
26-
) -> impl Stream<Item = Vec<Event>> + 'a {
35+
) -> impl Stream<Item = Vec<OutputEventsPayload>> + 'a {
2736
let watch_rx = ctx.data_unchecked::<WatchRx>().clone();
2837

2938
// Client input is confined to `u32` to provide sensible bounds.
@@ -39,15 +48,15 @@ fn create_events_stream(
3948
component_names: Vec<String>,
4049
interval: u64,
4150
limit: usize,
42-
) -> impl Stream<Item = Vec<Event>> {
51+
) -> impl Stream<Item = Vec<OutputEventsPayload>> {
4352
// Channel for receiving individual tap payloads. Since we can process at most `limit` per
4453
// interval, this is capped to the same value.
4554
let (tap_tx, mut tap_rx) = mpsc::channel(limit);
4655

4756
// The resulting vector of `Event` sent to the client. Only one result set will be streamed
4857
// back to the client at a time. This value is set higher than `1` to prevent blocking the event
4958
// pipeline on slower client connections, but low enough to apply a modest cap on mem usage.
50-
let (mut event_tx, event_rx) = mpsc::channel::<Vec<Event>>(10);
59+
let (mut event_tx, event_rx) = mpsc::channel::<Vec<OutputEventsPayload>>(10);
5160

5261
tokio::spawn(async move {
5362
// Create a tap sink. When this drops out of scope, clean up will be performed on the
@@ -58,14 +67,14 @@ fn create_events_stream(
5867
let mut interval = time::interval(time::Duration::from_millis(interval));
5968

6069
// Temporary structure to hold sortable values of `Event`.
61-
struct SortableEvent {
70+
struct SortableOutputEventsPayload {
6271
batch: usize,
63-
event: Event,
72+
payload: OutputEventsPayload,
6473
}
6574

6675
// Collect a vector of results, with a capacity of `limit`. As new `Event`s come in,
6776
// they will be sampled and added to results.
68-
let mut results = Vec::<SortableEvent>::with_capacity(limit);
77+
let mut results = Vec::<SortableOutputEventsPayload>::with_capacity(limit);
6978

7079
// Random number generator to allow for sampling. Speed trumps cryptographic security here.
7180
// The RNG must be Send + Sync to use with the `select!` loop below, hence `SmallRng`.
@@ -80,32 +89,32 @@ fn create_events_stream(
8089
// Process `TapPayload`s. A tap payload could contain log/metric events or a
8190
// notification. Notifications are emitted immediately; events buffer until
8291
// the next `interval`.
83-
Some(event) = tap_rx.next() => {
84-
let event = event.into();
92+
Some(payload) = tap_rx.next() => {
93+
let payload = payload.into();
8594

8695
// Emit notifications immediately; these don't count as a 'batch'.
87-
if let Event::Notification(_) = event {
96+
if let OutputEventsPayload::Notification(_) = payload {
8897
// If an error occurs when sending, the subscription has likely gone
8998
// away. Break the loop to terminate the thread.
90-
if let Err(err) = event_tx.send(vec![event]).await {
99+
if let Err(err) = event_tx.send(vec![payload]).await {
91100
debug!(message = "Couldn't send notification.", error = ?err);
92101
break;
93102
}
94103
} else {
95104
// Wrap tap in a 'sortable' wrapper, using the batch as a key, to
96105
// re-sort after random eviction.
97-
let event = SortableEvent { batch, event };
106+
let payload = SortableOutputEventsPayload { batch, payload };
98107

99108
// A simple implementation of "Algorithm R" per
100109
// https://en.wikipedia.org/wiki/Reservoir_sampling. As we're unable to
101110
// pluck the nth result, this is chosen over the more optimal "Algorithm L"
102111
// since discarding results isn't an option.
103112
if limit > results.len() {
104-
results.push(event);
113+
results.push(payload);
105114
} else {
106115
let random_number = rng.gen_range(0..batch);
107116
if random_number < results.len() {
108-
results[random_number] = event;
117+
results[random_number] = payload;
109118
}
110119
}
111120
// Increment the batch count, to be used for the next Algo R loop.
@@ -123,7 +132,7 @@ fn create_events_stream(
123132
let results = results
124133
.drain(..)
125134
.sorted_by_key(|r| r.batch)
126-
.map(|r| r.event)
135+
.map(|r| r.payload)
127136
.collect();
128137

129138
// If we get an error here, it likely means that the subscription has

src/api/schema/events/notification.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use async_graphql::{Enum, SimpleObject};
2+
3+
#[derive(Enum, Debug, Copy, Clone, PartialEq, Eq)]
4+
/// Event notification type
5+
pub enum EventNotificationType {
6+
/// A component was found that matched the provided pattern
7+
Matched,
8+
/// There isn't currently a component that matches this pattern
9+
NotMatched,
10+
}
11+
12+
#[derive(Debug, SimpleObject)]
13+
/// A notification regarding events observation
14+
pub struct EventNotification {
15+
/// Name of the component associated with the notification
16+
component_name: String,
17+
18+
/// Event notification type
19+
notification: EventNotificationType,
20+
}
21+
22+
impl EventNotification {
23+
pub fn new(component_name: &str, notification: EventNotificationType) -> Self {
24+
Self {
25+
component_name: component_name.to_string(),
26+
notification,
27+
}
28+
}
29+
}

src/api/schema/events/output.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use super::{
2+
log::Log,
3+
notification::{EventNotification, EventNotificationType},
4+
};
5+
use crate::api::tap::{TapNotification, TapPayload};
6+
7+
use async_graphql::Union;
8+
9+
#[derive(Union, Debug)]
10+
/// An event or a notification
11+
pub enum OutputEventsPayload {
12+
/// Log event
13+
Log(Log),
14+
15+
// Notification
16+
Notification(EventNotification),
17+
}
18+
19+
/// Convert an `api::TapPayload` to the equivalent GraphQL type.
20+
impl From<TapPayload> for OutputEventsPayload {
21+
fn from(t: TapPayload) -> Self {
22+
match t {
23+
TapPayload::Log(name, ev) => Self::Log(Log::new(&name, ev)),
24+
TapPayload::Notification(name, n) => match n {
25+
TapNotification::Matched => Self::Notification(EventNotification::new(
26+
&name,
27+
EventNotificationType::Matched,
28+
)),
29+
TapNotification::NotMatched => Self::Notification(EventNotification::new(
30+
&name,
31+
EventNotificationType::NotMatched,
32+
)),
33+
},
34+
_ => unreachable!("TODO: implement metrics"),
35+
}
36+
}
37+
}

src/api/tap.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ pub enum TapNotification {
4040
/// to be communicated back to the client to alert them about the status of the tap request.
4141
#[derive(Debug)]
4242
pub enum TapPayload {
43-
LogEvent(String, LogEvent),
44-
MetricEvent(String, LogEvent),
43+
Log(String, LogEvent),
44+
Metric(String, LogEvent),
4545
Notification(String, TapNotification),
4646
}
4747

@@ -92,7 +92,7 @@ async fn send_not_matched(mut tx: TapSender, pattern: &str) -> Result<(), SendEr
9292
tx.send(TapPayload::not_matched(pattern)).await
9393
}
9494

95-
/// Makes a `RouterSink` that relays `LogEvent` as `TapPayload::LogEvent` to a client.
95+
/// Makes a `RouterSink` that relays `Log` as `TapPayload::Log` to a client.
9696
fn make_router(mut tx: TapSender, component_name: &str) -> fanout::RouterSink {
9797
let (event_tx, mut event_rx) = futures_mpsc::unbounded();
9898
let component_name = component_name.to_string();
@@ -102,10 +102,7 @@ fn make_router(mut tx: TapSender, component_name: &str) -> fanout::RouterSink {
102102

103103
while let Some(ev) = event_rx.next().await {
104104
if let Event::Log(ev) = ev {
105-
if let Err(err) = tx
106-
.send(TapPayload::LogEvent(component_name.clone(), ev))
107-
.await
108-
{
105+
if let Err(err) = tx.send(TapPayload::Log(component_name.clone(), ev)).await {
109106
debug!(
110107
message = "Couldn't send log event.",
111108
error = ?err,
@@ -334,7 +331,7 @@ mod tests {
334331
// 3rd payload should be the log event
335332
assert!(matches!(
336333
sink_rx.recv().await,
337-
Some(TapPayload::LogEvent(returned_name, _)) if returned_name == name
334+
Some(TapPayload::Log(returned_name, _)) if returned_name == name
338335
));
339336
}
340337
}

0 commit comments

Comments
 (0)