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:
pyranota
2024-09-24 17:13:06 +00:00
committed by GitHub
parent 17a8700956
commit 74c2cabd9f
3 changed files with 97 additions and 2 deletions

View 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>

View File

@@ -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

View File

@@ -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}