From a56629ba05f1932ca54b55401c72c914bbfdae63 Mon Sep 17 00:00:00 2001 From: Alexander Belanger Date: Tue, 1 Oct 2024 22:38:45 -0400 Subject: [PATCH] proper list on high queue depth --- .../openapi/components/schemas/_index.yaml | 2 + .../openapi/components/schemas/tenant.yaml | 7 + api-contracts/openapi/openapi.yaml | 2 + .../openapi/paths/tenant/tenant.yaml | 44 ++ .../tenants/get_step_run_queue_metrics.go | 24 + api/v1/server/oas/gen/openapi.gen.go | 457 +++++++++++------- frontend/app/src/lib/api/generated/Api.ts | 18 + .../src/lib/api/generated/data-contracts.ts | 4 + frontend/app/src/lib/api/queries.ts | 5 + .../components/workflow-runs-table.tsx | 2 +- pkg/client/rest/gen.go | 138 ++++++ 11 files changed, 524 insertions(+), 179 deletions(-) create mode 100644 api/v1/server/handlers/tenants/get_step_run_queue_metrics.go diff --git a/api-contracts/openapi/components/schemas/_index.yaml b/api-contracts/openapi/components/schemas/_index.yaml index 5e5d178e7..9c20d96c2 100644 --- a/api-contracts/openapi/components/schemas/_index.yaml +++ b/api-contracts/openapi/components/schemas/_index.yaml @@ -64,6 +64,8 @@ QueueMetrics: $ref: "./tenant.yaml#/QueueMetrics" TenantQueueMetrics: $ref: "./tenant.yaml#/TenantQueueMetrics" +TenantStepRunQueueMetrics: + $ref: "./tenant.yaml#/TenantStepRunQueueMetrics" AcceptInviteRequest: $ref: "./user.yaml#/AcceptInviteRequest" RejectInviteRequest: diff --git a/api-contracts/openapi/components/schemas/tenant.yaml b/api-contracts/openapi/components/schemas/tenant.yaml index f2a65446e..46acbaa11 100644 --- a/api-contracts/openapi/components/schemas/tenant.yaml +++ b/api-contracts/openapi/components/schemas/tenant.yaml @@ -335,3 +335,10 @@ TenantQueueMetrics: type: object additionalProperties: type: integer + +TenantStepRunQueueMetrics: + properties: + queues: + type: object + additionalProperties: + type: integer diff --git a/api-contracts/openapi/openapi.yaml b/api-contracts/openapi/openapi.yaml index 8ddf2a53b..cc4af51db 100644 --- a/api-contracts/openapi/openapi.yaml +++ b/api-contracts/openapi/openapi.yaml @@ -92,6 +92,8 @@ paths: $ref: "./paths/api-tokens/api_tokens.yaml#/revoke" /api/v1/tenants/{tenant}/queue-metrics: $ref: "./paths/tenant/tenant.yaml#/getQueueMetrics" + /api/v1/tenants/{tenant}/step-run-queue-metrics: + $ref: "./paths/tenant/tenant.yaml#/getStepRunQueueMetrics" /api/v1/tenants/{tenant}/events: $ref: "./paths/event/event.yaml#/withTenant" /api/v1/tenants/{tenant}/events/bulk: diff --git a/api-contracts/openapi/paths/tenant/tenant.yaml b/api-contracts/openapi/paths/tenant/tenant.yaml index 9b8701bec..ec2c85fb7 100644 --- a/api-contracts/openapi/paths/tenant/tenant.yaml +++ b/api-contracts/openapi/paths/tenant/tenant.yaml @@ -619,3 +619,47 @@ getQueueMetrics: summary: Get workflow metrics tags: - Workflow + +getStepRunQueueMetrics: + get: + x-resources: ["tenant"] + description: Get the queue metrics for the tenant + operationId: tenant:get:step-run-queue-metrics + parameters: + - description: The tenant id + in: path + name: tenant + required: true + schema: + type: string + format: uuid + minLength: 36 + maxLength: 36 + responses: + "200": + content: + application/json: + schema: + $ref: "../../components/schemas/_index.yaml#/TenantStepRunQueueMetrics" + description: Successfully retrieved the step run queue metrics + "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 + "404": + content: + application/json: + schema: + $ref: "../../components/schemas/_index.yaml#/APIErrors" + description: Not found + summary: Get step run metrics + tags: + - Tenant diff --git a/api/v1/server/handlers/tenants/get_step_run_queue_metrics.go b/api/v1/server/handlers/tenants/get_step_run_queue_metrics.go new file mode 100644 index 000000000..1f9516cc0 --- /dev/null +++ b/api/v1/server/handlers/tenants/get_step_run_queue_metrics.go @@ -0,0 +1,24 @@ +package tenants + +import ( + "github.com/labstack/echo/v4" + + "github.com/hatchet-dev/hatchet/api/v1/server/oas/gen" + "github.com/hatchet-dev/hatchet/pkg/repository/prisma/db" +) + +func (t *TenantService) TenantGetStepRunQueueMetrics(ctx echo.Context, request gen.TenantGetStepRunQueueMetricsRequestObject) (gen.TenantGetStepRunQueueMetricsResponseObject, error) { + tenant := ctx.Get("tenant").(*db.TenantModel) + + stepRunQueueCounts, err := t.config.EngineRepository.StepRun().GetQueueCounts(ctx.Request().Context(), tenant.ID) + + if err != nil { + return nil, err + } + + resp := gen.TenantStepRunQueueMetrics{ + Queues: &stepRunQueueCounts, + } + + return gen.TenantGetStepRunQueueMetrics200JSONResponse(resp), nil +} diff --git a/api/v1/server/oas/gen/openapi.gen.go b/api/v1/server/oas/gen/openapi.gen.go index e969c8741..c3c9a14dd 100644 --- a/api/v1/server/oas/gen/openapi.gen.go +++ b/api/v1/server/oas/gen/openapi.gen.go @@ -877,6 +877,11 @@ type TenantResourcePolicy struct { Limits []TenantResourceLimit `json:"limits"` } +// TenantStepRunQueueMetrics defines model for TenantStepRunQueueMetrics. +type TenantStepRunQueueMetrics struct { + Queues *map[string]int `json:"queues,omitempty"` +} + // TriggerWorkflowRunRequest defines model for TriggerWorkflowRunRequest. type TriggerWorkflowRunRequest struct { AdditionalMetadata *map[string]interface{} `json:"additionalMetadata,omitempty"` @@ -1736,6 +1741,9 @@ type ServerInterface interface { // Create SNS integration // (POST /api/v1/tenants/{tenant}/sns) SnsCreate(ctx echo.Context, tenant openapi_types.UUID) error + // Get step run metrics + // (GET /api/v1/tenants/{tenant}/step-run-queue-metrics) + TenantGetStepRunQueueMetrics(ctx echo.Context, tenant openapi_types.UUID) error // Get step run // (GET /api/v1/tenants/{tenant}/step-runs/{step-run}) StepRunGet(ctx echo.Context, tenant openapi_types.UUID, stepRun openapi_types.UUID) error @@ -2875,6 +2883,26 @@ func (w *ServerInterfaceWrapper) SnsCreate(ctx echo.Context) error { return err } +// TenantGetStepRunQueueMetrics converts echo context to params. +func (w *ServerInterfaceWrapper) TenantGetStepRunQueueMetrics(ctx echo.Context) error { + var err error + // ------------- Path parameter "tenant" ------------- + var tenant openapi_types.UUID + + err = runtime.BindStyledParameterWithLocation("simple", false, "tenant", runtime.ParamLocationPath, ctx.Param("tenant"), &tenant) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter tenant: %s", err)) + } + + ctx.Set(BearerAuthScopes, []string{}) + + ctx.Set(CookieAuthScopes, []string{}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.TenantGetStepRunQueueMetrics(ctx, tenant) + return err +} + // StepRunGet converts echo context to params. func (w *ServerInterfaceWrapper) StepRunGet(ctx echo.Context) error { var err error @@ -3904,6 +3932,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/api/v1/tenants/:tenant/slack/start", wrapper.UserUpdateSlackOauthStart) router.GET(baseURL+"/api/v1/tenants/:tenant/sns", wrapper.SnsList) router.POST(baseURL+"/api/v1/tenants/:tenant/sns", wrapper.SnsCreate) + router.GET(baseURL+"/api/v1/tenants/:tenant/step-run-queue-metrics", wrapper.TenantGetStepRunQueueMetrics) router.GET(baseURL+"/api/v1/tenants/:tenant/step-runs/:step-run", wrapper.StepRunGet) router.POST(baseURL+"/api/v1/tenants/:tenant/step-runs/:step-run/cancel", wrapper.StepRunUpdateCancel) router.POST(baseURL+"/api/v1/tenants/:tenant/step-runs/:step-run/rerun", wrapper.StepRunUpdateRerun) @@ -5477,6 +5506,50 @@ func (response SnsCreate405JSONResponse) VisitSnsCreateResponse(w http.ResponseW return json.NewEncoder(w).Encode(response) } +type TenantGetStepRunQueueMetricsRequestObject struct { + Tenant openapi_types.UUID `json:"tenant"` +} + +type TenantGetStepRunQueueMetricsResponseObject interface { + VisitTenantGetStepRunQueueMetricsResponse(w http.ResponseWriter) error +} + +type TenantGetStepRunQueueMetrics200JSONResponse TenantStepRunQueueMetrics + +func (response TenantGetStepRunQueueMetrics200JSONResponse) VisitTenantGetStepRunQueueMetricsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type TenantGetStepRunQueueMetrics400JSONResponse APIErrors + +func (response TenantGetStepRunQueueMetrics400JSONResponse) VisitTenantGetStepRunQueueMetricsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type TenantGetStepRunQueueMetrics403JSONResponse APIErrors + +func (response TenantGetStepRunQueueMetrics403JSONResponse) VisitTenantGetStepRunQueueMetricsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type TenantGetStepRunQueueMetrics404JSONResponse APIErrors + +func (response TenantGetStepRunQueueMetrics404JSONResponse) VisitTenantGetStepRunQueueMetricsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + type StepRunGetRequestObject struct { Tenant openapi_types.UUID `json:"tenant"` StepRun openapi_types.UUID `json:"step-run"` @@ -7123,6 +7196,8 @@ type StrictServerInterface interface { SnsCreate(ctx echo.Context, request SnsCreateRequestObject) (SnsCreateResponseObject, error) + TenantGetStepRunQueueMetrics(ctx echo.Context, request TenantGetStepRunQueueMetricsRequestObject) (TenantGetStepRunQueueMetricsResponseObject, error) + StepRunGet(ctx echo.Context, request StepRunGetRequestObject) (StepRunGetResponseObject, error) StepRunUpdateCancel(ctx echo.Context, request StepRunUpdateCancelRequestObject) (StepRunUpdateCancelResponseObject, error) @@ -8337,6 +8412,31 @@ func (sh *strictHandler) SnsCreate(ctx echo.Context, tenant openapi_types.UUID) return nil } +// TenantGetStepRunQueueMetrics operation middleware +func (sh *strictHandler) TenantGetStepRunQueueMetrics(ctx echo.Context, tenant openapi_types.UUID) error { + var request TenantGetStepRunQueueMetricsRequestObject + + request.Tenant = tenant + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.TenantGetStepRunQueueMetrics(ctx, request.(TenantGetStepRunQueueMetricsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "TenantGetStepRunQueueMetrics") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(TenantGetStepRunQueueMetricsResponseObject); ok { + return validResponse.VisitTenantGetStepRunQueueMetricsResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("Unexpected response type: %T", response) + } + return nil +} + // StepRunGet operation middleware func (sh *strictHandler) StepRunGet(ctx echo.Context, tenant openapi_types.UUID, stepRun openapi_types.UUID) error { var request StepRunGetRequestObject @@ -9425,184 +9525,185 @@ func (sh *strictHandler) WorkflowVersionGet(ctx echo.Context, workflow openapi_t var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/frontend/app/src/lib/api/generated/Api.ts b/frontend/app/src/lib/api/generated/Api.ts index 7e849bf65..8ecc19847 100644 --- a/frontend/app/src/lib/api/generated/Api.ts +++ b/frontend/app/src/lib/api/generated/Api.ts @@ -63,6 +63,7 @@ import { TenantMemberList, TenantQueueMetrics, TenantResourcePolicy, + TenantStepRunQueueMetrics, TriggerWorkflowRunRequest, UpdateTenantAlertEmailGroupRequest, UpdateTenantInviteRequest, @@ -823,6 +824,23 @@ export class Api extends HttpClient + this.request({ + path: `/api/v1/tenants/${tenant}/step-run-queue-metrics`, + method: 'GET', + secure: true, + format: 'json', + ...params, + }); /** * @description Lists all events for a tenant. * diff --git a/frontend/app/src/lib/api/generated/data-contracts.ts b/frontend/app/src/lib/api/generated/data-contracts.ts index e34640228..0a4f1bf6a 100644 --- a/frontend/app/src/lib/api/generated/data-contracts.ts +++ b/frontend/app/src/lib/api/generated/data-contracts.ts @@ -360,6 +360,10 @@ export interface TenantQueueMetrics { queues?: Record; } +export interface TenantStepRunQueueMetrics { + queues?: Record; +} + export interface AcceptInviteRequest { /** * @minLength 36 diff --git a/frontend/app/src/lib/api/queries.ts b/frontend/app/src/lib/api/queries.ts index 9bc4ce478..42b8953c2 100644 --- a/frontend/app/src/lib/api/queries.ts +++ b/frontend/app/src/lib/api/queries.ts @@ -214,6 +214,11 @@ export const queries = createQueryKeyStore({ queryKey: ['queue-metrics:get', tenant], queryFn: async () => (await api.tenantGetQueueMetrics(tenant)).data, }), + getStepRunQueueMetrics: (tenant: string) => ({ + queryKey: ['queue-metrics:get:step-run', tenant], + queryFn: async () => + (await api.tenantGetStepRunQueueMetrics(tenant)).data, + }), }, stepRuns: { get: (tenant: string, stepRun: string) => ({ diff --git a/frontend/app/src/pages/main/workflow-runs/components/workflow-runs-table.tsx b/frontend/app/src/pages/main/workflow-runs/components/workflow-runs-table.tsx index 7877f1d77..e0e406f0b 100644 --- a/frontend/app/src/pages/main/workflow-runs/components/workflow-runs-table.tsx +++ b/frontend/app/src/pages/main/workflow-runs/components/workflow-runs-table.tsx @@ -322,7 +322,7 @@ export function WorkflowRunsTable({ }); const tenantMetricsQuery = useQuery({ - ...queries.metrics.get(tenant.metadata.id), + ...queries.metrics.getStepRunQueueMetrics(tenant.metadata.id), refetchInterval, }); diff --git a/pkg/client/rest/gen.go b/pkg/client/rest/gen.go index 4523d7c0c..67d55e4fb 100644 --- a/pkg/client/rest/gen.go +++ b/pkg/client/rest/gen.go @@ -874,6 +874,11 @@ type TenantResourcePolicy struct { Limits []TenantResourceLimit `json:"limits"` } +// TenantStepRunQueueMetrics defines model for TenantStepRunQueueMetrics. +type TenantStepRunQueueMetrics struct { + Queues *map[string]int `json:"queues,omitempty"` +} + // TriggerWorkflowRunRequest defines model for TriggerWorkflowRunRequest. type TriggerWorkflowRunRequest struct { AdditionalMetadata *map[string]interface{} `json:"additionalMetadata,omitempty"` @@ -1828,6 +1833,9 @@ type ClientInterface interface { SnsCreate(ctx context.Context, tenant openapi_types.UUID, body SnsCreateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // TenantGetStepRunQueueMetrics request + TenantGetStepRunQueueMetrics(ctx context.Context, tenant openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) + // StepRunGet request StepRunGet(ctx context.Context, tenant openapi_types.UUID, stepRun openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -2624,6 +2632,18 @@ func (c *Client) SnsCreate(ctx context.Context, tenant openapi_types.UUID, body return c.Client.Do(req) } +func (c *Client) TenantGetStepRunQueueMetrics(ctx context.Context, tenant openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewTenantGetStepRunQueueMetricsRequest(c.Server, tenant) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) StepRunGet(ctx context.Context, tenant openapi_types.UUID, stepRun openapi_types.UUID, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewStepRunGetRequest(c.Server, tenant, stepRun) if err != nil { @@ -5298,6 +5318,40 @@ func NewSnsCreateRequestWithBody(server string, tenant openapi_types.UUID, conte return req, nil } +// NewTenantGetStepRunQueueMetricsRequest generates requests for TenantGetStepRunQueueMetrics +func NewTenantGetStepRunQueueMetricsRequest(server string, tenant openapi_types.UUID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "tenant", runtime.ParamLocationPath, tenant) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/tenants/%s/step-run-queue-metrics", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewStepRunGetRequest generates requests for StepRunGet func NewStepRunGetRequest(server string, tenant openapi_types.UUID, stepRun openapi_types.UUID) (*http.Request, error) { var err error @@ -7474,6 +7528,9 @@ type ClientWithResponsesInterface interface { SnsCreateWithResponse(ctx context.Context, tenant openapi_types.UUID, body SnsCreateJSONRequestBody, reqEditors ...RequestEditorFn) (*SnsCreateResponse, error) + // TenantGetStepRunQueueMetricsWithResponse request + TenantGetStepRunQueueMetricsWithResponse(ctx context.Context, tenant openapi_types.UUID, reqEditors ...RequestEditorFn) (*TenantGetStepRunQueueMetricsResponse, error) + // StepRunGetWithResponse request StepRunGetWithResponse(ctx context.Context, tenant openapi_types.UUID, stepRun openapi_types.UUID, reqEditors ...RequestEditorFn) (*StepRunGetResponse, error) @@ -8627,6 +8684,31 @@ func (r SnsCreateResponse) StatusCode() int { return 0 } +type TenantGetStepRunQueueMetricsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TenantStepRunQueueMetrics + JSON400 *APIErrors + JSON403 *APIErrors + JSON404 *APIErrors +} + +// Status returns HTTPResponse.Status +func (r TenantGetStepRunQueueMetricsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r TenantGetStepRunQueueMetricsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type StepRunGetResponse struct { Body []byte HTTPResponse *http.Response @@ -10088,6 +10170,15 @@ func (c *ClientWithResponses) SnsCreateWithResponse(ctx context.Context, tenant return ParseSnsCreateResponse(rsp) } +// TenantGetStepRunQueueMetricsWithResponse request returning *TenantGetStepRunQueueMetricsResponse +func (c *ClientWithResponses) TenantGetStepRunQueueMetricsWithResponse(ctx context.Context, tenant openapi_types.UUID, reqEditors ...RequestEditorFn) (*TenantGetStepRunQueueMetricsResponse, error) { + rsp, err := c.TenantGetStepRunQueueMetrics(ctx, tenant, reqEditors...) + if err != nil { + return nil, err + } + return ParseTenantGetStepRunQueueMetricsResponse(rsp) +} + // StepRunGetWithResponse request returning *StepRunGetResponse func (c *ClientWithResponses) StepRunGetWithResponse(ctx context.Context, tenant openapi_types.UUID, stepRun openapi_types.UUID, reqEditors ...RequestEditorFn) (*StepRunGetResponse, error) { rsp, err := c.StepRunGet(ctx, tenant, stepRun, reqEditors...) @@ -12191,6 +12282,53 @@ func ParseSnsCreateResponse(rsp *http.Response) (*SnsCreateResponse, error) { return response, nil } +// ParseTenantGetStepRunQueueMetricsResponse parses an HTTP response from a TenantGetStepRunQueueMetricsWithResponse call +func ParseTenantGetStepRunQueueMetricsResponse(rsp *http.Response) (*TenantGetStepRunQueueMetricsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &TenantGetStepRunQueueMetricsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TenantStepRunQueueMetrics + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest APIErrors + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest APIErrors + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest APIErrors + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + } + + return response, nil +} + // ParseStepRunGetResponse parses an HTTP response from a StepRunGetWithResponse call func ParseStepRunGetResponse(rsp *http.Response) (*StepRunGetResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body)