Repository: kestra-io/kestra
Issue: Broken execution duration/gantt chart when running under load
Labels: bug

### Expected Behavior

The Gantt chart displaying an execution of a sequential flow has (almost) no overlaps. The rendered execution duration matches the data.

### Actual Behaviour

In some cases execution's duration doesn't get updated correctly. This happens mainly when the Kestra cluster is under load.

The issue manifests itself in Gantt chart displaying an execution of _a sequential process_ contains strange overlaps.

Execution of the second task on the chart displayed below states its duration was zero seconds and the chart illustrates a 1+ hour runtime. This is clearly wrong as both the tool tip and the rest of the chart show real duration sub-timings.
![image](https://user-images.githubusercontent.com/13468636/228749566-1168724b-0391-4c98-9fa2-fac04fbf3440.png)

This issue also affects the Executions table. The list of executions sorted by their duration gets sorted correctly but displaying odd duration for those executions having broken duration/Gantt chart.
![image (1)](https://user-images.githubusercontent.com/13468636/228752069-cbf2d2aa-97d9-4bcb-b05d-c46d60854513.png)


### Steps To Reproduce

1. Create the attached `io.kestra.tests.ATF0P0test` flow
2. Load Kestra with a stream of new executions of the flow
3. Sort the Executions table by Duration
4. Observe & pick executions having an odd value of Duration

### Environment Information

- Kestra Version: 0.7.1
- Operating System (OS / Docker / Kubernetes): CentOS
- Java Version (If not docker): openjdk version "11.0.18" 2023-01-17 LTS
- Persistence/Queues: PostgreSQL 14.5

Timezone info:
> `$ timedatectl`
>       Local time: Wed 2023-03-29 10:53:16 CEST
>   Universal time: Wed 2023-03-29 08:53:16 UTC
>         RTC time: Wed 2023-03-29 08:53:15
>        Time zone: Europe/Prague (CEST, +0200)
>      NTP enabled: yes
> NTP synchronized: yes
>  RTC in local TZ: no
>       DST active: yes
>  Last DST change: DST began at
>                   Sun 2023-03-26 01:59:59 CET
>                   Sun 2023-03-26 03:00:00 CEST
>  Next DST change: DST ends (the clock jumps one hour backwards) at
>                   Sun 2023-10-29 02:59:59 CEST
>                   Sun 2023-10-29 02:00:00 CET

### Example flow

```yaml
id: ATF0P0test
namespace: io.kestra.tests
description: Model of test workflow.
disabled: false

labels:
  connector: test
  mode: transformation

inputs:
  - type: FILE
    description: Current input file - mandatory.
    name: inputMessage
  - type: STRING
    description: Current CLE ID - mandatory.
    name: CLEID
  - type: STRING
    description: Current TDID - mandatory.
    name: TDID
  - type: STRING
    description: Current reference ID - mandatory.
    name: REF_NR
  - type: STRING
    description: Current COV DEID - mandatory.
    name: COV_DEID
  - type: STRING
    description: Path to the XSLT sheet - mandatory.
    name: xsltPath
  - type: STRING
    description: Path to the input serv resource - optional.
    name: servInputResourcePath
    required: false
  - type: STRING
    description: The input serv service - dependent.
    name: servInputService
    required: false
  - type: STRING
    description: The encoding of the flat input serv - dependent.
    name: servInputEncoding
    defaults: ISO-8859-1
    required: false
  - type: STRING
    description: Path to the output serv resource - optional.
    name: servOutputResourcePath
    required: false
  - type: STRING
    description: The output serv service name - dependent.
    name: servOutputService
    required: false
  - type: STRING
    description: The encoding of the flat output serv - dependent.
    name: servOutputEncoding
    defaults: ISO-8859-1
    required: false
  - type: BOOLEAN
    description: Is this execution treated as a test or not - mandatory.
    name: test
    defaults: "false"
  - type: JSON
    description: JSON struct - mandatory.
    name: sender

variables:
  customScriptsDirPath: "/opt/custom"

tasks:
  - id: archivate-input
    type: io.kestra.core.tasks.scripts.Bash
    description: "Archive input"
    commands:
      - >
        "{{ vars.customScriptsDirPath }}/common-tools/archive.sh"
        "FILE_TO_AR={{ inputs.inputMessage }}"
        "SENDER={{ inputs.CLEID }}"
        "RECEIVER=test"
        "OBJ_CLASS={{ flow.id }}"
        "JOB_ID={{ execution.id }}"
        "TDID={{ inputs.TDID }}"
        "STAGE=INPUT"

  - id: validate-input
    type: io.kestra.core.tasks.scripts.Bash
    description: "Validate input"
    commands:
      - >
       "{{ vars.customScriptsDirPath }}/validation/validate-CIV.sh"
       "VALIDATION_CONFIG="
       "JOBID={{ execution.id }}"
       "TDID={{ inputs.TDID }}"
       "VALIDATION_INPUT={{ inputs.inputMessage }}"
       "VALIDATION_TEMP={{ inputs.inputMessage ~ ".valtf" }}"
       "REACTION_OBJ_CLASS="
       "SENDER=test"
       "RECEIVER={{ inputs.CLEID }}"
       "OBJ_CLASS={{ flow.id }}"
       "COV_DEID={{ inputs.COV_DEID }}"
       "WF_WARNING="

  - id: run-input-serv-when-necessary
    type: io.kestra.core.tasks.flows.Switch
    value: "{{ inputs contains 'servInputResourcePath' ? 'RUN' : 'SKIP' }}"
    cases:
      RUN:
        - id: run-input-serv
          type: io.kestra.core.tasks.scripts.Bash
          description: "Run serv on the input"
          outputFiles:
            - xmlMessage
          commands:
            - >
              "{{ vars.customScriptsDirPath }}/call_serv.sh"
              "{{ inputs.servInputResourcePath }}"
              "{{ inputs.inputMessage }}"
              "{{ outputFiles.xmlMessage }}"
              "service={{ inputs.servInputService }}"
              "{{ "encoding=" ~ inputs.servInputEncoding }}"
    defaults:
      - id: skipping-input-serv
        type: io.kestra.core.tasks.debugs.Echo
        description: "Switch requires to have a task to execute"
        format: "Skipping input serv"

  - id: transform-file-using-XSLT
    type: io.kestra.core.tasks.scripts.Bash
    description: "Transform the input using given XLST transformation"
    outputFiles:
      - transformedMessage
      - tferrFile
    commands:
      - >
        "{{ vars.customScriptsDirPath }}/transform/transform-xslt.sh"
        "XSL_FILE={{ inputs.xsltPath }}"
        "INPUT_FILE={{ outputs contains "run-input-serv" ? outputs["run-input-serv"].outputFiles.xmlMessage : inputs.inputMessage }}"
        "OUTPUT_FILE={{ outputFiles.transformedMessage }}"
        "STRICT_PARSE=true"
        "NAN_VALIDATION=true"
        "TFERR={{ outputFiles.tferrFile }}"
        "REF_NR={{ inputs.REF_NR }}"

  - id: run-output-serv-when-necessary
    type: io.kestra.core.tasks.flows.Switch
    value: "{{ inputs contains 'servOutputResourcePath' ? 'RUN' : 'SKIP' }}"
    cases:
      RUN:
        - id: run-output-serv
          type: io.kestra.core.tasks.scripts.Bash
          description: "Run serv on the output"
          outputFiles:
            - flatMessage
          commands:
            - >
              "{{ vars.customScriptsDirPath }}/call_serv.sh"
              "{{ inputs.servOutputResourcePath }}"
              "{{ outputs["transform-file-using-XSLT"].outputFiles.transformedMessage }}"
              "{{ outputFiles.flatMessage }}"
              "service={{ inputs.servOutputService }}"
              "{{ "encoding=" ~ inputs.servOutputEncoding }}"
    defaults:
      - id: skipping-output-serv
        type: io.kestra.core.tasks.debugs.Echo
        description: "Switch requires to have a task to execute"
        format: "Skipping output serv"

  - id: archivate-output
    type: io.kestra.core.tasks.scripts.Bash
    description: "Archive output"
    commands:
      - >
        "{{ vars.customScriptsDirPath }}/common-tools/archive.sh"
        "FILE_TO_AR={{ outputs contains "run-output-serv" ? outputs["run-output-serv"].outputFiles.flatMessage : outputs["transform-file-using-XSLT"].outputFiles.transformedMessage }}"
        "SENDER={{ inputs.CLEID }}"
        "RECEIVER=test"
        "OBJ_CLASS={{ flow.id }}"
        "JOB_ID={{ execution.id }}"
        "TDID={{ inputs.TDID }}"
        "STAGE=OUTPUT"

  - id: validate-output
    type: io.kestra.core.tasks.scripts.Bash
    description: "Validate output"
    commands:
      - >
        "{{ vars.customScriptsDirPath }}/validation/validate-COV.sh"
        "VALIDATION_CONFIG="
        "JOBID={{ execution.id }}"
        "TDID={{ inputs.TDID }}"
        "VALIDATION_INPUT={{ outputs contains "run-output-serv" ? outputs["run-output-serv"].outputFiles.flatMessage : outputs["transform-file-using-XSLT"].outputFiles.transformedMessage }}"
        "VALIDATION_TEMP={{ inputs.inputMessage ~ ".valtf" }}"
        "REACTION_OBJ_CLASS="
        "SENDER=test"
        "RECEIVER={{ inputs.CLEID }}"
        "OBJ_CLASS={{ flow.id }}"
        "COV_DEID={{ inputs.COV_DEID }}"
        "WF_WARNING="

  - id: sent-to-test
    type: io.kestra.core.tasks.scripts.Bash
    description: "Send the ultimate payload"
    commands:
      - >
        "{{ vars.customScriptsDirPath }}/connectors/test/call-test-send.sh"
        "JOB_ID={{ execution.id }}"
        "FILE={{ outputs contains "run-output-serv" ? outputs["run-output-serv"].outputFiles.flatMessage : outputs["transform-file-using-XSLT"].outputFiles.transformedMessage }}"
        "RECEIVER_SID={{ inputs.sender.RECEIVER_SID }}"
        "VFN={{ inputs.sender.VFN }}"
        "SENDER_SID={{ inputs.sender.SENDER_SID }}"
        "IN_EBCDIC={{ inputs.sender.IN_EBCDIC }}"
        "FORMAT={{ inputs.sender.FORMAT }}"
        "SECURITY={{ inputs.sender.SECURITY }}"
        "SERIALIZE={{ inputs.sender.SERIALIZE }}"
        "LABEL={{ inputs.sender.LABEL }}"
        "PRIORITY={{ inputs.sender.PRIORITY }}"
        "ENCRYPTION={{ inputs.sender.ENCRYPTION }}"
        "COMPRESSION={{ inputs.sender.COMPRESSION }}"
        "SIGN={{ inputs.sender.SIGN }}"
        "REQ_SIGNED_RESPONSE={{ inputs.sender.REQ_SIGNED_RESPONSE }}"

  - id: wait-for-DN
    type: io.kestra.core.tasks.flows.Pause
    description: "Wait 20 sec for the DN delivery notification to arrive from test"
    delay: PT20S
    tasks:
      - id: check-test
        type: io.kestra.core.tasks.scripts.Bash
        description: "Check whether the DN arrived"
        commands:
          - >
            "{{ vars.customScriptsDirPath }}/connectors/rvs/call-rvs-eerp.sh"
            "JOB_ID={{ execution.id }}"
            "FILE={{ outputs contains "run-output-serv" ? outputs["run-output-serv"].outputFiles.flatMessage : outputs["transform-file-using-XSLT"].outputFiles.transformedMessage }}"
            "EERP_FILE={{ outputs contains "run-output-serv" ? outputs["run-output-serv"].outputFiles.flatMessage ~ ".eerp" : outputs["transform-file-using-XSLT"].outputFiles.transformedMessage ~ ".eerp" }}"
            "CLEID={{ inputs.CLEID }}"
            "SENDER={{ inputs.CLEID }}"
            "RECEIVER=test"
            "OBJ_CLASS=IFTS97_DAIM_BE"

taskDefaults:
  - type: io.kestra.core.tasks.scripts.Bash
    values:
      interpreter: /bin/bash # always use Bash
      timeout: PT15S
      retry:
        maxAttempt: 4
        type: exponential
        interval: PT2S
        delayFactor: 2
        maxInterval: PT2M

  - type: io.kestra.core.tasks.debugs.Echo
    values:
      timeout: PT15S
      retry:
        maxAttempt: 4
        type: exponential
        interval: PT2S
        delayFactor: 2
        maxInterval: PT2M
```

Issue comments:
An example of a broken execution duration via `/api/v1/executions/{executionId}`:

[4Dxtymd4UR4yQUjqTs3MSG.json.txt](https://github.com/kestra-io/kestra/files/11109546/4Dxtymd4UR4yQUjqTs3MSG.json.txt)

![image](https://user-images.githubusercontent.com/13468636/228778339-78c89202-4bfe-497c-8650-8de070fef7f5.png)

Pull request review comments:

Patch that resolved the issue:
diff --git a/ui/src/components/executions/Gantt.vue b/ui/src/components/executions/Gantt.vue
index 8afa9cf33f..38596f8038 100644
--- a/ui/src/components/executions/Gantt.vue
+++ b/ui/src/components/executions/Gantt.vue
@@ -31,7 +31,7 @@
                                 </span>
                             </template>
                             <div
-                                :style="{left: Math.max(1, (currentTaskRun.start - 1)) + '%', width: currentTaskRun.width - 1 + '%'}"
+                                :style="{left: currentTaskRun.start + '%', width: currentTaskRun.width + '%'}"
                                 class="task-progress"
                                 @click="onTaskSelect(currentTaskRun.task)"
                             >


Test patch: