forked from txpipe/oura
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Introduce Redis Streams sink (txpipe#253)
- Loading branch information
Showing
9 changed files
with
226 additions
and
11 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Redis Streams | ||
|
||
A sink that implements a _Redis Stream_ producer. The sink allows different stream strategies. | ||
|
||
It is possible to send all Event to a single stream or create multiple streams, one for each event type. | ||
Both modes use `<millisecondsTime>-<sequenceNumber>` as unique entry ID (redis stream standard). | ||
With StreamStrategy `None` a single redis-stream is used for all events, a stream name can be defined by `stream_name`, the default stream name is `oura`. | ||
StreamStrategy `ByEventType` creates its own redis-stream for each event type. By appling filters it is possible to define the streams which should be created. | ||
|
||
The sink will use fingerprints as keys, if fingerprints are active otherwise the event type name in lowercase is used. | ||
|
||
## Configuration | ||
|
||
_Single Stream Mode:_ | ||
|
||
```toml | ||
[sink] | ||
type = "Redis" | ||
redis_server = "redis://default:@127.0.0.1:6379/0" | ||
stream_name = "mystream" | ||
stream_strategy = "None" | ||
``` | ||
|
||
_Multi Stream Mode:_ | ||
```toml | ||
[sink] | ||
type = "Redis" | ||
redis_server = "redis://default:@127.0.0.1:6379/0" | ||
stream_strategy = "ByEventType" | ||
``` | ||
|
||
- `type`: the literal value `Redis`. | ||
- `redis_server`: the redis server in the format `redis://[<username>][:<password>]@<hostname>[:port][/<db>]` | ||
- `stream_name` : the name of the redis stream for StreamStrategy `None`, default is "oura" if not specified | ||
- `stream_strategy` : `None` or `ByEventType` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod run; | ||
mod setup; | ||
pub use setup::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#![allow(unused_variables)] | ||
use std::sync::Arc; | ||
use serde::Serialize; | ||
use serde_json::{json}; | ||
use crate::{pipelining::StageReceiver, utils::Utils, Error, model::Event}; | ||
use super::{StreamStrategy}; | ||
|
||
#[derive(Serialize)] | ||
pub struct RedisRecord { | ||
pub event: Event, | ||
pub key: String, | ||
} | ||
|
||
impl From<Event> for RedisRecord { | ||
fn from(event: Event) -> Self { | ||
let key = key(&event); | ||
RedisRecord { | ||
event, | ||
key, | ||
} | ||
} | ||
} | ||
|
||
fn key(event : &Event) -> String { | ||
if let Some(fingerprint) = &event.fingerprint { | ||
fingerprint.clone() | ||
} else { | ||
event.data.clone().to_string().to_lowercase() | ||
} | ||
} | ||
|
||
pub fn producer_loop( | ||
input : StageReceiver, | ||
utils : Arc<Utils>, | ||
conn : &mut redis::Connection, | ||
stream_strategy : StreamStrategy, | ||
redis_stream : String, | ||
) -> Result<(), Error> { | ||
for event in input.iter() { | ||
utils.track_sink_progress(&event); | ||
let payload = RedisRecord::from(event); | ||
let stream : String; | ||
match stream_strategy { | ||
StreamStrategy::ByEventType => { | ||
stream = payload.event.data.clone().to_string().to_lowercase(); | ||
} | ||
_ => { | ||
stream = redis_stream.clone(); | ||
} | ||
} | ||
log::debug!("Stream: {:?}, Key: {:?}, Event: {:?}", stream, payload.key, payload.event); | ||
let _ : () = redis::cmd("XADD").arg(stream).arg("*").arg(&[(payload.key,json!(payload.event).to_string())]).query(conn)?; | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use redis::{Client}; | ||
use serde::Deserialize; | ||
|
||
use crate::{ | ||
pipelining::{BootstrapResult, SinkProvider, StageReceiver}, | ||
utils::WithUtils, | ||
}; | ||
|
||
use super::run::*; | ||
|
||
#[derive(Debug, Clone, Deserialize)] | ||
pub enum StreamStrategy { | ||
ByEventType, | ||
None | ||
} | ||
|
||
#[derive(Debug, Deserialize)] | ||
pub struct Config { | ||
pub redis_server : String, | ||
pub stream_strategy : Option<StreamStrategy>, | ||
pub stream_name : Option<String>, | ||
} | ||
|
||
impl SinkProvider for WithUtils<Config> { | ||
fn bootstrap(&self, input: StageReceiver) -> BootstrapResult { | ||
let client = Client::open(self.inner.redis_server.clone())?; | ||
let mut connection = client.get_connection()?; | ||
log::debug!("Connected to Redis Database!"); | ||
let stream_strategy = match self.inner.stream_strategy.clone() { | ||
Some(strategy) => { | ||
strategy | ||
}, | ||
_ => StreamStrategy::None | ||
}; | ||
let redis_stream = self.inner.stream_name.clone().unwrap_or("oura".to_string()); | ||
let utils = self.utils.clone(); | ||
let handle = std::thread::spawn(move || { | ||
producer_loop(input, utils, &mut connection, stream_strategy, redis_stream).expect("redis sink loop failed"); | ||
}); | ||
Ok(handle) | ||
} | ||
} |