fix: stop zombie actors
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:
parent
f72e9e1221
commit
b3f1ed596c
7 changed files with 78 additions and 31 deletions
|
@ -6,10 +6,7 @@ use kameo::{mailbox::unbounded::UnboundedMailbox, Actor};
|
||||||
use crate::{
|
use crate::{
|
||||||
nextcloud::model::{NextcloudBoardId, NextcloudCardId, NextcloudStackId},
|
nextcloud::model::{NextcloudBoardId, NextcloudCardId, NextcloudStackId},
|
||||||
on_actor_start, p,
|
on_actor_start, p,
|
||||||
trello::model::{
|
trello::model::{attachment::TrelloAttachment, card::TrelloCardId},
|
||||||
attachment::{TrelloAttachment, TrelloAttachmentId},
|
|
||||||
card::TrelloCardId,
|
|
||||||
},
|
|
||||||
FullCtx,
|
FullCtx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,8 +52,8 @@ impl Actor for ImportAttachmentActor {
|
||||||
.trello_client()
|
.trello_client()
|
||||||
.save_attachment(
|
.save_attachment(
|
||||||
&this.trello_card_id,
|
&this.trello_card_id,
|
||||||
&TrelloAttachmentId::new(&this.trello_attachment.id),
|
&this.trello_attachment.id,
|
||||||
Some(&PathBuf::from(&this.trello_attachment.id)),
|
Some(&PathBuf::from(&(*this.trello_attachment.id))),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let attachment_file = this.ctx.fs.file(&attachment_path);
|
let attachment_file = this.ctx.fs.file(&attachment_path);
|
||||||
|
|
|
@ -15,7 +15,10 @@ use crate::{
|
||||||
model::{NextcloudBoardId, NextcloudCardDescription, NextcloudCardTitle, NextcloudStackId},
|
model::{NextcloudBoardId, NextcloudCardDescription, NextcloudCardTitle, NextcloudStackId},
|
||||||
},
|
},
|
||||||
on_actor_link_died, on_actor_start, p, spawn_in_thread,
|
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,
|
FullCtx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,8 +29,8 @@ pub(crate) struct ImportCardActor {
|
||||||
nextcloud_stack_id: NextcloudStackId,
|
nextcloud_stack_id: NextcloudStackId,
|
||||||
labels_actor_ref: ActorRef<LabelsActor>,
|
labels_actor_ref: ActorRef<LabelsActor>,
|
||||||
|
|
||||||
labels_children: HashMap<ActorID, ActorRef<ImportLabelActor>>,
|
labels_children: HashMap<ActorID, (TrelloLabelName, ActorRef<ImportLabelActor>)>,
|
||||||
attachments_children: HashMap<ActorID, ActorRef<ImportAttachmentActor>>,
|
attachments_children: HashMap<ActorID, (TrelloAttachmentName, ActorRef<ImportAttachmentActor>)>,
|
||||||
}
|
}
|
||||||
impl ImportCardActor {
|
impl ImportCardActor {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
|
@ -78,6 +81,7 @@ impl Actor for ImportCardActor {
|
||||||
let mut labels = vec![];
|
let mut labels = vec![];
|
||||||
std::mem::swap(&mut this.trello_card.labels, &mut labels);
|
std::mem::swap(&mut this.trello_card.labels, &mut labels);
|
||||||
for trello_label in labels.into_iter() {
|
for trello_label in labels.into_iter() {
|
||||||
|
let trello_label_name = trello_label.name.clone();
|
||||||
let child = spawn_in_thread!(
|
let child = spawn_in_thread!(
|
||||||
actor_ref,
|
actor_ref,
|
||||||
ImportLabelActor::new(
|
ImportLabelActor::new(
|
||||||
|
@ -89,7 +93,8 @@ impl Actor for ImportCardActor {
|
||||||
this.labels_actor_ref.clone(),
|
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
|
let attachments = trello_client
|
||||||
|
@ -98,6 +103,7 @@ impl Actor for ImportCardActor {
|
||||||
.result?
|
.result?
|
||||||
.attachments;
|
.attachments;
|
||||||
for trello_attachment in attachments.into_iter() {
|
for trello_attachment in attachments.into_iter() {
|
||||||
|
let trello_attachment_name = trello_attachment.name.clone();
|
||||||
let child = spawn_in_thread!(
|
let child = spawn_in_thread!(
|
||||||
actor_ref,
|
actor_ref,
|
||||||
ImportAttachmentActor::new(
|
ImportAttachmentActor::new(
|
||||||
|
@ -109,23 +115,35 @@ impl Actor for ImportCardActor {
|
||||||
trello_attachment,
|
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, {
|
on_actor_link_died!(this, actor_ref, id, reason, {
|
||||||
match reason {
|
match reason {
|
||||||
ActorStopReason::Normal => {
|
ActorStopReason::Normal => {
|
||||||
this.labels_children.remove(&id);
|
let label = this.labels_children.remove(&id);
|
||||||
this.attachments_children.remove(&id);
|
let attachment = this.attachments_children.remove(&id);
|
||||||
|
tracing::debug!(
|
||||||
|
?id,
|
||||||
|
?label,
|
||||||
|
?attachment,
|
||||||
|
"card (label|attachment) child stopped"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok(Some(reason));
|
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() {
|
if this.labels_children.is_empty() && this.attachments_children.is_empty() {
|
||||||
return Ok(Some(ActorStopReason::Normal));
|
return Ok(Some(ActorStopReason::Normal));
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,10 @@ use crate::{
|
||||||
on_actor_link_died, on_actor_start, spawn_in_thread,
|
on_actor_link_died, on_actor_start, spawn_in_thread,
|
||||||
trello::{
|
trello::{
|
||||||
client::TrelloClient,
|
client::TrelloClient,
|
||||||
model::list::{TrelloList, TrelloListId},
|
model::{
|
||||||
|
card::TrelloCardName,
|
||||||
|
list::{TrelloList, TrelloListId},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
FullCtx,
|
FullCtx,
|
||||||
};
|
};
|
||||||
|
@ -25,7 +28,7 @@ pub(super) struct ImportStackActor {
|
||||||
nextcloud_board_id: NextcloudBoardId,
|
nextcloud_board_id: NextcloudBoardId,
|
||||||
nextcloud_stack_id: NextcloudStackId,
|
nextcloud_stack_id: NextcloudStackId,
|
||||||
labels_actor_ref: ActorRef<LabelsActor>,
|
labels_actor_ref: ActorRef<LabelsActor>,
|
||||||
children: HashMap<ActorID, ActorRef<ImportCardActor>>,
|
children: HashMap<ActorID, (TrelloCardName, ActorRef<ImportCardActor>)>,
|
||||||
}
|
}
|
||||||
impl ImportStackActor {
|
impl ImportStackActor {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
|
@ -60,6 +63,7 @@ impl Actor for ImportStackActor {
|
||||||
|
|
||||||
// - for each card in the trello stack
|
// - for each card in the trello stack
|
||||||
for trello_card in trello_cards.into_iter() {
|
for trello_card in trello_cards.into_iter() {
|
||||||
|
let trello_card_name = trello_card.name.clone();
|
||||||
let child: ActorRef<ImportCardActor> = spawn_in_thread!(
|
let child: ActorRef<ImportCardActor> = spawn_in_thread!(
|
||||||
actor_ref.clone(),
|
actor_ref.clone(),
|
||||||
ImportCardActor::new(
|
ImportCardActor::new(
|
||||||
|
@ -70,20 +74,27 @@ impl Actor for ImportStackActor {
|
||||||
this.labels_actor_ref.clone(),
|
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, {
|
on_actor_link_died!(this, actor_ref, id, reason, {
|
||||||
match reason {
|
match reason {
|
||||||
ActorStopReason::Normal => {
|
ActorStopReason::Normal => {
|
||||||
this.children.remove(&id);
|
let stopped = this.children.remove(&id);
|
||||||
|
tracing::debug!(?id, ?stopped, "stack card child stopped");
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok(Some(reason));
|
return Ok(Some(reason));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tracing::debug!(children = ?this.children, "stack children");
|
||||||
if this.children.is_empty() {
|
if this.children.is_empty() {
|
||||||
return Ok(Some(ActorStopReason::Normal));
|
return Ok(Some(ActorStopReason::Normal));
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
model::{NextcloudBoardId, Stack},
|
model::{NextcloudBoardId, Stack},
|
||||||
},
|
},
|
||||||
on_actor_link_died, on_actor_start, p, spawn_in_thread,
|
on_actor_link_died, on_actor_start, p, spawn_in_thread,
|
||||||
trello::model::list::TrelloList,
|
trello::model::list::{TrelloList, TrelloListName},
|
||||||
FullCtx,
|
FullCtx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ pub(super) struct ImportStacksActor {
|
||||||
nextcloud_board_id: NextcloudBoardId,
|
nextcloud_board_id: NextcloudBoardId,
|
||||||
labels_actor_ref: ActorRef<LabelsActor>,
|
labels_actor_ref: ActorRef<LabelsActor>,
|
||||||
|
|
||||||
children: HashMap<ActorID, ActorRef<ImportStackActor>>,
|
children: HashMap<ActorID, (TrelloListName, ActorRef<ImportStackActor>)>,
|
||||||
}
|
}
|
||||||
impl ImportStacksActor {
|
impl ImportStacksActor {
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
|
@ -128,20 +128,27 @@ impl Actor for ImportStacksActor {
|
||||||
this.labels_actor_ref.clone(),
|
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, {
|
on_actor_link_died!(this, actor_ref, id, reason, {
|
||||||
match reason {
|
match reason {
|
||||||
ActorStopReason::Normal => {
|
ActorStopReason::Normal => {
|
||||||
this.children.remove(&id);
|
let stopped = this.children.remove(&id);
|
||||||
|
tracing::debug!(?id, ?stopped, "stacks stack child stopped");
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok(Some(reason));
|
return Ok(Some(reason));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tracing::debug!(?this.children, "stacks children");
|
||||||
if this.children.is_empty() {
|
if this.children.is_empty() {
|
||||||
return Ok(Some(ActorStopReason::Normal));
|
return Ok(Some(ActorStopReason::Normal));
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,10 +108,11 @@ pub async fn run(ctx: &Ctx, commands: &Commands) -> color_eyre::Result<()> {
|
||||||
tracing::subscriber::set_global_default(
|
tracing::subscriber::set_global_default(
|
||||||
tracing_subscriber::FmtSubscriber::builder()
|
tracing_subscriber::FmtSubscriber::builder()
|
||||||
.with_max_level(tracing::Level::TRACE)
|
.with_max_level(tracing::Level::TRACE)
|
||||||
|
.with_env_filter("trace,hyper_util=info,kxio=info")
|
||||||
.finish(),
|
.finish(),
|
||||||
)?;
|
)?;
|
||||||
tracing::info!("ready");
|
|
||||||
}
|
}
|
||||||
|
tracing::info!("ready");
|
||||||
|
|
||||||
let cfg = AppConfig::load(ctx);
|
let cfg = AppConfig::load(ctx);
|
||||||
match cfg {
|
match cfg {
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl<'ctx> TrelloClient<'ctx> {
|
||||||
let url = attachment.url;
|
let url = attachment.url;
|
||||||
let file_name = file_name
|
let file_name = file_name
|
||||||
.cloned()
|
.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 file_name = self.ctx.fs.base().join(file_name);
|
||||||
let resp = with_exponential_backoff!(
|
let resp = with_exponential_backoff!(
|
||||||
&self.ctx,
|
&self.ctx,
|
||||||
|
|
|
@ -5,12 +5,25 @@ use serde::Deserialize;
|
||||||
use crate::newtype;
|
use crate::newtype;
|
||||||
|
|
||||||
newtype!(TrelloAttachmentId, String, Display, "Card Attachment ID");
|
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) struct TrelloAttachment {
|
||||||
pub(crate) id: String, // "5abbe4b7ddc1b351ef961414",
|
pub(crate) id: TrelloAttachmentId, // "5abbe4b7ddc1b351ef961414",
|
||||||
pub(crate) name: String, //"Deprecation Extension Notice",
|
pub(crate) name: TrelloAttachmentName, //"Deprecation Extension Notice",
|
||||||
pub(crate) url: String, //"https://admin.typeform.com/form/RzExEM/share#/link",
|
pub(crate) url: TrelloAttachmentUrl, //"https://admin.typeform.com/form/RzExEM/share#/link",
|
||||||
#[serde(rename = "fileName")]
|
#[serde(rename = "fileName")]
|
||||||
pub(crate) file_name: String,
|
pub(crate) file_name: TrelloAttachmentFilename,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue