diff --git a/Taskfile.yaml b/Taskfile.yaml index 06c688d32..c0dfd7191 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -53,6 +53,12 @@ tasks: SERVER_INTERNAL_CLIENT_BASE_STRATEGY=none SERVER_INTERNAL_CLIENT_BASE_INHERIT_BASE=false EOF + pre: + cmds: + # FIXME: Remove this once we have a better way to handle pre-commit reliably + - pre-commit run --all-files + - pre-commit run --all-files + - pre-commit run --all-files start-db: cmds: - docker compose up -d @@ -185,5 +191,10 @@ tasks: - pre-commit run --all-files || pre-commit run --all-files docs: cmds: - - npx serve -l 4001 & - - cd frontend/docs/ && pnpm run dev + - | + # Create a trap to kill the watcher when the task exits + trap 'kill $(jobs -p) 2>/dev/null' EXIT + + npx chokidar "sdks/**/*.{py,ts,go}" --ignore "**/node_modules/**" --ignore "**/.git/**" --ignore "**/dist/**" --ignore "**/build/**" --initial -c "echo 'TODO: Add a task to generate and sync the docs'" & + + cd frontend/docs/ && pnpm run taskfile-dev diff --git a/api-contracts/openapi/components/schemas/_index.yaml b/api-contracts/openapi/components/schemas/_index.yaml index 7d1fe014d..9ab3e2485 100644 --- a/api-contracts/openapi/components/schemas/_index.yaml +++ b/api-contracts/openapi/components/schemas/_index.yaml @@ -330,3 +330,7 @@ V1LogLineLevel: $ref: "./v1/logs.yaml#/V1LogLineLevel" V1LogLineList: $ref: "./v1/logs.yaml#/V1LogLineList" +V1TaskTiming: + $ref: "./v1/task.yaml#/V1TaskTiming" +V1TaskTimingList: + $ref: "./v1/task.yaml#/V1TaskTimingList" diff --git a/api-contracts/openapi/components/schemas/v1/task.yaml b/api-contracts/openapi/components/schemas/v1/task.yaml index 90249e423..f7e566edf 100644 --- a/api-contracts/openapi/components/schemas/v1/task.yaml +++ b/api-contracts/openapi/components/schemas/v1/task.yaml @@ -118,6 +118,7 @@ V1TaskSummary: - tenantId - type - workflowId + - workflowRunExternalId V1WorkflowRunDisplayName: properties: @@ -332,3 +333,77 @@ V1ReplayTaskRequest: maxLength: 36 filter: $ref: "#/V1TaskFilter" + +V1TaskTiming: + properties: + metadata: + $ref: ".././metadata.yaml#/APIResourceMeta" + depth: + type: integer + description: The depth of the task in the waterfall. + status: + $ref: "#/V1TaskStatus" + taskDisplayName: + type: string + description: The display name of the task run. + taskExternalId: + type: string + description: The external ID of the task. + format: uuid + minLength: 36 + maxLength: 36 + taskId: + type: integer + description: The ID of the task. + taskInsertedAt: + type: string + format: date-time + description: The timestamp the task was inserted. + tenantId: + type: string + description: The ID of the tenant. + example: bb214807-246e-43a5-a25d-41761d1cff9e + minLength: 36 + maxLength: 36 + format: uuid + parentTaskExternalId: + type: string + description: The external ID of the parent task. + format: uuid + minLength: 36 + maxLength: 36 + queuedAt: + type: string + format: date-time + description: The timestamp the task run was queued. + startedAt: + type: string + format: date-time + description: The timestamp the task run started. + finishedAt: + type: string + format: date-time + description: The timestamp the task run finished. + required: + - metadata + - depth + - status + - taskDisplayName + - taskExternalId + - taskId + - taskInsertedAt + - tenantId + +V1TaskTimingList: + type: object + properties: + pagination: + $ref: ".././metadata.yaml#/PaginationResponse" + rows: + type: array + items: + $ref: "#/V1TaskTiming" + description: The list of task timings + required: + - pagination + - rows diff --git a/api-contracts/openapi/openapi.yaml b/api-contracts/openapi/openapi.yaml index 546e9499f..69c455b6d 100644 --- a/api-contracts/openapi/openapi.yaml +++ b/api-contracts/openapi/openapi.yaml @@ -42,6 +42,8 @@ paths: $ref: "./paths/v1/workflow-runs/workflow_run.yaml#/getWorkflowRunDetails" /api/v1/stable/workflow-runs/{v1-workflow-run}/task-events: $ref: "./paths/v1/workflow-runs/workflow_run.yaml#/listTaskEventsForWorkflowRun" + /api/v1/stable/workflow-runs/{v1-workflow-run}/task-timings: + $ref: "./paths/v1/workflow-runs/workflow_run.yaml#/getTimings" /api/v1/stable/tenants/{tenant}/task-metrics: $ref: "./paths/v1/tasks/tasks.yaml#/getTaskStatusMetrics" /api/v1/stable/tenants/{tenant}/task-point-metrics: diff --git a/api-contracts/openapi/paths/v1/tasks/tasks.yaml b/api-contracts/openapi/paths/v1/tasks/tasks.yaml index 7ab429d9a..0ac73abce 100644 --- a/api-contracts/openapi/paths/v1/tasks/tasks.yaml +++ b/api-contracts/openapi/paths/v1/tasks/tasks.yaml @@ -191,6 +191,12 @@ getTaskStatusMetrics: schema: type: string format: date-time + - description: The end time to get metrics for + in: query + name: until + schema: + type: string + format: date-time - description: The workflow id to find runs for in: query name: workflow_ids diff --git a/api-contracts/openapi/paths/v1/workflow-runs/workflow_run.yaml b/api-contracts/openapi/paths/v1/workflow-runs/workflow_run.yaml index e1851ef8f..02a7ed8a6 100644 --- a/api-contracts/openapi/paths/v1/workflow-runs/workflow_run.yaml +++ b/api-contracts/openapi/paths/v1/workflow-runs/workflow_run.yaml @@ -322,3 +322,54 @@ trigger: summary: Create workflow run tags: - Workflow Runs + +getTimings: + get: + x-resources: ["tenant", "v1-workflow-run"] + description: Get the timings for a workflow run + operationId: v1-workflow-run:get:timings + parameters: + - description: The workflow run id to get + in: path + name: v1-workflow-run + required: true + schema: + type: string + format: uuid + minLength: 36 + maxLength: 36 + - description: The depth to retrieve children + in: query + name: depth + required: false + schema: + type: integer + format: int64 + responses: + "200": + content: + application/json: + schema: + $ref: "../../../components/schemas/_index.yaml#/V1TaskTimingList" + description: Successfully listed the tasks + "400": + content: + application/json: + schema: + $ref: "../../../components/schemas/_index.yaml#/APIErrors" + description: A malformed or bad request + "403": + content: + application/json: + schema: + $ref: "../../../components/schemas/_index.yaml#/APIErrors" + description: Forbidden + "501": + content: + application/json: + schema: + $ref: "../../../components/schemas/_index.yaml#/APIErrors" + description: Not implemented + summary: List timings for a workflow run + tags: + - Workflow Runs diff --git a/api/v1/server/handlers/v1/tasks/get_metrics.go b/api/v1/server/handlers/v1/tasks/get_metrics.go index d7b4941a1..62bdfb906 100644 --- a/api/v1/server/handlers/v1/tasks/get_metrics.go +++ b/api/v1/server/handlers/v1/tasks/get_metrics.go @@ -34,6 +34,7 @@ func (t *TasksService) V1TaskListStatusMetrics(ctx echo.Context, request gen.V1T metrics, err := t.config.V1.OLAP().ReadTaskRunMetrics(ctx.Request().Context(), tenantId, v1.ReadTaskRunMetricsOpts{ CreatedAfter: request.Params.Since, + CreatedBefore: request.Params.Until, WorkflowIds: workflowIds, ParentTaskExternalID: parentTaskExternalId, }) diff --git a/api/v1/server/handlers/v1/workflow-runs/get-timings.go b/api/v1/server/handlers/v1/workflow-runs/get-timings.go new file mode 100644 index 000000000..d7ab26271 --- /dev/null +++ b/api/v1/server/handlers/v1/workflow-runs/get-timings.go @@ -0,0 +1,126 @@ +package workflowruns + +import ( + "sort" + + "github.com/labstack/echo/v4" + + "github.com/hatchet-dev/hatchet/api/v1/server/oas/apierrors" + "github.com/hatchet-dev/hatchet/api/v1/server/oas/gen" + "github.com/hatchet-dev/hatchet/pkg/repository/postgres/dbsqlc" + "github.com/hatchet-dev/hatchet/pkg/repository/postgres/sqlchelpers" + v1 "github.com/hatchet-dev/hatchet/pkg/repository/v1" + + transformers "github.com/hatchet-dev/hatchet/api/v1/server/oas/transformers/v1" + + openapi_types "github.com/oapi-codegen/runtime/types" +) + +func (t *V1WorkflowRunsService) V1WorkflowRunGetTimings(ctx echo.Context, request gen.V1WorkflowRunGetTimingsRequestObject) (gen.V1WorkflowRunGetTimingsResponseObject, error) { + tenant := ctx.Get("tenant").(*dbsqlc.Tenant) + tenantId := sqlchelpers.UUIDToStr(tenant.ID) + rawWorkflowRun := ctx.Get("v1-workflow-run").(*v1.V1WorkflowRunPopulator) + + workflowRun := rawWorkflowRun.WorkflowRun + + var depth int32 = 0 + + if request.Params.Depth != nil { + depth = int32(*request.Params.Depth) + } + + if depth > 10 { + return gen.V1WorkflowRunGetTimings400JSONResponse( + apierrors.NewAPIErrors("depth must be less than or equal to 10"), + ), nil + } + + taskTimings, idsToDepths, err := t.config.V1.OLAP().GetTaskTimings( + ctx.Request().Context(), + tenantId, + workflowRun.ExternalID, + depth, + ) + + if err != nil { + return nil, err + } + + result := transformers.ToTaskTimings(taskTimings, idsToDepths) + + // Search for api errors to see how we handle errors in other cases + return gen.V1WorkflowRunGetTimings200JSONResponse( + gen.V1TaskTimingList{ + Rows: DFSOrder(result), + }, + ), nil +} + +func DFSOrder(tasks []gen.V1TaskTiming) []gen.V1TaskTiming { + // build set of task IDs + idSet := make(map[openapi_types.UUID]struct{}, len(tasks)) + for _, t := range tasks { + idSet[t.TaskExternalId] = struct{}{} + } + + // group children and collect roots + children := make(map[openapi_types.UUID][]gen.V1TaskTiming) + var roots []gen.V1TaskTiming + for _, t := range tasks { + if t.ParentTaskExternalId != nil { + if _, ok := idSet[*t.ParentTaskExternalId]; ok { + children[*t.ParentTaskExternalId] = append(children[*t.ParentTaskExternalId], t) + continue + } + } + roots = append(roots, t) + } + + // sort roots by queuedAt, then taskId + sort.SliceStable(roots, func(i, j int) bool { + qi, qj := roots[i].QueuedAt, roots[j].QueuedAt + if qi != nil && qj != nil { + if !qi.Equal(*qj) { + return qi.Before(*qj) + } + } else if qi != nil { + return true + } else if qj != nil { + return false + } + return roots[i].TaskId < roots[j].TaskId + }) + + // sort each child list the same way + for parent, childs := range children { + sort.SliceStable(childs, func(i, j int) bool { + qi, qj := childs[i].QueuedAt, childs[j].QueuedAt + if qi != nil && qj != nil { + if !qi.Equal(*qj) { + return qi.Before(*qj) + } + } else if qi != nil { + return true + } else if qj != nil { + return false + } + return childs[i].TaskId < childs[j].TaskId + }) + children[parent] = childs + } + + // depth-first traversal + var result []gen.V1TaskTiming + var visit func(gen.V1TaskTiming) + visit = func(t gen.V1TaskTiming) { + result = append(result, t) + for _, child := range children[t.TaskExternalId] { + visit(child) + } + } + for _, root := range roots { + visit(root) + } + + return result +} diff --git a/api/v1/server/oas/gen/openapi.gen.go b/api/v1/server/oas/gen/openapi.gen.go index 8de9f65a0..a83123736 100644 --- a/api/v1/server/oas/gen/openapi.gen.go +++ b/api/v1/server/oas/gen/openapi.gen.go @@ -1328,7 +1328,7 @@ type V1TaskSummary struct { WorkflowName *string `json:"workflowName,omitempty"` // WorkflowRunExternalId The external ID of the workflow run - WorkflowRunExternalId *openapi_types.UUID `json:"workflowRunExternalId,omitempty"` + WorkflowRunExternalId openapi_types.UUID `json:"workflowRunExternalId"` // WorkflowVersionId The version ID of the workflow WorkflowVersionId *openapi_types.UUID `json:"workflowVersionId,omitempty"` @@ -1342,6 +1342,49 @@ type V1TaskSummaryList struct { Rows []V1TaskSummary `json:"rows"` } +// V1TaskTiming defines model for V1TaskTiming. +type V1TaskTiming struct { + // Depth The depth of the task in the waterfall. + Depth int `json:"depth"` + + // FinishedAt The timestamp the task run finished. + FinishedAt *time.Time `json:"finishedAt,omitempty"` + Metadata APIResourceMeta `json:"metadata"` + + // ParentTaskExternalId The external ID of the parent task. + ParentTaskExternalId *openapi_types.UUID `json:"parentTaskExternalId,omitempty"` + + // QueuedAt The timestamp the task run was queued. + QueuedAt *time.Time `json:"queuedAt,omitempty"` + + // StartedAt The timestamp the task run started. + StartedAt *time.Time `json:"startedAt,omitempty"` + Status V1TaskStatus `json:"status"` + + // TaskDisplayName The display name of the task run. + TaskDisplayName string `json:"taskDisplayName"` + + // TaskExternalId The external ID of the task. + TaskExternalId openapi_types.UUID `json:"taskExternalId"` + + // TaskId The ID of the task. + TaskId int `json:"taskId"` + + // TaskInsertedAt The timestamp the task was inserted. + TaskInsertedAt time.Time `json:"taskInsertedAt"` + + // TenantId The ID of the tenant. + TenantId openapi_types.UUID `json:"tenantId"` +} + +// V1TaskTimingList defines model for V1TaskTimingList. +type V1TaskTimingList struct { + Pagination PaginationResponse `json:"pagination"` + + // Rows The list of task timings + Rows []V1TaskTiming `json:"rows"` +} + // V1TriggerWorkflowRunRequest defines model for V1TriggerWorkflowRunRequest. type V1TriggerWorkflowRunRequest struct { AdditionalMetadata *map[string]interface{} `json:"additionalMetadata,omitempty"` @@ -1814,6 +1857,9 @@ type V1TaskListStatusMetricsParams struct { // Since The start time to get metrics for Since time.Time `form:"since" json:"since"` + // Until The end time to get metrics for + Until *time.Time `form:"until,omitempty" json:"until,omitempty"` + // WorkflowIds The workflow id to find runs for WorkflowIds *[]openapi_types.UUID `form:"workflow_ids,omitempty" json:"workflow_ids,omitempty"` @@ -1878,6 +1924,12 @@ type V1WorkflowRunTaskEventsListParams struct { Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"` } +// V1WorkflowRunGetTimingsParams defines parameters for V1WorkflowRunGetTimings. +type V1WorkflowRunGetTimingsParams struct { + // Depth The depth to retrieve children + Depth *int64 `form:"depth,omitempty" json:"depth,omitempty"` +} + // StepRunListArchivesParams defines parameters for StepRunListArchives. type StepRunListArchivesParams struct { // Offset The number to skip @@ -2309,6 +2361,9 @@ type ServerInterface interface { // List tasks // (GET /api/v1/stable/workflow-runs/{v1-workflow-run}/task-events) V1WorkflowRunTaskEventsList(ctx echo.Context, v1WorkflowRun openapi_types.UUID, params V1WorkflowRunTaskEventsListParams) error + // List timings for a workflow run + // (GET /api/v1/stable/workflow-runs/{v1-workflow-run}/task-timings) + V1WorkflowRunGetTimings(ctx echo.Context, v1WorkflowRun openapi_types.UUID, params V1WorkflowRunGetTimingsParams) error // List archives for step run // (GET /api/v1/step-runs/{step-run}/archives) StepRunListArchives(ctx echo.Context, stepRun openapi_types.UUID, params StepRunListArchivesParams) error @@ -2918,6 +2973,13 @@ func (w *ServerInterfaceWrapper) V1TaskListStatusMetrics(ctx echo.Context) error return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter since: %s", err)) } + // ------------- Optional query parameter "until" ------------- + + err = runtime.BindQueryParameter("form", true, false, "until", ctx.QueryParams(), ¶ms.Until) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter until: %s", err)) + } + // ------------- Optional query parameter "workflow_ids" ------------- err = runtime.BindQueryParameter("form", true, false, "workflow_ids", ctx.QueryParams(), ¶ms.WorkflowIds) @@ -3210,6 +3272,35 @@ func (w *ServerInterfaceWrapper) V1WorkflowRunTaskEventsList(ctx echo.Context) e return err } +// V1WorkflowRunGetTimings converts echo context to params. +func (w *ServerInterfaceWrapper) V1WorkflowRunGetTimings(ctx echo.Context) error { + var err error + // ------------- Path parameter "v1-workflow-run" ------------- + var v1WorkflowRun openapi_types.UUID + + err = runtime.BindStyledParameterWithLocation("simple", false, "v1-workflow-run", runtime.ParamLocationPath, ctx.Param("v1-workflow-run"), &v1WorkflowRun) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter v1-workflow-run: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{}) + + ctx.Set(CookieAuthScopes, []string{}) + + // Parameter object where we will unmarshal all parameters from the context + var params V1WorkflowRunGetTimingsParams + // ------------- Optional query parameter "depth" ------------- + + err = runtime.BindQueryParameter("form", true, false, "depth", ctx.QueryParams(), ¶ms.Depth) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter depth: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.V1WorkflowRunGetTimings(ctx, v1WorkflowRun, params) + return err +} + // StepRunListArchives converts echo context to params. func (w *ServerInterfaceWrapper) StepRunListArchives(ctx echo.Context) error { var err error @@ -5389,6 +5480,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.POST(baseURL+"/api/v1/stable/tenants/:tenant/workflow-runs/trigger", wrapper.V1WorkflowRunCreate) router.GET(baseURL+"/api/v1/stable/workflow-runs/:v1-workflow-run", wrapper.V1WorkflowRunGet) router.GET(baseURL+"/api/v1/stable/workflow-runs/:v1-workflow-run/task-events", wrapper.V1WorkflowRunTaskEventsList) + router.GET(baseURL+"/api/v1/stable/workflow-runs/:v1-workflow-run/task-timings", wrapper.V1WorkflowRunGetTimings) router.GET(baseURL+"/api/v1/step-runs/:step-run/archives", wrapper.StepRunListArchives) router.GET(baseURL+"/api/v1/step-runs/:step-run/events", wrapper.StepRunListEvents) router.GET(baseURL+"/api/v1/step-runs/:step-run/logs", wrapper.LogLineList) @@ -6519,6 +6611,51 @@ func (response V1WorkflowRunTaskEventsList501JSONResponse) VisitV1WorkflowRunTas return json.NewEncoder(w).Encode(response) } +type V1WorkflowRunGetTimingsRequestObject struct { + V1WorkflowRun openapi_types.UUID `json:"v1-workflow-run"` + Params V1WorkflowRunGetTimingsParams +} + +type V1WorkflowRunGetTimingsResponseObject interface { + VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error +} + +type V1WorkflowRunGetTimings200JSONResponse V1TaskTimingList + +func (response V1WorkflowRunGetTimings200JSONResponse) VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type V1WorkflowRunGetTimings400JSONResponse APIErrors + +func (response V1WorkflowRunGetTimings400JSONResponse) VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type V1WorkflowRunGetTimings403JSONResponse APIErrors + +func (response V1WorkflowRunGetTimings403JSONResponse) VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type V1WorkflowRunGetTimings501JSONResponse APIErrors + +func (response V1WorkflowRunGetTimings501JSONResponse) VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(501) + + return json.NewEncoder(w).Encode(response) +} + type StepRunListArchivesRequestObject struct { StepRun openapi_types.UUID `json:"step-run"` Params StepRunListArchivesParams @@ -9644,6 +9781,8 @@ type StrictServerInterface interface { V1WorkflowRunTaskEventsList(ctx echo.Context, request V1WorkflowRunTaskEventsListRequestObject) (V1WorkflowRunTaskEventsListResponseObject, error) + V1WorkflowRunGetTimings(ctx echo.Context, request V1WorkflowRunGetTimingsRequestObject) (V1WorkflowRunGetTimingsResponseObject, error) + StepRunListArchives(ctx echo.Context, request StepRunListArchivesRequestObject) (StepRunListArchivesResponseObject, error) StepRunListEvents(ctx echo.Context, request StepRunListEventsRequestObject) (StepRunListEventsResponseObject, error) @@ -10512,6 +10651,32 @@ func (sh *strictHandler) V1WorkflowRunTaskEventsList(ctx echo.Context, v1Workflo return nil } +// V1WorkflowRunGetTimings operation middleware +func (sh *strictHandler) V1WorkflowRunGetTimings(ctx echo.Context, v1WorkflowRun openapi_types.UUID, params V1WorkflowRunGetTimingsParams) error { + var request V1WorkflowRunGetTimingsRequestObject + + request.V1WorkflowRun = v1WorkflowRun + request.Params = params + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.V1WorkflowRunGetTimings(ctx, request.(V1WorkflowRunGetTimingsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "V1WorkflowRunGetTimings") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(V1WorkflowRunGetTimingsResponseObject); ok { + return validResponse.VisitV1WorkflowRunGetTimingsResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("Unexpected response type: %T", response) + } + return nil +} + // StepRunListArchives operation middleware func (sh *strictHandler) StepRunListArchives(ctx echo.Context, stepRun openapi_types.UUID, params StepRunListArchivesParams) error { var request StepRunListArchivesRequestObject @@ -12666,244 +12831,247 @@ func (sh *strictHandler) WorkflowVersionGet(ctx echo.Context, workflow openapi_t // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/4iwYK/VaQIA", + "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/v1/server/oas/transformers/v1/tasks.go b/api/v1/server/oas/transformers/v1/tasks.go index 4a25ee66b..14d502780 100644 --- a/api/v1/server/oas/transformers/v1/tasks.go +++ b/api/v1/server/oas/transformers/v1/tasks.go @@ -23,7 +23,7 @@ func jsonToMap(jsonBytes []byte) map[string]interface{} { } func ToTaskSummary(task *sqlcv1.PopulateTaskRunDataRow) gen.V1TaskSummary { - workflowVersionId := uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowVersionID)) + workflowVersionID := uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowVersionID)) additionalMetadata := jsonToMap(task.AdditionalMetadata) var finishedAt *time.Time @@ -54,24 +54,25 @@ func ToTaskSummary(task *sqlcv1.PopulateTaskRunDataRow) gen.V1TaskSummary { CreatedAt: task.InsertedAt.Time, UpdatedAt: task.InsertedAt.Time, }, - Input: jsonToMap(task.Input), - Output: jsonToMap(task.Output), - Type: gen.V1WorkflowTypeTASK, - DisplayName: task.DisplayName, - Duration: durationPtr, - StartedAt: startedAt, - FinishedAt: finishedAt, - AdditionalMetadata: &additionalMetadata, - ErrorMessage: &task.ErrorMessage.String, - Status: gen.V1TaskStatus(task.Status), - TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)), - WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)), - TaskId: int(task.ID), - TaskInsertedAt: task.InsertedAt.Time, - TaskExternalId: taskExternalId, - StepId: &stepId, - ActionId: &task.ActionID, - WorkflowVersionId: &workflowVersionId, + Input: jsonToMap(task.Input), + Output: jsonToMap(task.Output), + Type: gen.V1WorkflowTypeTASK, + DisplayName: task.DisplayName, + Duration: durationPtr, + StartedAt: startedAt, + FinishedAt: finishedAt, + AdditionalMetadata: &additionalMetadata, + ErrorMessage: &task.ErrorMessage.String, + Status: gen.V1TaskStatus(task.Status), + TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)), + WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)), + TaskId: int(task.ID), + TaskInsertedAt: task.InsertedAt.Time, + TaskExternalId: taskExternalId, + StepId: &stepId, + ActionId: &task.ActionID, + WorkflowRunExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowRunID)), + WorkflowVersionId: &workflowVersionID, } } @@ -228,8 +229,8 @@ func ToTaskRunMetrics(metrics *[]v1.TaskRunMetric) gen.V1TaskRunMetrics { return toReturn } -func ToTask(taskWithData *sqlcv1.PopulateSingleTaskRunDataRow, workflowRunExternalId *pgtype.UUID) gen.V1TaskSummary { - workflowVersionId := uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.WorkflowVersionID)) +func ToTask(taskWithData *sqlcv1.PopulateSingleTaskRunDataRow, workflowRunExternalId pgtype.UUID) gen.V1TaskSummary { + workflowVersionID := uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.WorkflowVersionID)) additionalMetadata := jsonToMap(taskWithData.AdditionalMetadata) var finishedAt *time.Time @@ -259,13 +260,6 @@ func ToTask(taskWithData *sqlcv1.PopulateSingleTaskRunDataRow, workflowRunExtern input := jsonToMap(taskWithData.Input) - var parsedWorkflowRunUUID *uuid.UUID - - if workflowRunExternalId != nil && workflowRunExternalId.Valid { - id := uuid.MustParse(sqlchelpers.UUIDToStr(*workflowRunExternalId)) - parsedWorkflowRunUUID = &id - } - stepId := uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.StepID)) return gen.V1TaskSummary{ @@ -287,13 +281,13 @@ func ToTask(taskWithData *sqlcv1.PopulateSingleTaskRunDataRow, workflowRunExtern TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.TenantID)), WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.WorkflowID)), ErrorMessage: &taskWithData.ErrorMessage.String, - WorkflowRunExternalId: parsedWorkflowRunUUID, + WorkflowRunExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(workflowRunExternalId)), TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.ExternalID)), Type: gen.V1WorkflowTypeTASK, NumSpawnedChildren: int(taskWithData.SpawnedChildren.Int64), StepId: &stepId, ActionId: &taskWithData.ActionID, - WorkflowVersionId: &workflowVersionId, + WorkflowVersionId: &workflowVersionID, } } @@ -391,3 +385,48 @@ func ToWorkflowRunDetails( Tasks: parsedTasks, }, nil } + +func ToTaskTimings( + timings []*sqlcv1.PopulateTaskRunDataRow, + idsToDepth map[string]int32, +) []gen.V1TaskTiming { + toReturn := make([]gen.V1TaskTiming, len(timings)) + + for i, timing := range timings { + depth := idsToDepth[sqlchelpers.UUIDToStr(timing.ExternalID)] + + toReturn[i] = gen.V1TaskTiming{ + Metadata: gen.APIResourceMeta{ + Id: sqlchelpers.UUIDToStr(timing.ExternalID), + CreatedAt: timing.InsertedAt.Time, + UpdatedAt: timing.InsertedAt.Time, + }, + Status: gen.V1TaskStatus(timing.Status), + TaskDisplayName: timing.DisplayName, + TaskId: int(timing.ID), + TaskInsertedAt: timing.InsertedAt.Time, + TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(timing.ExternalID)), + TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(timing.TenantID)), + Depth: int(depth), + } + + if timing.QueuedAt.Valid { + toReturn[i].QueuedAt = &timing.QueuedAt.Time + } + + if timing.StartedAt.Valid { + toReturn[i].StartedAt = &timing.StartedAt.Time + } + + if timing.FinishedAt.Valid { + toReturn[i].FinishedAt = &timing.FinishedAt.Time + } + + if timing.ParentTaskExternalID.Valid { + parentId := uuid.MustParse(sqlchelpers.UUIDToStr(timing.ParentTaskExternalID)) + toReturn[i].ParentTaskExternalId = &parentId + } + } + + return toReturn +} diff --git a/api/v1/server/oas/transformers/v1/workflow_runs.go b/api/v1/server/oas/transformers/v1/workflow_runs.go index 4d8eb23e4..f7191d869 100644 --- a/api/v1/server/oas/transformers/v1/workflow_runs.go +++ b/api/v1/server/oas/transformers/v1/workflow_runs.go @@ -69,26 +69,27 @@ func WorkflowRunDataToV1TaskSummary(task *v1.WorkflowRunData, workflowIdsToNames CreatedAt: task.InsertedAt.Time, UpdatedAt: task.InsertedAt.Time, }, - CreatedAt: task.CreatedAt.Time, - DisplayName: task.DisplayName, - Duration: durationPtr, - StartedAt: startedAt, - FinishedAt: finishedAt, - Input: input, - Output: output, - AdditionalMetadata: &additionalMetadata, - ErrorMessage: &task.ErrorMessage, - Status: gen.V1TaskStatus(task.ReadableStatus), - TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)), - WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)), - WorkflowVersionId: &workflowVersionId, - TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)), - TaskId: taskId, - TaskInsertedAt: task.InsertedAt.Time, - Type: gen.V1WorkflowTypeDAG, - WorkflowName: workflowName, - StepId: &stepId, - ActionId: &actionId, + CreatedAt: task.CreatedAt.Time, + DisplayName: task.DisplayName, + Duration: durationPtr, + StartedAt: startedAt, + FinishedAt: finishedAt, + Input: input, + Output: output, + AdditionalMetadata: &additionalMetadata, + ErrorMessage: &task.ErrorMessage, + Status: gen.V1TaskStatus(task.ReadableStatus), + TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)), + WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)), + WorkflowVersionId: &workflowVersionId, + TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)), + TaskId: taskId, + TaskInsertedAt: task.InsertedAt.Time, + Type: gen.V1WorkflowTypeDAG, + WorkflowName: workflowName, + StepId: &stepId, + ActionId: &actionId, + WorkflowRunExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)), } } @@ -138,7 +139,7 @@ func ToWorkflowRunMany( } func PopulateTaskRunDataRowToV1TaskSummary(task *sqlcv1.PopulateTaskRunDataRow, workflowName *string) gen.V1TaskSummary { - workflowVersionId := uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowVersionID)) + workflowVersionID := uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowVersionID)) additionalMetadata := jsonToMap(task.AdditionalMetadata) var finishedAt *time.Time @@ -170,27 +171,28 @@ func PopulateTaskRunDataRowToV1TaskSummary(task *sqlcv1.PopulateTaskRunDataRow, CreatedAt: task.InsertedAt.Time, UpdatedAt: task.InsertedAt.Time, }, - CreatedAt: task.InsertedAt.Time, - DisplayName: task.DisplayName, - Duration: durationPtr, - StartedAt: startedAt, - FinishedAt: finishedAt, - Input: input, - Output: output, - AdditionalMetadata: &additionalMetadata, - ErrorMessage: &task.ErrorMessage.String, - Status: gen.V1TaskStatus(task.Status), - TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)), - WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)), - WorkflowVersionId: &workflowVersionId, - Children: nil, - TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)), - TaskId: int(task.ID), - TaskInsertedAt: task.InsertedAt.Time, - Type: gen.V1WorkflowTypeTASK, - WorkflowName: workflowName, - StepId: &stepId, - ActionId: &task.ActionID, + CreatedAt: task.InsertedAt.Time, + DisplayName: task.DisplayName, + Duration: durationPtr, + StartedAt: startedAt, + FinishedAt: finishedAt, + Input: input, + Output: output, + AdditionalMetadata: &additionalMetadata, + ErrorMessage: &task.ErrorMessage.String, + Status: gen.V1TaskStatus(task.Status), + TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)), + WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)), + WorkflowVersionId: &workflowVersionID, + Children: nil, + TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)), + TaskId: int(task.ID), + TaskInsertedAt: task.InsertedAt.Time, + Type: gen.V1WorkflowTypeTASK, + WorkflowName: workflowName, + StepId: &stepId, + ActionId: &task.ActionID, + WorkflowRunExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowRunID)), } } diff --git a/frontend/app/src/lib/api/generated/Api.ts b/frontend/app/src/lib/api/generated/Api.ts index cd0c7bf37..2f091e08a 100644 --- a/frontend/app/src/lib/api/generated/Api.ts +++ b/frontend/app/src/lib/api/generated/Api.ts @@ -94,6 +94,7 @@ import { V1TaskStatus, V1TaskSummary, V1TaskSummaryList, + V1TaskTimingList, V1TriggerWorkflowRunRequest, V1WorkflowRunDetails, V1WorkflowRunDisplayNameList, @@ -417,6 +418,34 @@ export class Api extends HttpClient + this.request({ + path: `/api/v1/stable/workflow-runs/${v1WorkflowRun}/task-timings`, + method: 'GET', + query: query, + secure: true, + format: 'json', + ...params, + }); /** * @description Get a summary of task run metrics for a tenant * @@ -434,6 +463,11 @@ export class Api extends HttpClient 10 { + return nil, nil, fmt.Errorf("depth too large") + } + + // start out by getting a list of task external ids for the workflow run id + rootTaskExternalIds := make([]pgtype.UUID, 0) + + rootTasks, err := r.queries.FlattenTasksByExternalIds(ctx, r.pool, sqlcv1.FlattenTasksByExternalIdsParams{ + Externalids: []pgtype.UUID{workflowRunId}, + Tenantid: sqlchelpers.UUIDFromStr(tenantId), + }) + + if err != nil { + return nil, nil, err + } + + for _, task := range rootTasks { + rootTaskExternalIds = append(rootTaskExternalIds, task.ExternalID) + } + + runsList, err := r.queries.GetRunsListRecursive(ctx, r.pool, sqlcv1.GetRunsListRecursiveParams{ + Taskexternalids: rootTaskExternalIds, + Tenantid: sqlchelpers.UUIDFromStr(tenantId), + Depth: depth, + }) + + if err != nil { + return nil, nil, err + } + + taskIds := make([]int64, 0) + taskInsertedAts := make([]pgtype.Timestamptz, 0) + + // associate each run external id with a depth + idsToDepth := make(map[string]int32) + + for _, row := range runsList { + taskIds = append(taskIds, row.ID) + taskInsertedAts = append(taskInsertedAts, row.InsertedAt) + + idsToDepth[sqlchelpers.UUIDToStr(row.ExternalID)] = row.Depth + } + + tasksWithData, err := r.queries.PopulateTaskRunData(ctx, r.pool, sqlcv1.PopulateTaskRunDataParams{ + Taskids: taskIds, + Taskinsertedats: taskInsertedAts, + Tenantid: sqlchelpers.UUIDFromStr(tenantId), + }) + + if err != nil && !errors.Is(err, pgx.ErrNoRows) { + return nil, nil, err + } + + return tasksWithData, idsToDepth, nil +} diff --git a/pkg/repository/v1/sqlcv1/olap.sql b/pkg/repository/v1/sqlcv1/olap.sql index d2f5a259a..5c785d48c 100644 --- a/pkg/repository/v1/sqlcv1/olap.sql +++ b/pkg/repository/v1/sqlcv1/olap.sql @@ -447,7 +447,9 @@ WITH input AS ( t.display_name, t.input, t.additional_metadata, - t.readable_status + t.readable_status, + t.parent_task_external_id, + t.workflow_run_id FROM v1_tasks_olap t JOIN @@ -471,10 +473,10 @@ WITH input AS ( relevant_events e GROUP BY e.tenant_id, e.task_id, e.task_inserted_at -), finished_ats AS ( +), queued_ats AS ( SELECT e.task_id::bigint, - MAX(e.event_timestamp) AS finished_at + MAX(e.event_timestamp) AS queued_at FROM relevant_events e JOIN @@ -484,7 +486,7 @@ WITH input AS ( AND e.task_inserted_at = mrc.task_inserted_at AND e.retry_count = mrc.max_retry_count WHERE - e.readable_status = ANY(ARRAY['COMPLETED', 'FAILED', 'CANCELLED']::v1_readable_status_olap[]) + e.event_type = 'QUEUED' GROUP BY e.task_id ), started_ats AS ( SELECT @@ -501,6 +503,21 @@ WITH input AS ( WHERE e.event_type = 'STARTED' GROUP BY e.task_id +), finished_ats AS ( + SELECT + e.task_id::bigint, + MAX(e.event_timestamp) AS finished_at + FROM + relevant_events e + JOIN + max_retry_counts mrc ON + e.tenant_id = mrc.tenant_id + AND e.task_id = mrc.task_id + AND e.task_inserted_at = mrc.task_inserted_at + AND e.retry_count = mrc.max_retry_count + WHERE + e.readable_status = ANY(ARRAY['COMPLETED', 'FAILED', 'CANCELLED']::v1_readable_status_olap[]) + GROUP BY e.task_id ), error_message AS ( SELECT DISTINCT ON (e.task_id) e.task_id::bigint, @@ -528,7 +545,6 @@ WITH input AS ( GROUP BY task_id ) - SELECT t.tenant_id, t.id, @@ -545,10 +561,13 @@ SELECT t.sticky, t.display_name, t.additional_metadata, + t.parent_task_external_id, t.input, t.readable_status::v1_readable_status_olap as status, + t.workflow_run_id, f.finished_at::timestamptz as finished_at, s.started_at::timestamptz as started_at, + q.queued_at::timestamptz as queued_at, e.error_message as error_message, o.output::jsonb as output FROM @@ -557,13 +576,14 @@ LEFT JOIN finished_ats f ON f.task_id = t.id LEFT JOIN started_ats s ON s.task_id = t.id +LEFT JOIN + queued_ats q ON q.task_id = t.id LEFT JOIN error_message e ON e.task_id = t.id LEFT JOIN task_output o ON o.task_id = t.id ORDER BY t.inserted_at DESC, t.id DESC; - -- name: UpdateTaskStatuses :one WITH locked_events AS ( SELECT @@ -988,6 +1008,9 @@ FROM v1_statuses_olap WHERE tenant_id = @tenantId::UUID AND inserted_at >= @createdAfter::TIMESTAMPTZ + AND ( + sqlc.narg('createdBefore')::TIMESTAMPTZ IS NULL OR inserted_at <= sqlc.narg('createdBefore')::TIMESTAMPTZ + ) AND ( sqlc.narg('workflowIds')::UUID[] IS NULL OR workflow_id = ANY(sqlc.narg('workflowIds')::UUID[]) ) @@ -1127,7 +1150,7 @@ WITH lookups AS ( FROM lookups l JOIN - v1_dag_to_task_olap dt ON l.dag_id = dt.dag_id + v1_dag_to_task_olap dt ON l.dag_id = dt.dag_id AND l.inserted_at = dt.dag_inserted_at WHERE l.dag_id IS NOT NULL ), unioned_tasks AS ( @@ -1150,6 +1173,7 @@ SELECT t.tenant_id, t.id, t.inserted_at, + t.external_id, t.latest_retry_count AS retry_count FROM v1_tasks_olap t @@ -1170,3 +1194,66 @@ WHERE AND lt.tenant_id = @tenantId::uuid LIMIT 10000 ; + +-- name: GetRunsListRecursive :many +WITH RECURSIVE all_runs AS ( + -- seed term + SELECT + t.id, + t.inserted_at, + t.tenant_id, + t.external_id, + t.parent_task_external_id, + 0 AS depth + FROM + v1_lookup_table_olap lt + JOIN v1_tasks_olap t + ON t.inserted_at = lt.inserted_at + AND t.id = lt.task_id + WHERE + lt.external_id = ANY(@taskExternalIds::uuid[]) + + UNION ALL + + -- single recursive term for both DAG- and TASK-driven children + SELECT + t.id, + t.inserted_at, + t.tenant_id, + t.external_id, + t.parent_task_external_id, + ar.depth + 1 AS depth + FROM + v1_runs_olap r + JOIN all_runs ar ON ar.external_id = r.parent_task_external_id + + -- only present when r.kind = 'DAG' + LEFT JOIN v1_dag_to_task_olap dt ON r.kind = 'DAG' AND r.id = dt.dag_id AND r.inserted_at = dt.dag_inserted_at + + -- pick the correct task row for either branch + JOIN v1_tasks_olap t + ON ( + r.kind = 'DAG' + AND t.id = dt.task_id + AND t.inserted_at = dt.task_inserted_at + ) + OR ( + r.kind = 'TASK' + AND t.id = r.id + AND t.inserted_at = r.inserted_at + ) + WHERE + r.tenant_id = @tenantId::uuid + AND ar.depth < @depth::int +) +SELECT + tenant_id, + id, + inserted_at, + external_id, + parent_task_external_id, + depth +FROM + all_runs +WHERE + tenant_id = @tenantId::uuid; diff --git a/pkg/repository/v1/sqlcv1/olap.sql.go b/pkg/repository/v1/sqlcv1/olap.sql.go index 0b49fa958..0007afdb4 100644 --- a/pkg/repository/v1/sqlcv1/olap.sql.go +++ b/pkg/repository/v1/sqlcv1/olap.sql.go @@ -111,7 +111,7 @@ WITH lookups AS ( FROM lookups l JOIN - v1_dag_to_task_olap dt ON l.dag_id = dt.dag_id + v1_dag_to_task_olap dt ON l.dag_id = dt.dag_id AND l.inserted_at = dt.dag_inserted_at WHERE l.dag_id IS NOT NULL ), unioned_tasks AS ( @@ -133,6 +133,7 @@ SELECT t.tenant_id, t.id, t.inserted_at, + t.external_id, t.latest_retry_count AS retry_count FROM v1_tasks_olap t @@ -149,6 +150,7 @@ type FlattenTasksByExternalIdsRow struct { TenantID pgtype.UUID `json:"tenant_id"` ID int64 `json:"id"` InsertedAt pgtype.Timestamptz `json:"inserted_at"` + ExternalID pgtype.UUID `json:"external_id"` RetryCount int32 `json:"retry_count"` } @@ -166,6 +168,7 @@ func (q *Queries) FlattenTasksByExternalIds(ctx context.Context, db DBTX, arg Fl &i.TenantID, &i.ID, &i.InsertedAt, + &i.ExternalID, &i.RetryCount, ); err != nil { return nil, err @@ -178,6 +181,112 @@ func (q *Queries) FlattenTasksByExternalIds(ctx context.Context, db DBTX, arg Fl return items, nil } +const getRunsListRecursive = `-- name: GetRunsListRecursive :many +WITH RECURSIVE all_runs AS ( + -- seed term + SELECT + t.id, + t.inserted_at, + t.tenant_id, + t.external_id, + t.parent_task_external_id, + 0 AS depth + FROM + v1_lookup_table_olap lt + JOIN v1_tasks_olap t + ON t.inserted_at = lt.inserted_at + AND t.id = lt.task_id + WHERE + lt.external_id = ANY($2::uuid[]) + + UNION ALL + + -- single recursive term for both DAG- and TASK-driven children + SELECT + t.id, + t.inserted_at, + t.tenant_id, + t.external_id, + t.parent_task_external_id, + ar.depth + 1 AS depth + FROM + v1_runs_olap r + JOIN all_runs ar ON ar.external_id = r.parent_task_external_id + + -- only present when r.kind = 'DAG' + LEFT JOIN v1_dag_to_task_olap dt ON r.kind = 'DAG' AND r.id = dt.dag_id AND r.inserted_at = dt.dag_inserted_at + + -- pick the correct task row for either branch + JOIN v1_tasks_olap t + ON ( + r.kind = 'DAG' + AND t.id = dt.task_id + AND t.inserted_at = dt.task_inserted_at + ) + OR ( + r.kind = 'TASK' + AND t.id = r.id + AND t.inserted_at = r.inserted_at + ) + WHERE + r.tenant_id = $1::uuid + AND ar.depth < $3::int +) +SELECT + tenant_id, + id, + inserted_at, + external_id, + parent_task_external_id, + depth +FROM + all_runs +WHERE + tenant_id = $1::uuid +` + +type GetRunsListRecursiveParams struct { + Tenantid pgtype.UUID `json:"tenantid"` + Taskexternalids []pgtype.UUID `json:"taskexternalids"` + Depth int32 `json:"depth"` +} + +type GetRunsListRecursiveRow struct { + TenantID pgtype.UUID `json:"tenant_id"` + ID int64 `json:"id"` + InsertedAt pgtype.Timestamptz `json:"inserted_at"` + ExternalID pgtype.UUID `json:"external_id"` + ParentTaskExternalID pgtype.UUID `json:"parent_task_external_id"` + Depth int32 `json:"depth"` +} + +func (q *Queries) GetRunsListRecursive(ctx context.Context, db DBTX, arg GetRunsListRecursiveParams) ([]*GetRunsListRecursiveRow, error) { + rows, err := db.Query(ctx, getRunsListRecursive, arg.Tenantid, arg.Taskexternalids, arg.Depth) + if err != nil { + return nil, err + } + defer rows.Close() + var items []*GetRunsListRecursiveRow + for rows.Next() { + var i GetRunsListRecursiveRow + if err := rows.Scan( + &i.TenantID, + &i.ID, + &i.InsertedAt, + &i.ExternalID, + &i.ParentTaskExternalID, + &i.Depth, + ); err != nil { + return nil, err + } + items = append(items, &i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getTaskPointMetrics = `-- name: GetTaskPointMetrics :many SELECT DATE_BIN( @@ -239,7 +348,7 @@ WITH task_external_ids AS ( SELECT external_id FROM v1_runs_olap WHERE ( - $4::UUID IS NULL OR parent_task_external_id = $4::UUID + $5::UUID IS NULL OR parent_task_external_id = $5::UUID ) ) SELECT @@ -254,7 +363,10 @@ WHERE tenant_id = $1::UUID AND inserted_at >= $2::TIMESTAMPTZ AND ( - $3::UUID[] IS NULL OR workflow_id = ANY($3::UUID[]) + $3::TIMESTAMPTZ IS NULL OR inserted_at <= $3::TIMESTAMPTZ + ) + AND ( + $4::UUID[] IS NULL OR workflow_id = ANY($4::UUID[]) ) AND external_id IN ( SELECT external_id @@ -266,6 +378,7 @@ GROUP BY tenant_id type GetTenantStatusMetricsParams struct { Tenantid pgtype.UUID `json:"tenantid"` Createdafter pgtype.Timestamptz `json:"createdafter"` + CreatedBefore pgtype.Timestamptz `json:"createdBefore"` WorkflowIds []pgtype.UUID `json:"workflowIds"` ParentTaskExternalId pgtype.UUID `json:"parentTaskExternalId"` } @@ -283,6 +396,7 @@ func (q *Queries) GetTenantStatusMetrics(ctx context.Context, db DBTX, arg GetTe row := db.QueryRow(ctx, getTenantStatusMetrics, arg.Tenantid, arg.Createdafter, + arg.CreatedBefore, arg.WorkflowIds, arg.ParentTaskExternalId, ) @@ -1092,7 +1206,9 @@ WITH input AS ( t.display_name, t.input, t.additional_metadata, - t.readable_status + t.readable_status, + t.parent_task_external_id, + t.workflow_run_id FROM v1_tasks_olap t JOIN @@ -1116,10 +1232,10 @@ WITH input AS ( relevant_events e GROUP BY e.tenant_id, e.task_id, e.task_inserted_at -), finished_ats AS ( +), queued_ats AS ( SELECT e.task_id::bigint, - MAX(e.event_timestamp) AS finished_at + MAX(e.event_timestamp) AS queued_at FROM relevant_events e JOIN @@ -1129,7 +1245,7 @@ WITH input AS ( AND e.task_inserted_at = mrc.task_inserted_at AND e.retry_count = mrc.max_retry_count WHERE - e.readable_status = ANY(ARRAY['COMPLETED', 'FAILED', 'CANCELLED']::v1_readable_status_olap[]) + e.event_type = 'QUEUED' GROUP BY e.task_id ), started_ats AS ( SELECT @@ -1146,6 +1262,21 @@ WITH input AS ( WHERE e.event_type = 'STARTED' GROUP BY e.task_id +), finished_ats AS ( + SELECT + e.task_id::bigint, + MAX(e.event_timestamp) AS finished_at + FROM + relevant_events e + JOIN + max_retry_counts mrc ON + e.tenant_id = mrc.tenant_id + AND e.task_id = mrc.task_id + AND e.task_inserted_at = mrc.task_inserted_at + AND e.retry_count = mrc.max_retry_count + WHERE + e.readable_status = ANY(ARRAY['COMPLETED', 'FAILED', 'CANCELLED']::v1_readable_status_olap[]) + GROUP BY e.task_id ), error_message AS ( SELECT DISTINCT ON (e.task_id) e.task_id::bigint, @@ -1173,7 +1304,6 @@ WITH input AS ( GROUP BY task_id ) - SELECT t.tenant_id, t.id, @@ -1190,10 +1320,13 @@ SELECT t.sticky, t.display_name, t.additional_metadata, + t.parent_task_external_id, t.input, t.readable_status::v1_readable_status_olap as status, + t.workflow_run_id, f.finished_at::timestamptz as finished_at, s.started_at::timestamptz as started_at, + q.queued_at::timestamptz as queued_at, e.error_message as error_message, o.output::jsonb as output FROM @@ -1202,6 +1335,8 @@ LEFT JOIN finished_ats f ON f.task_id = t.id LEFT JOIN started_ats s ON s.task_id = t.id +LEFT JOIN + queued_ats q ON q.task_id = t.id LEFT JOIN error_message e ON e.task_id = t.id LEFT JOIN @@ -1216,27 +1351,30 @@ type PopulateTaskRunDataParams struct { } type PopulateTaskRunDataRow struct { - TenantID pgtype.UUID `json:"tenant_id"` - ID int64 `json:"id"` - InsertedAt pgtype.Timestamptz `json:"inserted_at"` - ExternalID pgtype.UUID `json:"external_id"` - Queue string `json:"queue"` - ActionID string `json:"action_id"` - StepID pgtype.UUID `json:"step_id"` - WorkflowID pgtype.UUID `json:"workflow_id"` - WorkflowVersionID pgtype.UUID `json:"workflow_version_id"` - ScheduleTimeout string `json:"schedule_timeout"` - StepTimeout pgtype.Text `json:"step_timeout"` - Priority pgtype.Int4 `json:"priority"` - Sticky V1StickyStrategyOlap `json:"sticky"` - DisplayName string `json:"display_name"` - AdditionalMetadata []byte `json:"additional_metadata"` - Input []byte `json:"input"` - Status V1ReadableStatusOlap `json:"status"` - FinishedAt pgtype.Timestamptz `json:"finished_at"` - StartedAt pgtype.Timestamptz `json:"started_at"` - ErrorMessage pgtype.Text `json:"error_message"` - Output []byte `json:"output"` + TenantID pgtype.UUID `json:"tenant_id"` + ID int64 `json:"id"` + InsertedAt pgtype.Timestamptz `json:"inserted_at"` + ExternalID pgtype.UUID `json:"external_id"` + Queue string `json:"queue"` + ActionID string `json:"action_id"` + StepID pgtype.UUID `json:"step_id"` + WorkflowID pgtype.UUID `json:"workflow_id"` + WorkflowVersionID pgtype.UUID `json:"workflow_version_id"` + ScheduleTimeout string `json:"schedule_timeout"` + StepTimeout pgtype.Text `json:"step_timeout"` + Priority pgtype.Int4 `json:"priority"` + Sticky V1StickyStrategyOlap `json:"sticky"` + DisplayName string `json:"display_name"` + AdditionalMetadata []byte `json:"additional_metadata"` + ParentTaskExternalID pgtype.UUID `json:"parent_task_external_id"` + Input []byte `json:"input"` + Status V1ReadableStatusOlap `json:"status"` + WorkflowRunID pgtype.UUID `json:"workflow_run_id"` + FinishedAt pgtype.Timestamptz `json:"finished_at"` + StartedAt pgtype.Timestamptz `json:"started_at"` + QueuedAt pgtype.Timestamptz `json:"queued_at"` + ErrorMessage pgtype.Text `json:"error_message"` + Output []byte `json:"output"` } func (q *Queries) PopulateTaskRunData(ctx context.Context, db DBTX, arg PopulateTaskRunDataParams) ([]*PopulateTaskRunDataRow, error) { @@ -1264,10 +1402,13 @@ func (q *Queries) PopulateTaskRunData(ctx context.Context, db DBTX, arg Populate &i.Sticky, &i.DisplayName, &i.AdditionalMetadata, + &i.ParentTaskExternalID, &i.Input, &i.Status, + &i.WorkflowRunID, &i.FinishedAt, &i.StartedAt, + &i.QueuedAt, &i.ErrorMessage, &i.Output, ); err != nil { diff --git a/pkg/repository/v1/trigger.go b/pkg/repository/v1/trigger.go index 1fd68c012..4317715a6 100644 --- a/pkg/repository/v1/trigger.go +++ b/pkg/repository/v1/trigger.go @@ -148,7 +148,7 @@ func (r *TriggerRepositoryImpl) TriggerFromEvents(ctx context.Context, tenantId for _, opt := range opts { triggerConverter := &TriggeredByEvent{ l: r.l, - eventId: opt.EventId, + eventID: opt.EventId, eventKey: workflow.EventKey, } @@ -309,7 +309,7 @@ type TriggeredBy interface { type TriggeredByEvent struct { l *zerolog.Logger - eventId string + eventID string eventKey string } @@ -338,7 +338,7 @@ func cleanAdditionalMetadata(additionalMetadata []byte) map[string]interface{} { func (t *TriggeredByEvent) ToMetadata(additionalMetadata []byte) []byte { res := cleanAdditionalMetadata(additionalMetadata) - res["hatchet__event_id"] = t.eventId + res["hatchet__event_id"] = t.eventID res["hatchet__event_key"] = t.eventKey resBytes, err := json.Marshal(res)