-
Notifications
You must be signed in to change notification settings - Fork 28
Conversation
Codecov Report
@@ Coverage Diff @@
## master #487 +/- ##
==========================================
+ Coverage 51.56% 52.02% +0.46%
==========================================
Files 63 65 +2
Lines 3105 3160 +55
==========================================
+ Hits 1601 1644 +43
- Misses 1504 1516 +12
Continue to review full report at Codecov.
|
7c68155
to
60c3407
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good, interesting use of modules! a few comments for clarification:
However, the
#[derive(InfluxDbWriteable)]
cannot be applied on enums.
So instead, we ended up with lots of little #[derive(InfluxDbWriteable)]
structs, one for each metric, I guess?
I first started with a couple of structs but that didn't work that well.
I would be interested to hear why it didn't work out?
The idea of the enum was to have another layer of abstraction. Therefore my idea was to introduce another layer of abstraction via an enum.
We would still need the structs because they define how the data is transformed into influx data model.
The
But instead of sending a - pub struct MetricsSender(UnboundedSender<WriteQuery>);
+ pub struct MetricsSender(UnboundedSender<Metrics>); and call the method pub async fn run_metric_service(mut metics_service: MetricsService) {
loop {
match metics_service.receiver.recv().await {
- Some(write_query) => {
+ Some(metric) => { <---- Type Metrics
let _ = metics_service
.client
- .query(&write_query)
+ .query(&metric.into_query()) <---- call into_query
.await
.map_err(|e| error!("{}", e));
}
None => {
warn!("All senders have been dropped!");
return;
}
}
}
} However for this to work, we need to implement impl Metrics {
fn into_query(self) -> WriteQuery {
match metric {
RoundParamSum(metric) => metric.into_query()
...
}
}
}
// or
pub async fn run_metric_service(mut metics_service: MetricsService) {
loop {
match metics_service.receiver.recv().await {
Some(metric) => {
let query = match metric {
RoundParamSum(metric) => metric.into_query(),
...
};
let _ = metics_service
.client
.query(&query())
.await
.map_err(|e| error!("{}", e));
}
None => {
warn!("All senders have been dropped!");
return;
}
}
}
} In both cases we need to alter the match pattern manually once we add/change or remove a metric which is quite annoying I think. |
} | ||
} | ||
|
||
pub async fn run_metric_service(mut metics_service: MetricsService) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One downside of this implementation is that we process the request one by one. If the queries are slow, the queries will accumulate in the channel. Maybe we could make that channel bounded to apply some backpressure. On the longer run, we could use tower to benefit from timeouts, backpressure and concurrent requests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my plan is, to create a PR on the influxdb crate which implements #[derive(InfluxDbWriteable)] for enums.
That would be nice, but your workaround is very clean too. This is a good first implementation imo. I'd just convert the unbounded channels to bounded ones to be on the safe side.
@finiteprods @little-dude thanks for the review👍 @little-dude I agree that is a good idea to use |
Yes exactly |
60c3407
to
12d4b94
Compare
12d4b94
to
e8cfb8e
Compare
First part of the
Implement data collection to InfluxDB
task.This PR focus on the definition of the influx data models and the implementation of the metrics service + sender. The integration of the metrics service into the state machine is addressed in a separate PR.
metrics/mod.rs
The
mod.rs
file contains theMetricsService
, theMetricsSender
and the functions to generate metrics.The
MetricsSender
communicates with theMetricsService
via an unbounded channel. The unbounded channelaccepts the type
WriteQuery
. TheWriteQuery
is a struct of theinfluxdb
crate.My actual plan was to build a wrapper around that struct like:
However, the
#[derive(InfluxDbWriteable)]
cannot be applied on enums. I would have had to implement the methodinto_query
on each variant. So I decided against it.I tried to find a solution to encode some properties/information of the metrics into the code. I first started with a couple of structs but that didn't work that well. In the end I used the module system.
Of course, I could have implemented it more generic and save some lines of code. However, I think the abstraction makes it easier to use and less error-prone.
Structure:
The
models.rs
file only contains the influx data models. ThroughInfluxDbWriteable
the structs are automatically implement theinto_query
methods.