Skip to content

Commit

Permalink
Implement workaround for series deletion
Browse files Browse the repository at this point in the history
So this is super annoying. The endpoint in opencast will
start `republish metadata` workflows for each event in the
series. This takes roughly 10 second **per video**.
Not in parallel but.. in.. series (pun intended? idk).

So long story short, it could take several minutes to return,
at which point it probably just responds with a `503`.
The series is still being deleted though, so I don't know how
to handle that case.

Anyway, this workaround will send the request, mark the series
as deleted and then just return without waiting. If it fails,
it will **eventually** become apparent, but until then it's just
showing as `deletion pending`, or sth. Actually, depending on
the configured interval, it will already show `deletion failed`
after a couple of minutes or sooner, when the "deletion process"
is still running in Opencast. So all in all a poor solution but
idk what else to do here other than an Opencast rewrite™.
  • Loading branch information
owi92 committed Feb 28, 2025
1 parent a0b465d commit 6dfc3ce
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 38 deletions.
7 changes: 0 additions & 7 deletions backend/src/api/model/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,10 +764,3 @@ define_sort_column_and_order!(
};
pub struct VideosSortOrder
);


#[derive(juniper::GraphQLObject)]
#[graphql(Context = Context)]
pub(crate) struct RemovedEvent {
id: Id,
}
71 changes: 45 additions & 26 deletions backend/src/api/model/series.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use super::{
};


#[derive(Clone)]
pub(crate) struct Series {
pub(crate) key: Key,
pub(crate) opencast_id: String,
Expand All @@ -53,7 +54,7 @@ pub(crate) struct Series {
pub(crate) tobira_deletion_timestamp: Option<DateTime<Utc>>,
}

#[derive(GraphQLObject)]
#[derive(Clone, GraphQLObject)]
pub(crate) struct SyncedSeriesData {
description: Option<String>,
}
Expand Down Expand Up @@ -491,32 +492,49 @@ impl Series {
)
).await?;

let response = context
.oc_client
.delete(&series)
.await
.map_err(|e| {
error!("Failed to send delete request: {}", e);
err::opencast_unavailable!("Failed to communicate with Opencast")
})?;
info!(series_id = %id, "Attempting to send request to delete series in Opencast");

context.db.execute("\
update all_series \
set tobira_deletion_timestamp = current_timestamp \
where id = $1 \
", &[&series.key]).await?;
// Todo: Consider optimistically updating events as well.


let oc_client = context.oc_client.clone();
let series_clone = series.clone();

// Unfortunately we can't wait for the http response. The Opencast delete endpoint will
// automatically start `republish metadata` workflows for all events in the series, which
// takes roughly 10 seconds per event, and only returns once all have finished.
// This would block the request for a long time.
tokio::spawn(async move {
let response = match oc_client.delete(&series_clone).await {
Ok(response) => response,
Err(e) => {
error!("Failed to send delete request: {}", e);
return;
}
};

if response.status() == StatusCode::NO_CONTENT {
info!(series_id = %id, "Deletion successfully requested");
} else {
// This is kinda pointless. Depending on the number of videos, the request takes
// super long to respond and will potentially return with `504 Gateway Timeout`.
// This is not necessarily an error in this case as the deletion could still be
// in progress.
warn!(
series_id = %id,
"Failed to delete series, Opencast returned status: {}",
response.status()
);
}
// Todo: Consider reverting DB changes on error or unexpected response.
});

if response.status() == StatusCode::NO_CONTENT {
// 204: The series has been deleted
info!(series_id = %id, "Requested deletion of series");
context.db.execute("\
update all_series \
set tobira_deletion_timestamp = current_timestamp \
where id = $1 \
", &[&series.key]).await?;
Ok(series)
} else {
warn!(
series_id = %id,
"Failed to delete series, OC returned status: {}",
response.status()
);
Err(err::opencast_unavailable!("Opencast API error: {}", response.status()))
}
Ok(series)
}
}

Expand Down Expand Up @@ -665,3 +683,4 @@ define_sort_column_and_order!(
};
pub struct SeriesSortOrder
);

2 changes: 1 addition & 1 deletion backend/src/api/mutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl Mutation {
/// The series is marked as "deletion pending" in Tobira and fully removed once Opencast
/// finished deleting the series.
///
/// Returns the deletion timestamp in case of success and errors otherwise.
/// Returns the preliminary deletion timestamp.
async fn delete_series(id: Id, context: &Context) -> ApiResult<Series> {
Series::delete(id, context).await
}
Expand Down
2 changes: 1 addition & 1 deletion backend/src/model/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{auth::AuthContext, HasRoles};


/// Information necessary to render a thumbnail.
#[derive(Debug, GraphQLObject)]
#[derive(Debug, GraphQLObject, Clone)]
pub(crate) struct ThumbnailInfo {
pub(crate) url: Option<String>,
pub(crate) live: bool,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/model/extra_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::prelude::*;
/// `"dcterms"` for the dc terms (most common namespace). The value for each
/// namespace is a simple string-key map where each value is an array of string
/// values.
#[derive(Debug, Serialize, Deserialize, Default, GraphQLScalar)]
#[derive(Clone, Debug, Serialize, Deserialize, Default, GraphQLScalar)]
#[cfg_attr(test, derive(PartialEq, Eq))]
#[graphql(
description = "Arbitrary metadata for events/series. Serialized as JSON object.",
Expand Down
2 changes: 1 addition & 1 deletion backend/src/model/series/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use juniper::GraphQLObject;
use super::ThumbnailInfo;


#[derive(Debug, GraphQLObject)]
#[derive(Debug, GraphQLObject, Clone)]
pub(crate) struct SeriesThumbnailStack {
pub(crate) thumbnails: Vec<ThumbnailInfo>,
}
2 changes: 1 addition & 1 deletion frontend/src/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ type Mutation {
The series is marked as "deletion pending" in Tobira and fully removed once Opencast
finished deleting the series.
Returns the deletion timestamp in case of success and errors otherwise.
Returns the preliminary deletion timestamp.
"""
deleteSeries(id: ID!): Series!
"""
Expand Down

0 comments on commit 6dfc3ce

Please sign in to comment.