improve action responsiveness by improving when update is sent out rel to action state set

This commit is contained in:
mbecker20
2024-08-11 14:59:34 -07:00
parent 85a16f6c6f
commit b0f80cafc3
11 changed files with 188 additions and 81 deletions
+13 -13
View File
@@ -70,6 +70,19 @@ impl Resolve<RunBuild, (User, Update)> for State {
return Err(anyhow!("Must attach builder to RunBuild"));
}
// get the action state for the build (or insert default).
let action_state =
action_states().build.get_or_insert_default(&build.id).await;
// This will set action state back to default when dropped.
// Will also check to ensure build not already busy before updating.
let _action_guard =
action_state.update(|state| state.building = true)?;
build.config.version.increment();
update.version = build.config.version;
update_update(update.clone()).await?;
let git_token = git_token(
&build.config.git_provider,
&build.config.git_account,
@@ -80,22 +93,9 @@ impl Resolve<RunBuild, (User, Update)> for State {
|| format!("Failed to get git token in call to db. This is a database error, not a token exisitence error. Stopping run. | {} | {}", build.config.git_provider, build.config.git_account),
)?;
// get the action state for the build (or insert default).
let action_state =
action_states().build.get_or_insert_default(&build.id).await;
// This will set action state back to default when dropped.
// Will also check to ensure build not already busy before updating.
let _action_guard =
action_state.update(|state| state.building = true)?;
let (registry_token, aws_ecr) =
validate_account_extract_registry_token_aws_ecr(&build).await?;
build.config.version.increment();
update.version = build.config.version;
update_update(update.clone()).await?;
let cancel = CancellationToken::new();
let cancel_clone = cancel.clone();
let mut cancel_recv =
+106 -41
View File
@@ -6,8 +6,7 @@ use monitor_client::{
build::{Build, ImageRegistry},
config::core::AwsEcrConfig,
deployment::{
extract_registry_domain, Deployment, DeploymentActionState,
DeploymentImage,
extract_registry_domain, Deployment, DeploymentImage,
},
get_image_name,
permission::PermissionLevel,
@@ -36,7 +35,6 @@ use crate::{
async fn setup_deployment_execution(
deployment: &str,
user: &User,
set_in_progress: impl Fn(&mut DeploymentActionState),
) -> anyhow::Result<(Deployment, Server)> {
let deployment = resource::get_check_permissions::<Deployment>(
deployment,
@@ -49,16 +47,6 @@ async fn setup_deployment_execution(
return Err(anyhow!("deployment has no server configured"));
}
// get the action state for the deployment (or insert default).
let action_state = action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await;
// Will check to ensure deployment not already busy before updating, and return Err if so.
// The returned guard will set the action state back to default when dropped.
let _action_guard = action_state.update(set_in_progress)?;
let (server, status) =
get_server_with_status(&deployment.config.server_id).await?;
if status != ServerState::Ok {
@@ -82,10 +70,21 @@ impl Resolve<Deploy, (User, Update)> for State {
(user, mut update): (User, Update),
) -> anyhow::Result<Update> {
let (mut deployment, server) =
setup_deployment_execution(&deployment, &user, |state| {
state.deploying = true
})
.await?;
setup_deployment_execution(&deployment, &user).await?;
// get the action state for the deployment (or insert default).
let action_state = action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await;
// Will check to ensure deployment not already busy before updating, and return Err if so.
// The returned guard will set the action state back to default when dropped.
let _action_guard =
action_state.update(|state| state.deploying = true)?;
// Send update after setting action state, this way frontend gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
@@ -234,10 +233,21 @@ impl Resolve<StartContainer, (User, Update)> for State {
(user, mut update): (User, Update),
) -> anyhow::Result<Update> {
let (deployment, server) =
setup_deployment_execution(&deployment, &user, |state| {
state.starting = true
})
.await?;
setup_deployment_execution(&deployment, &user).await?;
// get the action state for the deployment (or insert default).
let action_state = action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await;
// Will check to ensure deployment not already busy before updating, and return Err if so.
// The returned guard will set the action state back to default when dropped.
let _action_guard =
action_state.update(|state| state.starting = true)?;
// Send update after setting action state, this way frontend gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
@@ -271,10 +281,21 @@ impl Resolve<RestartContainer, (User, Update)> for State {
(user, mut update): (User, Update),
) -> anyhow::Result<Update> {
let (deployment, server) =
setup_deployment_execution(&deployment, &user, |state| {
state.restarting = true
})
.await?;
setup_deployment_execution(&deployment, &user).await?;
// get the action state for the deployment (or insert default).
let action_state = action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await;
// Will check to ensure deployment not already busy before updating, and return Err if so.
// The returned guard will set the action state back to default when dropped.
let _action_guard =
action_state.update(|state| state.restarting = true)?;
// Send update after setting action state, this way frontend gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
@@ -310,10 +331,21 @@ impl Resolve<PauseContainer, (User, Update)> for State {
(user, mut update): (User, Update),
) -> anyhow::Result<Update> {
let (deployment, server) =
setup_deployment_execution(&deployment, &user, |state| {
state.pausing = true
})
.await?;
setup_deployment_execution(&deployment, &user).await?;
// get the action state for the deployment (or insert default).
let action_state = action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await;
// Will check to ensure deployment not already busy before updating, and return Err if so.
// The returned guard will set the action state back to default when dropped.
let _action_guard =
action_state.update(|state| state.pausing = true)?;
// Send update after setting action state, this way frontend gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
@@ -347,10 +379,21 @@ impl Resolve<UnpauseContainer, (User, Update)> for State {
(user, mut update): (User, Update),
) -> anyhow::Result<Update> {
let (deployment, server) =
setup_deployment_execution(&deployment, &user, |state| {
state.unpausing = true
})
.await?;
setup_deployment_execution(&deployment, &user).await?;
// get the action state for the deployment (or insert default).
let action_state = action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await;
// Will check to ensure deployment not already busy before updating, and return Err if so.
// The returned guard will set the action state back to default when dropped.
let _action_guard =
action_state.update(|state| state.unpausing = true)?;
// Send update after setting action state, this way frontend gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
@@ -390,10 +433,21 @@ impl Resolve<StopContainer, (User, Update)> for State {
(user, mut update): (User, Update),
) -> anyhow::Result<Update> {
let (deployment, server) =
setup_deployment_execution(&deployment, &user, |state| {
state.stopping = true
})
.await?;
setup_deployment_execution(&deployment, &user).await?;
// get the action state for the deployment (or insert default).
let action_state = action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await;
// Will check to ensure deployment not already busy before updating, and return Err if so.
// The returned guard will set the action state back to default when dropped.
let _action_guard =
action_state.update(|state| state.stopping = true)?;
// Send update after setting action state, this way frontend gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
@@ -437,10 +491,21 @@ impl Resolve<RemoveContainer, (User, Update)> for State {
(user, mut update): (User, Update),
) -> anyhow::Result<Update> {
let (deployment, server) =
setup_deployment_execution(&deployment, &user, |state| {
state.removing = true
})
.await?;
setup_deployment_execution(&deployment, &user).await?;
// get the action state for the deployment (or insert default).
let action_state = action_states()
.deployment
.get_or_insert_default(&deployment.id)
.await;
// Will check to ensure deployment not already busy before updating, and return Err if so.
// The returned guard will set the action state back to default when dropped.
let _action_guard =
action_state.update(|state| state.removing = true)?;
// Send update after setting action state, this way frontend gets correct state.
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
+2
View File
@@ -69,6 +69,8 @@ fn resolve_inner(
let _action_guard =
action_state.update(|state| state.running = true)?;
update_update(update.clone()).await?;
let update = Mutex::new(update);
let res = execute_procedure(&procedure, &update).await;
+11 -9
View File
@@ -54,6 +54,17 @@ impl Resolve<CloneRepo, (User, Update)> for State {
)
.await?;
// get the action state for the repo (or insert default).
let action_state =
action_states().repo.get_or_insert_default(&repo.id).await;
// This will set action state back to default when dropped.
// Will also check to ensure repo not already busy before updating.
let _action_guard =
action_state.update(|state| state.cloning = true)?;
update_update(update.clone()).await?;
let git_token = git_token(
&repo.config.git_provider,
&repo.config.git_account,
@@ -64,15 +75,6 @@ impl Resolve<CloneRepo, (User, Update)> for State {
|| format!("Failed to get git token in call to db. This is a database error, not a token exisitence error. Stopping run. | {} | {}", repo.config.git_provider, repo.config.git_account),
)?;
// get the action state for the repo (or insert default).
let action_state =
action_states().repo.get_or_insert_default(&repo.id).await;
// This will set action state back to default when dropped.
// Will also check to ensure repo not already busy before updating.
let _action_guard =
action_state.update(|state| state.cloning = true)?;
if repo.config.server_id.is_empty() {
return Err(anyhow!("repo has no server attached"));
}
+8
View File
@@ -47,6 +47,8 @@ impl Resolve<StopAllContainers, (User, Update)> for State {
let _action_guard = action_state
.update(|state| state.stopping_containers = true)?;
update_update(update.clone()).await?;
let logs = periphery_client(&server)?
.request(api::container::StopAllContainers {})
.await
@@ -90,6 +92,8 @@ impl Resolve<PruneContainers, (User, Update)> for State {
let _action_guard =
action_state.update(|state| state.pruning_containers = true)?;
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
let log = match periphery
@@ -144,6 +148,8 @@ impl Resolve<PruneNetworks, (User, Update)> for State {
let _action_guard =
action_state.update(|state| state.pruning_networks = true)?;
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
let log = match periphery
@@ -196,6 +202,8 @@ impl Resolve<PruneImages, (User, Update)> for State {
let _action_guard =
action_state.update(|state| state.pruning_images = true)?;
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
let log =
+2
View File
@@ -48,6 +48,8 @@ impl Resolve<DeployStack, (User, Update)> for State {
let _action_guard =
action_state.update(|state| state.deploying = true)?;
update_update(update.clone()).await?;
let git_token = crate::helpers::git_token(
&stack.config.git_provider,
&stack.config.git_account,
+3
View File
@@ -58,6 +58,9 @@ impl Resolve<RunSync, (User, Update)> for State {
return Err(anyhow!("resource sync repo not configured"));
}
// Send update here for FE to recheck action state
update_update(update.clone()).await?;
let (res, logs, hash, message) =
crate::helpers::sync::remote::get_remote_resources(&sync)
.await
+3
View File
@@ -48,6 +48,9 @@ pub async fn execute_compose<T: ExecuteCompose>(
// The returned guard will set the action state back to default when dropped.
let _action_guard = action_state.update(set_in_progress)?;
// Send update here for frontend to recheck action state
update_update(update.clone()).await?;
let periphery = periphery_client(&server)?;
if let Some(service) = &service {
+19 -1
View File
@@ -59,6 +59,23 @@ pub async fn add_update(
Ok(id)
}
#[instrument(level = "debug")]
pub async fn add_update_without_send(
update: &Update,
) -> anyhow::Result<String> {
let id = db_client()
.await
.updates
.insert_one(update)
.await
.context("failed to insert update into db")?
.inserted_id
.as_object_id()
.context("inserted_id is not object id")?
.to_string();
Ok(id)
}
#[instrument(level = "debug")]
pub async fn update_update(update: Update) -> anyhow::Result<()> {
update_one_by_id(&db_client().await.updates, &update.id, mungos::update::Update::Set(to_document(&update)?), None)
@@ -312,6 +329,7 @@ pub async fn init_execution_update(
};
let mut update = make_update(target, operation, user);
update.in_progress();
update.id = add_update(update.clone()).await?;
// Don't actually send it here, let the handlers send it after they can set action state.
update.id = add_update_without_send(&update).await?;
Ok(update)
}
@@ -1,6 +1,14 @@
import { ActionWithDialog, ConfirmButton } from "@components/util";
import { useExecute, useInvalidate, useRead, useWrite } from "@lib/hooks";
import { Pause, Pen, Play, RefreshCcw, Rocket, Square, Trash2 } from "lucide-react";
import {
Pause,
Pen,
Play,
RefreshCcw,
Rocket,
Square,
Trash2,
} from "lucide-react";
import { useStack } from ".";
import { Types } from "@monitor/client";
import { useToast } from "@ui/use-toast";
@@ -20,8 +28,6 @@ export const DeployStack = ({ id }: { id: string }) => {
if (!stack || state === Types.StackState.Unknown) {
return null;
}
const pending = isPending || deploying;
const deployed =
state !== undefined &&
[
@@ -39,8 +45,8 @@ export const DeployStack = ({ id }: { id: string }) => {
title="Redeploy"
icon={<Rocket className="h-4 w-4" />}
onClick={() => deploy({ stack: id })}
disabled={pending}
loading={pending}
disabled={isPending}
loading={isPending || deploying}
/>
);
}
@@ -50,8 +56,8 @@ export const DeployStack = ({ id }: { id: string }) => {
title="Deploy"
icon={<Rocket className="w-4 h-4" />}
onClick={() => deploy({ stack: id })}
disabled={pending}
loading={pending}
disabled={isPending}
loading={isPending || deploying}
/>
);
};
@@ -74,8 +80,6 @@ export const DestroyStack = ({ id }: { id: string }) => {
return null;
}
const pending = isPending || destroying;
if (!stack) {
return null;
}
@@ -86,8 +90,8 @@ export const DestroyStack = ({ id }: { id: string }) => {
title="Destroy"
icon={<Trash2 className="h-4 w-4" />}
onClick={() => destroy({ stack: id })}
disabled={pending}
loading={pending}
disabled={isPending}
loading={isPending || destroying}
/>
);
};
@@ -22,8 +22,8 @@ export const StackInfo = ({
<Card>
<CardHeader>
deployed contents:{" "}
{stack?.info?.deployed_contents?.map((content) => (
<pre className="flex flex-col gap-2">
{stack?.info?.deployed_contents?.map((content, i) => (
<pre key={i} className="flex flex-col gap-2">
path: {content.path}
<pre>{content.contents}</pre>
</pre>
@@ -48,8 +48,8 @@ export const StackInfo = ({
<Card>
<CardHeader>
latest contents:{" "}
{stack?.info?.remote_contents?.map((content) => (
<pre className="flex flex-col gap-2">
{stack?.info?.remote_contents?.map((content, i) => (
<pre key={i} className="flex flex-col gap-2">
path: {content.path}
<pre>{content.contents}</pre>
</pre>
@@ -62,8 +62,8 @@ export const StackInfo = ({
<Card>
<CardHeader>
remote errors:{" "}
{stack?.info?.remote_errors?.map((content) => (
<pre className="flex flex-col gap-2">
{stack?.info?.remote_errors?.map((content, i) => (
<pre key={i} className="flex flex-col gap-2">
path: {content.path}
<pre>{content.contents}</pre>
</pre>