Skip to content
This repository was archived by the owner on Oct 18, 2023. It is now read-only.

Commit 2c39268

Browse files
bors[bot]penberg
andauthored
Merge #361
361: Server heartbeats r=MarinPostma a=penberg This adds support for server heartbeats. The patch adds three new command line options: `--heartbeat-url URL` is the URL to send a HTTP POST request to periodically to indicate server heatbeat. `--heartbeat-auth AUTH` is the HTTP "Authorization" header to send as part of the HTTP POST request. `--heartbeat-period SECS` is the heartbeat period in seconds. The payload of the HTTP POST request is pretty simple for now: ```json {"rows_read":1234,"rows_written":8765} ``` Fixes #343 Co-authored-by: Pekka Enberg <[email protected]>
2 parents 6d7ffb3 + d96ea35 commit 2c39268

File tree

7 files changed

+87
-5
lines changed

7 files changed

+87
-5
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sqld/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ tempfile = "3.3.0"
5959
memmap = "0.7.0"
6060
mimalloc = "0.1.36"
6161
sha256 = "1.1.3"
62+
reqwest = { version = "0.11.16", features = ["json"] }
6263

6364
[dev-dependencies]
6465
proptest = "1.0.0"

sqld/src/heartbeat.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use std::time::Duration;
2+
use tokio::time::sleep;
3+
4+
use crate::http::stats::StatsResponse;
5+
use crate::stats::Stats;
6+
7+
pub async fn server_heartbeat(
8+
url: String,
9+
auth: Option<String>,
10+
update_period: Duration,
11+
stats: Stats,
12+
) {
13+
let client = reqwest::Client::new();
14+
loop {
15+
sleep(update_period).await;
16+
let body = StatsResponse {
17+
rows_read_count: stats.rows_read(),
18+
rows_written_count: stats.rows_written(),
19+
};
20+
let request = client.post(&url);
21+
let request = if let Some(ref auth) = auth {
22+
request.header("Authorization", auth.clone())
23+
} else {
24+
request
25+
};
26+
let request = request.json(&body);
27+
if let Err(err) = request.send().await {
28+
tracing::warn!("Error sending heartbeat: {}", err);
29+
}
30+
}
31+
}

sqld/src/http/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mod hrana_over_http;
2-
mod stats;
2+
pub mod stats;
33
mod types;
44

55
use std::net::SocketAddr;

sqld/src/http/stats.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use serde::Serialize;
44
use crate::stats::Stats;
55

66
#[derive(Serialize)]
7-
struct StatsResponse {
8-
rows_read_count: usize,
9-
rows_written_count: usize,
7+
pub struct StatsResponse {
8+
pub rows_read_count: usize,
9+
pub rows_written_count: usize,
1010
}
1111

1212
pub fn handle_stats(stats: &Stats) -> Response<Body> {

sqld/src/lib.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub use sqld_libsql_bindings as libsql;
3232
mod auth;
3333
pub mod database;
3434
mod error;
35+
mod heartbeat;
3536
mod hrana;
3637
mod http;
3738
mod postgres;
@@ -90,6 +91,9 @@ pub struct Config {
9091
pub idle_shutdown_timeout: Option<Duration>,
9192
pub load_from_dump: Option<PathBuf>,
9293
pub max_log_size: u64,
94+
pub heartbeat_url: Option<String>,
95+
pub heartbeat_auth: Option<String>,
96+
pub heartbeat_period: Duration,
9397
}
9498

9599
async fn run_service(
@@ -133,7 +137,7 @@ async fn run_service(
133137
hrana_upgrade_tx,
134138
config.enable_http_console,
135139
idle_shutdown_layer,
136-
stats,
140+
stats.clone(),
137141
));
138142
}
139143

@@ -145,6 +149,32 @@ async fn run_service(
145149
});
146150
}
147151

152+
match &config.heartbeat_url {
153+
Some(heartbeat_url) => {
154+
let heartbeat_period = config.heartbeat_period;
155+
tracing::info!(
156+
"Server sending heartbeat to URL {} every {:?}",
157+
heartbeat_url,
158+
heartbeat_period,
159+
);
160+
let heartbeat_url = heartbeat_url.clone();
161+
let heartbeat_auth = config.heartbeat_auth.clone();
162+
join_set.spawn(async move {
163+
heartbeat::server_heartbeat(
164+
heartbeat_url,
165+
heartbeat_auth,
166+
heartbeat_period,
167+
stats.clone(),
168+
)
169+
.await;
170+
Ok(())
171+
});
172+
}
173+
None => {
174+
tracing::warn!("No server heartbeat configured")
175+
}
176+
}
177+
148178
Ok(())
149179
}
150180

sqld/src/main.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,22 @@ struct Cli {
133133

134134
#[clap(subcommand)]
135135
utils: Option<UtilsSubcommands>,
136+
137+
/// The URL to send a server heartbeat `POST` request to.
138+
/// By default, the server doesn't send a heartbeat.
139+
#[clap(long, env = "SQLD_HEARTBEAT_URL")]
140+
heartbeat_url: Option<String>,
141+
142+
/// The HTTP "Authornization" header to include in the a server heartbeat
143+
/// `POST` request.
144+
/// By default, the server doesn't send a heartbeat.
145+
#[clap(long, env = "SQLD_HEARTBEAT_AUTH")]
146+
heartbeat_auth: Option<String>,
147+
148+
/// The heartbeat time period in seconds.
149+
/// By default, the the period is 30 seconds.
150+
#[clap(long, env = "SQLD_HEARTBEAT_PERIOD_S", default_value = "30")]
151+
heartbeat_period_s: u64,
136152
}
137153

138154
#[derive(clap::Subcommand, Debug)]
@@ -231,6 +247,9 @@ fn config_from_args(args: Cli) -> Result<Config> {
231247
idle_shutdown_timeout: args.idle_shutdown_timeout_s.map(Duration::from_secs),
232248
load_from_dump: args.load_from_dump,
233249
max_log_size: args.max_log_size,
250+
heartbeat_url: args.heartbeat_url,
251+
heartbeat_auth: args.heartbeat_auth,
252+
heartbeat_period: Duration::from_secs(args.heartbeat_period_s),
234253
})
235254
}
236255

0 commit comments

Comments
 (0)