Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ tera = "1.20.0"
test-context = "0.3.0"
thiserror = "2.0.6"
tokio = { version = "1.42.0", features = ["full"] }
tower = { version = "0.5.1", features = ["full"] }
tower-http = { version = "0.6.2", features = ["full"] }
tracing = { version = "0.1.41", features = ["attributes"] }
tracing-appender = "0.2.3"
tracing-bunyan-formatter = "0.3.10"
Expand All @@ -76,4 +78,4 @@ uuid = { version = "1.11.0", features = ["v4", "serde"] }
tokio-tungstenite = "0.24.0"
garde = { version = "0.20.0", features = ["full"] }
regex = "1.11.1"
wiremock = "0.6.2"
wiremock = "0.6.2"
1 change: 1 addition & 0 deletions settings/base.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[server]
addr = "0.0.0.0"
port = 8_080
grace_shutdown_secs = 10

[db]
host = "127.0.0.1"
Expand Down
1 change: 1 addition & 0 deletions settings/dev.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ profile = "dev"
[server]
addr = "127.0.0.1"
port = 8_080
grace_shutdown_secs = 10

[db]
host = "127.0.0.1"
Expand Down
1 change: 1 addition & 0 deletions settings/prod.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ profile = "prod"
[server]
addr = "0.0.0.0"
port = 8_080
grace_shutdown_secs = 30

[db]
host = "127.0.0.1"
Expand Down
1 change: 1 addition & 0 deletions settings/test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ profile = "test"
[server]
addr = "127.0.0.1"
port = 0
grace_shutdown_secs = 1

[db]
host = "127.0.0.1"
Expand Down
2 changes: 2 additions & 0 deletions src/configure/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use serde::Deserialize;
pub struct ServerConfig {
pub addr: String,
pub port: u16,
pub grace_shutdown_secs: i64,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change the data type to std::time::Duration:

#[serde(deserialize_with = "deserialize_duration")]
pub grace_shutdown_secs: Duration,

}

impl ServerConfig {
Expand All @@ -31,6 +32,7 @@ pub mod tests {
let config = ServerConfig {
addr: "127.0.0.1".to_string(),
port: 1024,
grace_shutdown_secs: 10,
};
assert_eq!(config.get_http_addr(), "http://127.0.0.1:1024");
}
Expand Down
45 changes: 43 additions & 2 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ use self::state::AppState;
use crate::configure::AppConfig;
use crate::error::AppResult;
use crate::router::create_router_app;
use tower_http::timeout::TimeoutLayer;
use tower_http::trace::TraceLayer;
use tracing::info;
pub mod state;
pub mod worker;

Expand All @@ -20,8 +23,46 @@ impl AppServer {
}

pub async fn run(self) -> AppResult<()> {
let router = create_router_app(self.state);
axum::serve(self.tcp, router).await?;
let shutdown = self.grace_shutdown_time();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can remove this and directly pass the timeout to the layer.

let router = create_router_app(self.state)
.layer(TraceLayer::new_for_http()) // Visibility of the request and response, change as needed.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the two layers to the create_router_app function.

.layer(TimeoutLayer::new(shutdown)); // Graceful shutdown for hosting services requries N time to complete the request before shutting down.

axum::serve(self.tcp, router)
.with_graceful_shutdown(shutdown_signal())
.await?;
Ok(())
}

fn grace_shutdown_time(&self) -> std::time::Duration {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this function.

std::time::Duration::from_secs(self.state.config.server.grace_shutdown_secs as u64)
}
}

async fn shutdown_signal() {
let ctrl_c = async {
tokio::signal::ctrl_c()
.await
.expect("Failed to install CTRL+C signal handler");
};

#[cfg(unix)]
let terminate = async {
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("Failed to install SIGTERM signal handler")
.recv()
.await;
};

#[cfg(not(unix))]
let terminate = std::future::pending::<()>();

tokio::select! {
_ = ctrl_c => {
info!("Received Ctrl-C signal. Shutting down...");
},
_ = terminate => {
info!("Received SIGTERM signal. Shutting down...");
},
}
}
12 changes: 7 additions & 5 deletions src/util/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ pub async fn join_all(tasks: Vec<Task>) -> AppResult {
tokio::spawn(async {
if let Err(e) = task.await {
if let Some(sender) = sender {
sender
.send(e)
.await
.unwrap_or_else(|_| unreachable!("This channel never closed."));
let _ = sender.send(e).await;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this change does not explain why to ignore the result, but the previous code does. It might be better to add a comment : "This channel never closed."

} else {
error!("A task failed: {e}.");
}
}
});
}

// Explicitly drop the sender to close the channel.
drop(sender);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Please change the comment to: "Drop the original unused sender."


// Return Ok(()) if all futures are completed without error.
match receiver.recv().await {
Some(err) => Err(err),
None => unreachable!("This channel never closed."),
None => Ok(()),
}
}