Left-right single-writer, multi-reader index
This commit is contained in:
+12
-12
@@ -2,11 +2,10 @@ use std::collections::BTreeMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::engine::{EngineMessage, IngestedLine, MetricSample};
|
||||
use crate::engine::{EngineHandle, EngineMessage, IngestedLine, MetricSample};
|
||||
use crate::timestamp::Timestamp;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
const POLL_INTERVAL: Duration = Duration::from_millis(200);
|
||||
|
||||
@@ -14,7 +13,7 @@ const METRICS_POLL_INTERVAL: Duration = Duration::from_secs(15);
|
||||
|
||||
/// Reads stdin line by line, sequences and timestamps each line, and forwards it
|
||||
/// to the engine task.
|
||||
pub async fn run(engine: mpsc::Sender<EngineMessage>) {
|
||||
pub async fn run(engine: EngineHandle) {
|
||||
let stdin = tokio::io::stdin();
|
||||
let mut lines = BufReader::new(stdin).lines();
|
||||
let mut sequence: u64 = 0;
|
||||
@@ -27,7 +26,7 @@ pub async fn run(engine: mpsc::Sender<EngineMessage>) {
|
||||
};
|
||||
sequence += 1;
|
||||
|
||||
if engine.send(EngineMessage::Ingest(line)).await.is_err() {
|
||||
if engine.tx.send(EngineMessage::Ingest(line)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -59,7 +58,7 @@ async fn open(path: &Path) -> Option<(File, FileId)> {
|
||||
/// Tails `path` like `tail -f`, resuming from the start if the file is deleted/recreated or
|
||||
/// truncated in place rather than exiting or getting stuck; `sequence` keeps incrementing
|
||||
/// across a recreation since it's ottyl's own line counter, not tied to the source file.
|
||||
pub async fn run_file(engine: mpsc::Sender<EngineMessage>, path: PathBuf) {
|
||||
pub async fn run_file(engine: EngineHandle, path: PathBuf) {
|
||||
let mut sequence: u64 = 0;
|
||||
|
||||
'reopen: loop {
|
||||
@@ -109,7 +108,7 @@ pub async fn run_file(engine: mpsc::Sender<EngineMessage>, path: PathBuf) {
|
||||
};
|
||||
sequence += 1;
|
||||
|
||||
if engine.send(EngineMessage::Ingest(line)).await.is_err() {
|
||||
if engine.tx.send(EngineMessage::Ingest(line)).await.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +123,7 @@ pub async fn run_file(engine: mpsc::Sender<EngineMessage>, path: PathBuf) {
|
||||
/// Polls `url` on a fixed interval for a Prometheus/OpenMetrics-format response, pushing every parsed
|
||||
/// sample straight into the engine. Errors (unreachable endpoint, non-2xx response) are only logged —
|
||||
/// polling keeps retrying on the next tick rather than exiting.
|
||||
pub async fn run_metrics_poll(engine: mpsc::Sender<EngineMessage>, url: String) {
|
||||
pub async fn run_metrics_poll(engine: EngineHandle, url: String) {
|
||||
let client = reqwest::Client::new();
|
||||
loop {
|
||||
match client.get(&url).send().await {
|
||||
@@ -133,7 +132,7 @@ pub async fn run_metrics_poll(engine: mpsc::Sender<EngineMessage>, url: String)
|
||||
Ok(body) => {
|
||||
let now = Timestamp::now();
|
||||
for sample in parse_openmetrics(&body, now) {
|
||||
if engine.send(EngineMessage::IngestMetricSample(sample)).await.is_err() {
|
||||
if engine.tx.send(EngineMessage::IngestMetricSample(sample)).await.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -239,6 +238,7 @@ mod openmetrics_tests {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::Write;
|
||||
use crate::engine::test_handle;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
async fn recv_text(rx: &mut mpsc::Receiver<EngineMessage>) -> String {
|
||||
@@ -258,7 +258,7 @@ mod tests {
|
||||
let path = dir.path().join("app.log");
|
||||
std::fs::write(&path, "first\n").unwrap();
|
||||
|
||||
let (tx, mut rx) = mpsc::channel(16);
|
||||
let (tx, mut rx) = test_handle(16);
|
||||
tokio::spawn(run_file(tx, path.clone()));
|
||||
|
||||
assert_eq!(recv_text(&mut rx).await, "first");
|
||||
@@ -274,7 +274,7 @@ mod tests {
|
||||
let path = dir.path().join("app.log");
|
||||
std::fs::write(&path, "before-restart\n").unwrap();
|
||||
|
||||
let (tx, mut rx) = mpsc::channel(16);
|
||||
let (tx, mut rx) = test_handle(16);
|
||||
tokio::spawn(run_file(tx, path.clone()));
|
||||
|
||||
assert_eq!(recv_text(&mut rx).await, "before-restart");
|
||||
@@ -291,7 +291,7 @@ mod tests {
|
||||
let path = dir.path().join("app.log");
|
||||
std::fs::write(&path, "long line before truncation\n").unwrap();
|
||||
|
||||
let (tx, mut rx) = mpsc::channel(16);
|
||||
let (tx, mut rx) = test_handle(16);
|
||||
tokio::spawn(run_file(tx, path.clone()));
|
||||
|
||||
assert_eq!(recv_text(&mut rx).await, "long line before truncation");
|
||||
@@ -312,7 +312,7 @@ mod tests {
|
||||
let path = dir.path().join("app.log");
|
||||
std::fs::write(&path, "one\n").unwrap();
|
||||
|
||||
let (tx, mut rx) = mpsc::channel(16);
|
||||
let (tx, mut rx) = test_handle(16);
|
||||
tokio::spawn(run_file(tx, path.clone()));
|
||||
|
||||
let first = match tokio::time::timeout(Duration::from_secs(2), rx.recv())
|
||||
|
||||
Reference in New Issue
Block a user