Skip to main content

harmos_runtime/streams/
series.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12/* === Re-Exports === */
13
14/* === Imports === */
15
16// Standard Library Imports
17use std::any::Any;
18use std::ops::Bound;
19use std::sync::Arc;
20// Third Party Imports
21use harmos_domain::Aggregate;
22use harmos_storage::signal::Summary;
23use tokio::sync::{mpsc, oneshot};
24// Local Imports
25use super::BeneathWindow;
26use crate::aggregate;
27use crate::{state::At, state::Position, streams::Definition};
28
29/* === Constants === */
30
31/* === Macros === */
32
33/* === Type Aliases === */
34
35pub(crate) type Served = At<Arc<dyn Any + Send + Sync>>;
36
37/* === Structs === */
38
39#[derive(Clone, Copy, Debug, PartialEq)]
40pub struct Point {
41    pub axis: u64,
42    pub value: f64,
43}
44
45// One plot bucket of a field: the points a chart draws, and the running
46// summary every mergeable aggregate is answered from.
47#[derive(Clone, Debug, PartialEq)]
48pub struct Bucket {
49    pub field: String,
50    pub first: Point,
51    pub last: Point,
52    pub min: Point,
53    pub max: Point,
54    summary: Summary,
55}
56
57#[derive(Clone, Debug, PartialEq)]
58pub struct Aggregated {
59    pub field: String,
60    pub value: f64,
61}
62
63pub(crate) struct Opened {
64    pub(crate) source: Result<Box<dyn crate::streams::RowSource>, crate::streams::ReadError>,
65    pub(crate) permit: tokio::sync::OwnedSemaphorePermit,
66}
67
68#[derive(Clone)]
69pub(crate) struct Tier {
70    sender: mpsc::Sender<Request>,
71    readers: Arc<tokio::sync::Semaphore>,
72}
73
74/* === Enums === */
75
76pub(crate) enum Request {
77    Reader {
78        stream: Definition,
79        track: Vec<String>,
80        start: Bound<Position>,
81        end: Bound<Position>,
82        limits: crate::streams::ReadLimits,
83        permit: tokio::sync::OwnedSemaphorePermit,
84        reply: oneshot::Sender<Opened>,
85    },
86    Rows {
87        stream: Definition,
88        track: Vec<String>,
89        start: Bound<Position>,
90        end: Bound<Position>,
91        reply: oneshot::Sender<Result<Vec<Served>, BeneathWindow>>,
92    },
93    Buckets {
94        stream: Definition,
95        track: Vec<String>,
96        start: Bound<u64>,
97        end: Bound<u64>,
98        budget: usize,
99        reply: oneshot::Sender<Result<Vec<Bucket>, BeneathWindow>>,
100    },
101    Aggregate {
102        stream: Definition,
103        track: Vec<String>,
104        start: Bound<u64>,
105        end: Bound<u64>,
106        kind: Aggregate,
107        reply: oneshot::Sender<Result<Vec<Aggregated>, BeneathWindow>>,
108    },
109    Seal {
110        stream: Definition,
111        track: Vec<String>,
112        reply: oneshot::Sender<Position>,
113    },
114}
115
116/* === Traits === */
117
118/* === Implementations === */
119
120impl Bucket {
121    pub fn point(field: impl Into<String>, point: Point) -> Self {
122        let mut summary = Summary::new();
123        summary.push(point.value);
124
125        Self {
126            field: field.into(),
127            first: point,
128            last: point,
129            min: point,
130            max: point,
131            summary,
132        }
133    }
134
135    pub fn merge(&self, later: &Self) -> Self {
136        assert_eq!(self.field, later.field, "a bucket merges only with a bucket of its own field");
137
138        Self {
139            field: self.field.clone(),
140            first: earliest(self.first, later.first),
141            last: latest(self.last, later.last),
142            min: lower(self.min, later.min),
143            max: higher(self.max, later.max),
144            summary: self.summary.merge(&later.summary),
145        }
146    }
147
148    pub fn count(&self) -> u64 {
149        self.summary.count()
150    }
151
152    pub fn summary(&self) -> &Summary {
153        &self.summary
154    }
155
156    // Mergeable members only: a bucket keeps no values, so median and
157    // percentiles answer `None` and are asked of the values instead.
158    pub fn aggregate(&self, kind: Aggregate) -> Option<f64> {
159        aggregate::of_summary(&self.summary, kind)
160    }
161}
162
163impl Tier {
164    pub(crate) fn channel(capacity: usize) -> (Self, mpsc::Receiver<Request>) {
165        let (sender, receiver) = mpsc::channel(capacity.max(1));
166
167        (
168            Self {
169                sender,
170                readers: Arc::new(tokio::sync::Semaphore::new(capacity.max(1))),
171            },
172            receiver,
173        )
174    }
175
176    pub(crate) fn reader_permit(&self) -> Result<tokio::sync::OwnedSemaphorePermit, crate::streams::ReadError> {
177        self.readers.clone().try_acquire_owned().map_err(|_| crate::streams::ReadError::Busy)
178    }
179
180    pub(crate) async fn reader(
181        &self, stream: Definition, track: Vec<String>, start: Bound<Position>, end: Bound<Position>, limits: crate::streams::ReadLimits,
182        permit: tokio::sync::OwnedSemaphorePermit,
183    ) -> Result<Opened, crate::streams::ReadError> {
184        let (reply, answer) = oneshot::channel();
185        self.sender
186            .send(Request::Reader {
187                stream,
188                track,
189                start,
190                end,
191                limits,
192                permit,
193                reply,
194            })
195            .await
196            .map_err(|_| crate::streams::ReadError::Closed)?;
197        answer.await.map_err(|_| crate::streams::ReadError::Closed)
198    }
199
200    pub(crate) async fn rows(
201        &self, stream: Definition, track: Vec<String>, start: Bound<Position>, end: Bound<Position>,
202    ) -> Result<Vec<Served>, BeneathWindow> {
203        let (reply, answer) = oneshot::channel();
204        self.sender.send(Request::Rows { stream, track, start, end, reply }).await.map_err(|_| BeneathWindow::Unserved)?;
205
206        answer.await.map_err(|_| BeneathWindow::Unserved)?
207    }
208
209    pub(crate) async fn buckets(
210        &self, stream: Definition, track: Vec<String>, start: Bound<u64>, end: Bound<u64>, budget: usize,
211    ) -> Result<Vec<Bucket>, BeneathWindow> {
212        let (reply, answer) = oneshot::channel();
213        self.sender.send(Request::Buckets { stream, track, start, end, budget, reply }).await.map_err(|_| BeneathWindow::Unserved)?;
214
215        answer.await.map_err(|_| BeneathWindow::Unserved)?
216    }
217
218    pub(crate) async fn aggregate(
219        &self, stream: Definition, track: Vec<String>, start: Bound<u64>, end: Bound<u64>, kind: Aggregate,
220    ) -> Result<Vec<Aggregated>, BeneathWindow> {
221        let (reply, answer) = oneshot::channel();
222        self.sender.send(Request::Aggregate { stream, track, start, end, kind, reply }).await.map_err(|_| BeneathWindow::Unserved)?;
223
224        answer.await.map_err(|_| BeneathWindow::Unserved)?
225    }
226
227    pub(crate) async fn seal(&self, stream: Definition, track: Vec<String>) -> Position {
228        let (reply, answer) = oneshot::channel();
229
230        if self.sender.send(Request::Seal { stream, track, reply }).await.is_err() {
231            return Position::ORIGIN;
232        }
233
234        answer.await.unwrap_or(Position::ORIGIN)
235    }
236}
237
238/* === Public Functions === */
239
240/* === Private Functions === */
241
242fn earliest(held: Point, next: Point) -> Point {
243    if next.axis < held.axis { next } else { held }
244}
245
246fn latest(held: Point, next: Point) -> Point {
247    if next.axis >= held.axis { next } else { held }
248}
249
250fn lower(held: Point, next: Point) -> Point {
251    if next.value < held.value { next } else { held }
252}
253
254fn higher(held: Point, next: Point) -> Point {
255    if next.value > held.value { next } else { held }
256}
257
258/* === Tests === */