Skip to content
Snippets Groups Projects
Verified Commit a7f4538f authored by Daniel Göbel's avatar Daniel Göbel
Browse files

Use exponential backoff when polling running executions

#85
parent e078e13b
No related branches found
No related tags found
2 merge requests!84Remove development branch,!81Resolve "Use exponential backoff when polling running workflow executions"
abstract class BackoffStrategy {
protected currentVal: number;
protected iteration: number;
protected reachedMax: boolean;
protected maxValue: number;
constructor(maxValue?: number) {
this.currentVal = 0;
this.iteration = 0;
this.reachedMax = false;
this.maxValue = maxValue ?? 300;
}
protected abstract computeNextValue(): number;
public reset() {
this.iteration = 0;
this.currentVal = 0;
this.reachedMax = false;
}
public *generator(): Generator<number> {
while (true) {
this.iteration++;
if (this.reachedMax) {
yield this.maxValue;
} else {
this.currentVal = this.computeNextValue();
if (0 < this.maxValue && this.maxValue < this.currentVal) {
this.reachedMax = true;
yield this.maxValue;
} else {
yield this.currentVal;
}
}
}
}
}
export class ExponentialBackoff extends BackoffStrategy {
protected computeNextValue(): number {
return 2 << (this.iteration - 1);
}
}
export class NoBackoff extends BackoffStrategy {
private readonly constantValue: number;
constructor(constantValue?: number) {
super();
this.constantValue = constantValue ?? 30;
}
protected computeNextValue(): number {
return this.constantValue;
}
}
export class LinearBackoff extends BackoffStrategy {
private readonly backoff: number;
constructor(backoff?: number) {
super();
this.backoff = backoff ?? 5;
}
protected computeNextValue(): number {
return this.currentVal + this.backoff;
}
}
...@@ -8,12 +8,14 @@ import DeleteModal from "@/components/modals/DeleteModal.vue"; ...@@ -8,12 +8,14 @@ import DeleteModal from "@/components/modals/DeleteModal.vue";
import { useWorkflowStore } from "@/stores/workflows"; import { useWorkflowStore } from "@/stores/workflows";
import { useWorkflowExecutionStore } from "@/stores/workflowExecutions"; import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
import ParameterModal from "@/components/workflows/modals/ParameterModal.vue"; import ParameterModal from "@/components/workflows/modals/ParameterModal.vue";
import { ExponentialBackoff } from "@/utils/BackoffStrategy";
const workflowRepository = useWorkflowStore(); const workflowRepository = useWorkflowStore();
const executionRepository = useWorkflowExecutionStore(); const executionRepository = useWorkflowExecutionStore();
const backoff = new ExponentialBackoff();
let refreshTimeout: NodeJS.Timeout | undefined = undefined; let refreshTimeout: NodeJS.Timeout | undefined = undefined;
let intervalId: NodeJS.Timer | undefined = undefined; let pollingTimeout: NodeJS.Timeout | undefined = undefined;
const executionsState = reactive<{ const executionsState = reactive<{
loading: boolean; loading: boolean;
...@@ -80,9 +82,17 @@ const deleteModalString = computed<string>(() => { ...@@ -80,9 +82,17 @@ const deleteModalString = computed<string>(() => {
// Functions // Functions
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function updateExecutions() { function updateExecutions() {
executionRepository.fetchExecutions(() => { backoff.reset();
executionsState.loading = false; clearTimeout(pollingTimeout);
}); executionRepository
.fetchExecutions(() => {
executionsState.loading = false;
})
.then(() => {
if (runningExecutions.value.length > 0) {
refreshRunningWorkflowExecutionTimer();
}
});
} }
function refreshExecutions() { function refreshExecutions() {
...@@ -106,25 +116,37 @@ function cancelWorkflowExecution(executionId: string) { ...@@ -106,25 +116,37 @@ function cancelWorkflowExecution(executionId: string) {
executionRepository.cancelExecution(executionId); executionRepository.cancelExecution(executionId);
} }
const runningExecutions = computed<WorkflowExecutionOut[]>(() =>
executionRepository.executions.filter((execution) =>
workflowExecutionCancelable(execution),
),
);
function refreshRunningWorkflowExecution() { function refreshRunningWorkflowExecution() {
Promise.all( Promise.all(
executionRepository.executions runningExecutions.value.map((execution) =>
.filter((execution) => workflowExecutionCancelable(execution)) executionRepository.fetchExecution(execution.execution_id),
.map((execution) => ),
executionRepository.fetchExecution(execution.execution_id),
),
); );
} }
async function refreshRunningWorkflowExecutionTimer() {
for (const sleep of backoff.generator()) {
await new Promise((resolve) => {
pollingTimeout = setTimeout(resolve, sleep * 1000);
});
refreshRunningWorkflowExecution();
}
}
onMounted(() => { onMounted(() => {
updateExecutions(); updateExecutions();
workflowRepository.fetchWorkflows(); workflowRepository.fetchWorkflows();
intervalId = setInterval(refreshRunningWorkflowExecution, 5000);
new Tooltip("#refreshExecutionsButton"); new Tooltip("#refreshExecutionsButton");
}); });
onUnmounted(() => { onUnmounted(() => {
clearInterval(intervalId); clearTimeout(pollingTimeout);
}); });
</script> </script>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment