fix: stop zombie actors
Some checks failed
Test / build (map[name:nightly]) (push) Successful in 4m25s
Test / build (map[name:stable]) (push) Successful in 4m40s
Release Please / Release-plz (push) Failing after 40s

Some actors had been set up so that they only stopped when all their children stopped.
The detection for this was triggered by each child as it stopped.
However, some actors didn't have any children, so were never triggered to stop.
They now check after starting any children if they should simply stop there and then.
This commit is contained in:
Paul Campbell 2024-12-22 17:35:16 +00:00
parent f72e9e1221
commit b3f1ed596c
7 changed files with 78 additions and 31 deletions

View file

@ -6,10 +6,7 @@ use kameo::{mailbox::unbounded::UnboundedMailbox, Actor};
use crate::{
nextcloud::model::{NextcloudBoardId, NextcloudCardId, NextcloudStackId},
on_actor_start, p,
trello::model::{
attachment::{TrelloAttachment, TrelloAttachmentId},
card::TrelloCardId,
},
trello::model::{attachment::TrelloAttachment, card::TrelloCardId},
FullCtx,
};
@ -55,8 +52,8 @@ impl Actor for ImportAttachmentActor {
.trello_client()
.save_attachment(
&this.trello_card_id,
&TrelloAttachmentId::new(&this.trello_attachment.id),
Some(&PathBuf::from(&this.trello_attachment.id)),
&this.trello_attachment.id,
Some(&PathBuf::from(&(*this.trello_attachment.id))),
)
.await?;
let attachment_file = this.ctx.fs.file(&attachment_path);

View file

@ -15,7 +15,10 @@ use crate::{
model::{NextcloudBoardId, NextcloudCardDescription, NextcloudCardTitle, NextcloudStackId},
},
on_actor_link_died, on_actor_start, p, spawn_in_thread,
trello::{client::TrelloClient, model::card::TrelloShortCard},
trello::{
client::TrelloClient,
model::{attachment::TrelloAttachmentName, card::TrelloShortCard, label::TrelloLabelName},
},
FullCtx,
};
@ -26,8 +29,8 @@ pub(crate) struct ImportCardActor {
nextcloud_stack_id: NextcloudStackId,
labels_actor_ref: ActorRef<LabelsActor>,
labels_children: HashMap<ActorID, ActorRef<ImportLabelActor>>,
attachments_children: HashMap<ActorID, ActorRef<ImportAttachmentActor>>,
labels_children: HashMap<ActorID, (TrelloLabelName, ActorRef<ImportLabelActor>)>,
attachments_children: HashMap<ActorID, (TrelloAttachmentName, ActorRef<ImportAttachmentActor>)>,
}
impl ImportCardActor {
pub(crate) fn new(
@ -78,6 +81,7 @@ impl Actor for ImportCardActor {
let mut labels = vec![];
std::mem::swap(&mut this.trello_card.labels, &mut labels);
for trello_label in labels.into_iter() {
let trello_label_name = trello_label.name.clone();
let child = spawn_in_thread!(
actor_ref,
ImportLabelActor::new(
@ -89,7 +93,8 @@ impl Actor for ImportCardActor {
this.labels_actor_ref.clone(),
)
);
this.labels_children.insert(child.id(), child.clone());
this.labels_children
.insert(child.id(), (trello_label_name, child.clone()));
}
let attachments = trello_client
@ -98,6 +103,7 @@ impl Actor for ImportCardActor {
.result?
.attachments;
for trello_attachment in attachments.into_iter() {
let trello_attachment_name = trello_attachment.name.clone();
let child = spawn_in_thread!(
actor_ref,
ImportAttachmentActor::new(
@ -109,23 +115,35 @@ impl Actor for ImportCardActor {
trello_attachment,
)
);
this.attachments_children.insert(child.id(), child.clone());
this.attachments_children
.insert(child.id(), (trello_attachment_name, child.clone()));
}
Ok(())
if this.labels_children.is_empty() && this.attachments_children.is_empty() {
Ok(actor_ref.stop_gracefully().await?)
} else {
Ok(())
}
});
on_actor_link_died!(this, actor_ref, id, reason, {
match reason {
ActorStopReason::Normal => {
this.labels_children.remove(&id);
this.attachments_children.remove(&id);
let label = this.labels_children.remove(&id);
let attachment = this.attachments_children.remove(&id);
tracing::debug!(
?id,
?label,
?attachment,
"card (label|attachment) child stopped"
);
}
_ => {
return Ok(Some(reason));
}
}
tracing::debug!(?this.labels_children, ?this.attachments_children, "card children");
if this.labels_children.is_empty() && this.attachments_children.is_empty() {
return Ok(Some(ActorStopReason::Normal));
}

View file

@ -14,7 +14,10 @@ use crate::{
on_actor_link_died, on_actor_start, spawn_in_thread,
trello::{
client::TrelloClient,
model::list::{TrelloList, TrelloListId},
model::{
card::TrelloCardName,
list::{TrelloList, TrelloListId},
},
},
FullCtx,
};
@ -25,7 +28,7 @@ pub(super) struct ImportStackActor {
nextcloud_board_id: NextcloudBoardId,
nextcloud_stack_id: NextcloudStackId,
labels_actor_ref: ActorRef<LabelsActor>,
children: HashMap<ActorID, ActorRef<ImportCardActor>>,
children: HashMap<ActorID, (TrelloCardName, ActorRef<ImportCardActor>)>,
}
impl ImportStackActor {
pub(crate) fn new(
@ -60,6 +63,7 @@ impl Actor for ImportStackActor {
// - for each card in the trello stack
for trello_card in trello_cards.into_iter() {
let trello_card_name = trello_card.name.clone();
let child: ActorRef<ImportCardActor> = spawn_in_thread!(
actor_ref.clone(),
ImportCardActor::new(
@ -70,20 +74,27 @@ impl Actor for ImportStackActor {
this.labels_actor_ref.clone(),
)
);
this.children.insert(child.id(), child.clone());
this.children
.insert(child.id(), (trello_card_name, child.clone()));
}
if this.children.is_empty() {
Ok(actor_ref.stop_gracefully().await?)
} else {
Ok(())
}
Ok(())
});
on_actor_link_died!(this, actor_ref, id, reason, {
match reason {
ActorStopReason::Normal => {
this.children.remove(&id);
let stopped = this.children.remove(&id);
tracing::debug!(?id, ?stopped, "stack card child stopped");
}
_ => {
return Ok(Some(reason));
}
}
tracing::debug!(children = ?this.children, "stack children");
if this.children.is_empty() {
return Ok(Some(ActorStopReason::Normal));
}

View file

@ -15,7 +15,7 @@ use crate::{
model::{NextcloudBoardId, Stack},
},
on_actor_link_died, on_actor_start, p, spawn_in_thread,
trello::model::list::TrelloList,
trello::model::list::{TrelloList, TrelloListName},
FullCtx,
};
@ -58,7 +58,7 @@ pub(super) struct ImportStacksActor {
nextcloud_board_id: NextcloudBoardId,
labels_actor_ref: ActorRef<LabelsActor>,
children: HashMap<ActorID, ActorRef<ImportStackActor>>,
children: HashMap<ActorID, (TrelloListName, ActorRef<ImportStackActor>)>,
}
impl ImportStacksActor {
pub(super) fn new(
@ -128,20 +128,27 @@ impl Actor for ImportStacksActor {
this.labels_actor_ref.clone(),
)
);
this.children.insert(child.id(), child);
this.children
.insert(child.id(), (selected_trello_stack.name.clone(), child));
}
if this.children.is_empty() {
Ok(actor_ref.stop_gracefully().await?)
} else {
Ok(())
}
Ok(())
});
on_actor_link_died!(this, actor_ref, id, reason, {
match reason {
ActorStopReason::Normal => {
this.children.remove(&id);
let stopped = this.children.remove(&id);
tracing::debug!(?id, ?stopped, "stacks stack child stopped");
}
_ => {
return Ok(Some(reason));
}
}
tracing::debug!(?this.children, "stacks children");
if this.children.is_empty() {
return Ok(Some(ActorStopReason::Normal));
}

View file

@ -108,10 +108,11 @@ pub async fn run(ctx: &Ctx, commands: &Commands) -> color_eyre::Result<()> {
tracing::subscriber::set_global_default(
tracing_subscriber::FmtSubscriber::builder()
.with_max_level(tracing::Level::TRACE)
.with_env_filter("trace,hyper_util=info,kxio=info")
.finish(),
)?;
tracing::info!("ready");
}
tracing::info!("ready");
let cfg = AppConfig::load(ctx);
match cfg {

View file

@ -77,7 +77,7 @@ impl<'ctx> TrelloClient<'ctx> {
let url = attachment.url;
let file_name = file_name
.cloned()
.unwrap_or_else(|| PathBuf::from(attachment.file_name));
.unwrap_or_else(|| PathBuf::from((*attachment.file_name).clone()));
let file_name = self.ctx.fs.base().join(file_name);
let resp = with_exponential_backoff!(
&self.ctx,

View file

@ -5,12 +5,25 @@ use serde::Deserialize;
use crate::newtype;
newtype!(TrelloAttachmentId, String, Display, "Card Attachment ID");
newtype!(
TrelloAttachmentName,
String,
Display,
"Card Attachment name"
);
newtype!(TrelloAttachmentUrl, String, Display, "Card Attachment url");
newtype!(
TrelloAttachmentFilename,
String,
Display,
"Card Attachment filename"
);
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
pub(crate) struct TrelloAttachment {
pub(crate) id: String, // "5abbe4b7ddc1b351ef961414",
pub(crate) name: String, //"Deprecation Extension Notice",
pub(crate) url: String, //"https://admin.typeform.com/form/RzExEM/share#/link",
pub(crate) id: TrelloAttachmentId, // "5abbe4b7ddc1b351ef961414",
pub(crate) name: TrelloAttachmentName, //"Deprecation Extension Notice",
pub(crate) url: TrelloAttachmentUrl, //"https://admin.typeform.com/form/RzExEM/share#/link",
#[serde(rename = "fileName")]
pub(crate) file_name: String,
pub(crate) file_name: TrelloAttachmentFilename,
}