clickhouse_qol/worker/
insert_worker.rs1use std::time::Duration;
2
3use clickhouse::{Client, Row};
4use serde::Serialize;
5use tokio::sync::mpsc::{Receiver, Sender};
6
7pub enum WorkerCommand<T>
8where
9 T: Row + Serialize,
10{
11 Insert(T),
12 Ping,
13}
14
15pub struct StreamInsertWorker<T>
16where
17 T: Row + Serialize,
18{
19 rx: Receiver<WorkerCommand<T>>,
20 client: Client,
21 max_size: u64,
22 period_sec: u64,
23 table_name: String,
24}
25
26impl<T> StreamInsertWorker<T>
27where
28 T: Row + Serialize,
29{
30 pub fn new(client: Client, table_name: String, max_size: u64, period_sec: u64) -> (Self, Sender<WorkerCommand<T>>) {
31 let (tx, rx) = tokio::sync::mpsc::channel::<WorkerCommand<T>>(1024);
32 let worker = StreamInsertWorker {
33 rx,
34 client,
35 max_size,
36 period_sec,
37 table_name,
38 };
39 (worker, tx)
40 }
41
42 pub async fn recv(&mut self) -> anyhow::Result<()> {
43 let mut current = 0;
44 let mut inserter = self
45 .client
46 .inserter(&self.table_name)?
47 .with_max_bytes(100_000)
48 .with_max_rows(self.max_size)
49 .with_period(Some(Duration::from_secs(self.period_sec)));
50
51 while let Some(command) = self.rx.recv().await {
52 match command {
53 WorkerCommand::Insert(data) => {
54 let row = data;
55 match inserter.write(&row) {
56 Ok(_) => current += 1,
57 Err(e) => {
58 log::error!("failed to write: {:?}", e);
59 }
60 }
61
62 if current >= self.max_size {
63 match inserter.commit().await {
64 Ok(res) => {
65 log::info!("commit result: {:?}", res);
66 current = 0;
67 }
68 Err(e) => {
69 log::error!("failed to commit: {:?}", e);
70 }
71 }
72 }
73 }
74 WorkerCommand::Ping => {
75 if let Some(left) = inserter.time_left() {
76 if left.as_millis() <= 100 {
77 match inserter.commit().await {
78 Ok(res) => {
79 if res.rows > 0 {
80 log::info!("commit result: {:?}", res);
81 }
82 current = 0;
83 }
84 Err(e) => {
85 log::error!("failed to commit: {:?}", e);
86 }
87 }
88 }
89 }
90 }
91 }
92 }
93
94 Ok(())
95 }
96}