Add Explicit Progress Hint (#4433)
* Add new component `ExecutionDuration` Reusable component helps with tracking execution time of job It is not using job.started_at, but instead uses it's own tracking mechanism The key difference that it can give insights during execution of job and measures pure execution time Accuracy is configurable with `updateResolution` Can also detect if job is `longRunning` e.g. runs more than X-seconds * Implement Hint for Explicit Progress It uses ExecutionDuration component introduced in previous commit and device's local storage to handle `Dont show again` * Remove dublication of `FlowProgressBar` in `run` page * Change Hint styling * Change values in `ExecutionDuration` to match required `longDefinition`: 3 -> 30 `updateResolution`: 2 -> 10 Meaning jobs running more than 30s counts as a `Long Running Job` * Fix broken link * Scope to langs: `python3`, `bun` and `deno` jobKinds: `script` * Simplify ExecutionDuration for new scope * Bring `preview` job kind into the scope We need this to show this tip in preview pages. e.g. By clicking on subjob details of flow (this subjob is preview)
This commit is contained in:
52
frontend/src/lib/components/ExecutionDuration.svelte
Normal file
52
frontend/src/lib/components/ExecutionDuration.svelte
Normal file
@@ -0,0 +1,52 @@
|
||||
<script lang="ts">
|
||||
import { type Job } from '$lib/gen'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
export let job: Job | undefined = undefined
|
||||
/** Execution duration of current active job (in ms) */
|
||||
export let executionDuration: number = 0
|
||||
/** Is current job running more than specified value in `longDefinition` seconds */
|
||||
export let longRunning: boolean = false
|
||||
/** What do we count as "long" (in ms)*/
|
||||
export let longDefinition: number = 30_000
|
||||
/** How often component updates execution duration (in ms)
|
||||
* Higher value -> more efficient component is, less accuracy it has
|
||||
* Lower value -> less efficient component is, more accuracy it has
|
||||
*/
|
||||
export let updateResolution: number = 5_000
|
||||
|
||||
let startedAt: number | undefined = undefined
|
||||
let busy: boolean = false
|
||||
let interval: NodeJS.Timeout | undefined
|
||||
// Detect when execution of job started
|
||||
$: if (
|
||||
!busy &&
|
||||
job &&
|
||||
'running' in job &&
|
||||
(job.job_kind == 'script' || job?.job_kind == 'preview')
|
||||
)
|
||||
start(job)
|
||||
|
||||
function start(job: Job) {
|
||||
busy = true
|
||||
startedAt = new Date(job?.started_at ?? '').getTime()
|
||||
interval = setInterval(updateDuration, updateResolution)
|
||||
}
|
||||
|
||||
function updateDuration() {
|
||||
if (job?.type == 'CompletedJob') {
|
||||
clearInterval(interval)
|
||||
return
|
||||
}
|
||||
|
||||
if (startedAt) executionDuration = Date.now() - startedAt
|
||||
|
||||
// Detect long running
|
||||
if (executionDuration >= longDefinition) longRunning = true
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
// Clear the interval when the component is destroyed
|
||||
clearInterval(interval)
|
||||
})
|
||||
</script>
|
||||
@@ -3,6 +3,7 @@
|
||||
import ProgressBar from '../progressBar/ProgressBar.svelte'
|
||||
|
||||
export let job: Job | undefined = undefined
|
||||
export let currentSubJobProgress: number | undefined = undefined
|
||||
|
||||
let error: number | undefined = undefined
|
||||
let index = 0
|
||||
@@ -59,6 +60,9 @@
|
||||
// Jitter protection >^^^^^^^^
|
||||
subStepLength = 100
|
||||
subIndexIsPercent = true;
|
||||
currentSubJobProgress = subStepIndex
|
||||
} else {
|
||||
currentSubJobProgress = undefined
|
||||
}
|
||||
|
||||
error = newError
|
||||
|
||||
@@ -92,11 +92,13 @@
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import HighlightTheme from '$lib/components/HighlightTheme.svelte'
|
||||
import PreprocessedArgsDisplay from '$lib/components/runs/PreprocessedArgsDisplay.svelte'
|
||||
import ExecutionDuration from '$lib/components/ExecutionDuration.svelte'
|
||||
|
||||
let job: Job | undefined
|
||||
let jobUpdateLastFetch: Date | undefined
|
||||
|
||||
let scriptProgress: number | undefined = undefined;
|
||||
let currentJobIsLongRunning: boolean = false
|
||||
|
||||
let viewTab: 'result' | 'logs' | 'code' | 'stats' = 'result'
|
||||
let selectedJobStep: string | undefined = undefined
|
||||
@@ -112,6 +114,8 @@
|
||||
let persistentScriptDrawer: PersistentScriptDrawer
|
||||
let getLogs: (() => Promise<void>) | undefined = undefined
|
||||
|
||||
let showExplicitProgressTip: boolean =
|
||||
(localStorage.getItem('hideExplicitProgressTip') ?? 'false') == 'false'
|
||||
$: job?.logs == undefined && job && viewTab == 'logs' && getLogs?.()
|
||||
|
||||
let lastJobId: string | undefined = undefined
|
||||
@@ -747,7 +751,35 @@
|
||||
</div>
|
||||
<div>
|
||||
<Skeleton loading={!job} layout={[[9.5]]} />
|
||||
{#if job}<FlowMetadata {job} {scheduleEditor} />{/if}
|
||||
{#if job}
|
||||
<FlowMetadata {job} {scheduleEditor} />
|
||||
{#if currentJobIsLongRunning && showExplicitProgressTip && !scriptProgress && 'running' in job}
|
||||
<Alert
|
||||
class="mt-4 p-1 flex flex-row relative text-center"
|
||||
size="xs"
|
||||
type="info"
|
||||
title="tip: Track progress of longer jobs"
|
||||
tooltip="For better transparency and verbosity, you can try setting progress from within the script."
|
||||
documentationLink="https://www.windmill.dev/docs/advanced/explicit_progress"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => {
|
||||
localStorage.setItem('hideExplicitProgressTip', 'true')
|
||||
showExplicitProgressTip = false
|
||||
}}
|
||||
class="absolute m-2 top-0 right-0 inline-flex rounded-md bg-surface-secondary text-gray-400 hover:text-tertiary focus:outline-none"
|
||||
>
|
||||
<span class="sr-only">Close</span>
|
||||
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path
|
||||
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Alert>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -757,6 +789,9 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if job?.job_kind !== 'flow' && job?.job_kind !== 'flowpreview' && job?.job_kind !== 'singlescriptflow'}
|
||||
{#if ['python3', 'bun', 'deno'].includes(job?.language ?? '') && (job?.job_kind == 'script' || job?.job_kind == 'preview')}
|
||||
<ExecutionDuration bind:job bind:longRunning={currentJobIsLongRunning} />
|
||||
{/if}
|
||||
<div class="max-w-7xl mx-auto w-full px-4 mb-10">
|
||||
{#if job?.flow_status && typeof job.flow_status == 'object' && !('_metadata' in job.flow_status)}
|
||||
<div class="mt-10" />
|
||||
@@ -823,7 +858,11 @@
|
||||
</div>
|
||||
{:else if !job?.['deleted']}
|
||||
<div class="mt-10" />
|
||||
<FlowProgressBar {job} class="py-4 max-w-7xl mx-auto px-4" />
|
||||
<FlowProgressBar
|
||||
{job}
|
||||
bind:currentSubJobProgress={scriptProgress}
|
||||
class="py-4 max-w-7xl mx-auto px-4"
|
||||
/>
|
||||
<div class="w-full mt-10">
|
||||
<FlowStatusViewer
|
||||
jobId={job.id}
|
||||
|
||||
Reference in New Issue
Block a user