feat(server): avoid duplicate messages being passed for repo actor
All checks were successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
All checks were successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/cron/cron-docker-builder Pipeline was successful
ci/woodpecker/cron/push-next Pipeline was successful
ci/woodpecker/cron/tag-created Pipeline was successful
This commit is contained in:
parent
2c5f497be2
commit
da5bc69508
8 changed files with 104 additions and 39 deletions
|
@ -17,6 +17,7 @@ pub async fn advance_next(
|
|||
repo_config: config::RepoConfig,
|
||||
forge: gitforge::Forge,
|
||||
addr: Addr<super::RepoActor>,
|
||||
message_token: super::MessageToken,
|
||||
) {
|
||||
let next_commit = find_next_commit_on_dev(next, dev_commit_history);
|
||||
let Some(commit) = next_commit else {
|
||||
|
@ -36,7 +37,7 @@ pub async fn advance_next(
|
|||
warn!(?err, "Failed")
|
||||
}
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
addr.do_send(ValidateRepo)
|
||||
addr.do_send(ValidateRepo { message_token })
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
|
@ -78,6 +79,7 @@ pub async fn advance_main(
|
|||
repo_config: config::RepoConfig,
|
||||
forge: gitforge::Forge,
|
||||
addr: Addr<RepoActor>,
|
||||
message_token: super::MessageToken,
|
||||
) {
|
||||
info!("Advancing main to next");
|
||||
if let Err(err) = forge.branch_reset(
|
||||
|
@ -87,7 +89,7 @@ pub async fn advance_main(
|
|||
) {
|
||||
warn!(?err, "Failed")
|
||||
};
|
||||
addr.do_send(ValidateRepo)
|
||||
addr.do_send(ValidateRepo { message_token })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -11,11 +11,13 @@ use crate::server::{
|
|||
actors::repo::webhook::WebhookAuth,
|
||||
config::{RepoConfig, RepoDetails, Webhook},
|
||||
gitforge,
|
||||
types::MessageToken,
|
||||
};
|
||||
|
||||
use self::webhook::WebhookId;
|
||||
|
||||
pub struct RepoActor {
|
||||
message_token: MessageToken,
|
||||
span: tracing::Span,
|
||||
details: RepoDetails,
|
||||
webhook: Webhook,
|
||||
|
@ -40,6 +42,7 @@ impl RepoActor {
|
|||
};
|
||||
debug!(?forge, "new");
|
||||
Self {
|
||||
message_token: MessageToken::new(),
|
||||
span,
|
||||
details,
|
||||
webhook,
|
||||
|
@ -137,25 +140,52 @@ impl Handler<LoadedConfig> for RepoActor {
|
|||
.into_actor(self)
|
||||
.wait(ctx);
|
||||
}
|
||||
ctx.address().do_send(ValidateRepo);
|
||||
ctx.address().do_send(ValidateRepo {
|
||||
message_token: self.message_token,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Message)]
|
||||
#[rtype(result = "()")]
|
||||
pub struct ValidateRepo;
|
||||
pub struct ValidateRepo {
|
||||
message_token: MessageToken,
|
||||
}
|
||||
impl ValidateRepo {
|
||||
pub const fn new(message_token: MessageToken) -> Self {
|
||||
Self { message_token }
|
||||
}
|
||||
}
|
||||
impl Handler<ValidateRepo> for RepoActor {
|
||||
type Result = ();
|
||||
#[tracing::instrument(name = "RepoActor::ValidateRepo", skip_all, fields(repo = %self.details))]
|
||||
fn handle(&mut self, _msg: ValidateRepo, ctx: &mut Self::Context) -> Self::Result {
|
||||
#[tracing::instrument(name = "RepoActor::ValidateRepo", skip_all, fields(repo = %self.details, token = %msg.message_token))]
|
||||
fn handle(&mut self, msg: ValidateRepo, ctx: &mut Self::Context) -> Self::Result {
|
||||
match msg.message_token {
|
||||
message_token if self.message_token < message_token => {
|
||||
info!(%message_token, "New message token");
|
||||
self.message_token = msg.message_token;
|
||||
}
|
||||
message_token if self.message_token > message_token => {
|
||||
info!("Dropping message from previous generation");
|
||||
return; // message is expired
|
||||
}
|
||||
_ => {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
info!("Message Received");
|
||||
if let Some(repo_config) = self.details.repo_config.clone() {
|
||||
let forge = self.forge.clone();
|
||||
let addr = ctx.address();
|
||||
async move { forge.branches_validate_positions(repo_config, addr).await }
|
||||
.in_current_span()
|
||||
.into_actor(self)
|
||||
.wait(ctx);
|
||||
let message_token = self.message_token;
|
||||
async move {
|
||||
forge
|
||||
.branches_validate_positions(repo_config, addr, message_token)
|
||||
.await
|
||||
}
|
||||
.in_current_span()
|
||||
.into_actor(self)
|
||||
.wait(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +200,7 @@ pub struct StartMonitoring {
|
|||
}
|
||||
impl Handler<StartMonitoring> for RepoActor {
|
||||
type Result = ();
|
||||
#[tracing::instrument(name = "RepoActor::StartMonitoring", skip_all, fields(repo = %self.details, main = %msg.main, next= %msg.next, dev = %msg.dev))]
|
||||
#[tracing::instrument(name = "RepoActor::StartMonitoring", skip_all, fields(token = %self.message_token, repo = %self.details, main = %msg.main, next= %msg.next, dev = %msg.dev))]
|
||||
fn handle(&mut self, msg: StartMonitoring, ctx: &mut Self::Context) -> Self::Result {
|
||||
info!("Message Received");
|
||||
let Some(repo_config) = self.details.repo_config.clone() else {
|
||||
|
@ -186,15 +216,22 @@ impl Handler<StartMonitoring> for RepoActor {
|
|||
let forge = self.forge.clone();
|
||||
|
||||
if next_ahead_of_main {
|
||||
status::check_next(msg.next, addr, forge)
|
||||
status::check_next(msg.next, addr, forge, self.message_token)
|
||||
.in_current_span()
|
||||
.into_actor(self)
|
||||
.wait(ctx);
|
||||
} else if dev_ahead_of_next {
|
||||
branch::advance_next(msg.next, msg.dev_commit_history, repo_config, forge, addr)
|
||||
.in_current_span()
|
||||
.into_actor(self)
|
||||
.wait(ctx);
|
||||
branch::advance_next(
|
||||
msg.next,
|
||||
msg.dev_commit_history,
|
||||
repo_config,
|
||||
forge,
|
||||
addr,
|
||||
self.message_token,
|
||||
)
|
||||
.in_current_span()
|
||||
.into_actor(self)
|
||||
.wait(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -226,7 +263,7 @@ impl Handler<AdvanceMainTo> for RepoActor {
|
|||
};
|
||||
let forge = self.forge.clone();
|
||||
let addr = ctx.address();
|
||||
branch::advance_main(msg.0, repo_config, forge, addr)
|
||||
branch::advance_main(msg.0, repo_config, forge, addr, self.message_token)
|
||||
.in_current_span()
|
||||
.into_actor(self)
|
||||
.wait(ctx);
|
||||
|
|
|
@ -2,7 +2,7 @@ use actix::prelude::*;
|
|||
use gix::trace::warn;
|
||||
use tracing::info;
|
||||
|
||||
use crate::server::{actors::repo::ValidateRepo, gitforge};
|
||||
use crate::server::{actors::repo::ValidateRepo, gitforge, types::MessageToken};
|
||||
|
||||
use super::AdvanceMainTo;
|
||||
|
||||
|
@ -10,6 +10,7 @@ pub async fn check_next(
|
|||
next: gitforge::Commit,
|
||||
addr: Addr<super::RepoActor>,
|
||||
forge: gitforge::Forge,
|
||||
message_token: MessageToken,
|
||||
) {
|
||||
// get the status - pass, fail, pending (all others map to fail, e.g. error)
|
||||
let status = forge.commit_status(&next).await;
|
||||
|
@ -20,7 +21,7 @@ pub async fn check_next(
|
|||
}
|
||||
gitforge::CommitStatus::Pending => {
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
|
||||
addr.do_send(ValidateRepo);
|
||||
addr.do_send(ValidateRepo { message_token });
|
||||
}
|
||||
gitforge::CommitStatus::Fail => {
|
||||
warn!("Checks have failed");
|
||||
|
|
|
@ -204,13 +204,16 @@ impl Handler<WebhookMessage> for RepoActor {
|
|||
type Result = ();
|
||||
|
||||
#[allow(clippy::cognitive_complexity)] // TODO: (#49) reduce complexity
|
||||
#[tracing::instrument(name = "RepoActor::WebhookMessage", skip_all, fields(token = %self.message_token, repo = %self.details))]
|
||||
fn handle(&mut self, msg: WebhookMessage, ctx: &mut Self::Context) -> Self::Result {
|
||||
if msg.authorisation() != self.webhook_auth {
|
||||
return; // invalid auth
|
||||
}
|
||||
let id = msg.id();
|
||||
let span = tracing::info_span!("handle", %id);
|
||||
let _guard = span.enter();
|
||||
let body = msg.body();
|
||||
debug!(?id, "RepoActor received message");
|
||||
debug!(%id, "RepoActor received message");
|
||||
match serde_json::from_str::<Push>(body) {
|
||||
Err(err) => debug!(?err, %body, "Not a 'push'"),
|
||||
Ok(push) => {
|
||||
|
@ -225,10 +228,9 @@ impl Handler<WebhookMessage> for RepoActor {
|
|||
Branch::Main => {
|
||||
if self.last_main_commit == Some(push.commit()) {
|
||||
info!(
|
||||
?id,
|
||||
"Ignoring - already aware of branch '{}' at commit '{}'",
|
||||
config.branches().main(),
|
||||
push.commit()
|
||||
branch = %config.branches().main(),
|
||||
commit = %push.commit(),
|
||||
"Ignoring - already aware of branch at commit",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -237,10 +239,9 @@ impl Handler<WebhookMessage> for RepoActor {
|
|||
Branch::Next => {
|
||||
if self.last_next_commit == Some(push.commit()) {
|
||||
info!(
|
||||
?id,
|
||||
"Ignoring - already aware of branch '{}' at commit '{}'",
|
||||
config.branches().next(),
|
||||
push.commit()
|
||||
branch = %config.branches().next(),
|
||||
commit = %push.commit(),
|
||||
"Ignoring - already aware of branch at commit",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -249,22 +250,23 @@ impl Handler<WebhookMessage> for RepoActor {
|
|||
Branch::Dev => {
|
||||
if self.last_dev_commit == Some(push.commit()) {
|
||||
info!(
|
||||
?id,
|
||||
"Ignoring - already aware of branch '{}' at commit '{}'",
|
||||
config.branches().dev(),
|
||||
push.commit()
|
||||
branch = %config.branches().dev(),
|
||||
commit = %push.commit(),
|
||||
"Ignoring - already aware of branch at commit",
|
||||
);
|
||||
return;
|
||||
}
|
||||
self.last_dev_commit.replace(push.commit())
|
||||
}
|
||||
};
|
||||
let message_token = self.message_token.next();
|
||||
info!(
|
||||
token = %message_token,
|
||||
?branch,
|
||||
commit = %push.commit(),
|
||||
"New commit for branch - assessing branch positions"
|
||||
"New commit"
|
||||
);
|
||||
ctx.address().do_send(ValidateRepo);
|
||||
ctx.address().do_send(ValidateRepo { message_token });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::server::{
|
|||
actors::repo::{RepoActor, StartMonitoring, ValidateRepo},
|
||||
config::{BranchName, GitDir, RepoConfig, RepoDetails},
|
||||
gitforge::{self, forgejo::branch::ValidatedPositions, RepoCloneError},
|
||||
types::GitRef,
|
||||
types::{GitRef, MessageToken},
|
||||
};
|
||||
|
||||
struct ForgeJo;
|
||||
|
@ -45,7 +45,12 @@ impl super::ForgeLike for ForgeJoEnv {
|
|||
file::contents_get(&self.repo_details, &self.net, branch, file_path).await
|
||||
}
|
||||
|
||||
async fn branches_validate_positions(&self, repo_config: RepoConfig, addr: Addr<RepoActor>) {
|
||||
async fn branches_validate_positions(
|
||||
&self,
|
||||
repo_config: RepoConfig,
|
||||
addr: Addr<RepoActor>,
|
||||
message_token: MessageToken,
|
||||
) {
|
||||
match branch::validate_positions(self, repo_config).await {
|
||||
Ok(ValidatedPositions {
|
||||
main,
|
||||
|
@ -63,7 +68,7 @@ impl super::ForgeLike for ForgeJoEnv {
|
|||
Err(err) => {
|
||||
warn!("{}", err);
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
addr.do_send(ValidateRepo);
|
||||
addr.do_send(ValidateRepo::new(message_token));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::server::{
|
|||
actors::repo::RepoActor,
|
||||
config::{BranchName, GitDir, RepoConfig},
|
||||
gitforge::{self, RepoCloneError},
|
||||
types::GitRef,
|
||||
types::{GitRef, MessageToken},
|
||||
};
|
||||
|
||||
struct MockForge;
|
||||
|
@ -35,6 +35,7 @@ impl super::ForgeLike for MockForgeEnv {
|
|||
&self,
|
||||
_repo_config: RepoConfig,
|
||||
_addr: actix::prelude::Addr<RepoActor>,
|
||||
_message_token: MessageToken,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ pub use errors::*;
|
|||
|
||||
use crate::server::{
|
||||
config::{BranchName, GitDir, RepoConfig, RepoDetails},
|
||||
types::GitRef,
|
||||
types::{GitRef, MessageToken},
|
||||
};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -41,6 +41,7 @@ pub trait ForgeLike {
|
|||
&self,
|
||||
repo_config: RepoConfig,
|
||||
addr: actix::prelude::Addr<super::actors::repo::RepoActor>,
|
||||
message_token: MessageToken,
|
||||
);
|
||||
|
||||
/// Moves a branch to a new commit.
|
||||
|
|
|
@ -19,3 +19,19 @@ impl Display for GitRef {
|
|||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MessageToken(u32);
|
||||
impl MessageToken {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub const fn next(&self) -> Self {
|
||||
Self(self.0 + 1)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for MessageToken {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue