fix: small UI changes for metrics view and remove worker status (#432)

* fix: small UI changes

* fix: remove all references to worker status
This commit is contained in:
abelanger5
2024-04-30 12:58:19 -04:00
committed by GitHub
parent 42f86a1c5b
commit 8768e889c1
18 changed files with 83 additions and 168 deletions
+16 -2
View File
@@ -1,6 +1,8 @@
package transformers
import (
"time"
"github.com/google/uuid"
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
@@ -22,11 +24,17 @@ func ToWorker(worker *db.WorkerModel) *gen.Worker {
maxRuns = runs
}
status := gen.ACTIVE
if lastHeartbeat, ok := worker.LastHeartbeatAt(); ok && lastHeartbeat.Add(4*time.Second).Before(time.Now()) {
status = gen.INACTIVE
}
res := &gen.Worker{
Metadata: *toAPIMetadata(worker.ID, worker.CreatedAt, worker.UpdatedAt),
Name: worker.Name,
DispatcherId: &dispatcherUuid,
Status: (*gen.WorkerStatus)(&worker.Status),
Status: &status,
MaxRuns: &maxRuns,
}
@@ -60,10 +68,16 @@ func ToWorkerSqlc(worker *dbsqlc.Worker, stepCount *int64) *gen.Worker {
maxRuns := int(worker.MaxRuns.Int32)
availableRuns := maxRuns - int(*stepCount)
status := gen.ACTIVE
if worker.LastHeartbeatAt.Time.Add(4 * time.Second).Before(time.Now()) {
status = gen.INACTIVE
}
res := &gen.Worker{
Metadata: *toAPIMetadata(pgUUIDToStr(worker.ID), worker.CreatedAt.Time, worker.UpdatedAt.Time),
Name: worker.Name,
Status: (*gen.WorkerStatus)(&worker.Status),
Status: &status,
DispatcherId: &dispatcherId,
MaxRuns: &maxRuns,
AvailableRuns: &availableRuns,
@@ -31,7 +31,7 @@ interface DataTableToolbarProps<TData> {
actions: JSX.Element[];
setSearch?: (search: string) => void;
search?: string;
canVisibility?: boolean;
showColumnToggle?: boolean;
}
export function DataTableToolbar<TData>({
@@ -40,7 +40,7 @@ export function DataTableToolbar<TData>({
actions,
setSearch,
search,
canVisibility,
showColumnToggle,
}: DataTableToolbarProps<TData>) {
const isFiltered = table.getState().columnFilters?.length > 0;
@@ -77,7 +77,7 @@ export function DataTableToolbar<TData>({
</div>
<div className="flex flex-row gap-4">
{actions && actions.length > 0 && actions}
{canVisibility && <DataTableViewOptions table={table} />}
{showColumnToggle && <DataTableViewOptions table={table} />}
</div>
</div>
);
@@ -57,6 +57,7 @@ interface DataTableProps<TData extends IDGetter, TValue> {
setPagination?: OnChangeFn<PaginationState>;
pageCount?: number;
onSetPageSize?: (pageSize: number) => void;
showColumnToggle?: boolean;
columnVisibility?: VisibilityState;
setColumnVisibility?: OnChangeFn<VisibilityState>;
rowSelection?: RowSelectionState;
@@ -98,6 +99,7 @@ export function DataTable<TData extends IDGetter, TValue>({
setPagination,
pageCount,
onSetPageSize,
showColumnToggle,
columnVisibility,
setColumnVisibility,
rowSelection,
@@ -247,7 +249,7 @@ export function DataTable<TData extends IDGetter, TValue>({
actions={actions}
search={search}
setSearch={setSearch}
canVisibility={!!columnVisibility}
showColumnToggle={showColumnToggle}
/>
)}
<div className={`rounded-md ${!card && 'border'}`}>
@@ -175,6 +175,7 @@ function WorkflowRunSummary({ event }: { event: Event }) {
'Triggered by': false,
actions: false,
}}
showColumnToggle={false}
isLoading={listWorkflowRunsQuery.isLoading}
/>
</div>
@@ -319,6 +319,7 @@ function EventsTable() {
options: workflowRunStatusFilters,
},
]}
showColumnToggle={true}
columnVisibility={columnVisibility}
setColumnVisibility={setColumnVisibility}
actions={actions}
@@ -27,7 +27,7 @@ export const isHealthy = (worker?: Worker) => {
}
if (worker.status !== 'ACTIVE') {
reasons.push('Worker is not active');
reasons.push('Worker has stopped heartbeating');
}
if (!worker.dispatcherId) {
@@ -36,13 +36,6 @@ export const isHealthy = (worker?: Worker) => {
if (!worker.lastHeartbeatAt) {
reasons.push('Worker has no heartbeat');
} else {
const beat = new Date(worker.lastHeartbeatAt).getTime();
const now = new Date().getTime();
if (now - beat > 6 * 1000) {
reasons.push('Worker has missed a heartbeat');
}
}
return reasons;
@@ -59,8 +52,8 @@ export const WorkerStatus = ({
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Badge variant={health.length === 0 ? 'successful' : 'failed'}>
{health.length === 0 ? 'Healthy' : 'Unhealthy'}
<Badge variant={status === 'ACTIVE' ? 'successful' : 'failed'}>
{status === 'ACTIVE' ? 'Active' : 'Inactive'}
</Badge>
</TooltipTrigger>
<TooltipContent>
@@ -70,11 +63,6 @@ export const WorkerStatus = ({
</TooltipContent>
</Tooltip>
</TooltipProvider>
<span className="py-0.5">
<Badge variant={status === 'ACTIVE' ? 'successful' : 'failed'}>
{status === 'ACTIVE' ? 'Active' : 'Inactive'}
</Badge>
</span>
</div>
);
@@ -118,7 +106,7 @@ export default function ExpandedWorkflowRun() {
Last seen {relativeDate(worker?.lastHeartbeatAt)} <br />
{(worker.maxRuns ?? 0) > 0
? `${worker.availableRuns} / ${worker.maxRuns ?? 0}`
: ''}{' '}
: '100'}{' '}
available run slots
</p>
<Separator className="my-4" />
@@ -107,7 +107,7 @@ export function WorkersTable() {
Last seen {relativeDate(data?.lastHeartbeatAt)} <br />
{(data.maxRuns ?? 0) > 0
? `${data.availableRuns} / ${data.maxRuns ?? 0}`
: ''}{' '}
: '100'}{' '}
available run slots
</p>
</div>
@@ -1,6 +1,7 @@
import React from 'react';
import { WorkflowRunsMetrics } from '@/lib/api';
import { Badge } from '@/components/ui/badge';
interface WorkflowRunsMetricsProps {
metrics: WorkflowRunsMetrics;
@@ -29,47 +30,37 @@ export const WorkflowRunsMetricsView: React.FC<WorkflowRunsMetricsProps> = ({
const queuedPercentage = calculatePercentage(counts?.QUEUED ?? 0, total);
return (
<div className="flex flex-row space-x-4 dark:text-white">
<div className="flex items-center">
<div className="w-12 h-12 bg-green-500 rounded-full flex items-center justify-center">
<span className="text-white text-sm font-bold">
{succeededPercentage}%
</span>
</div>
<p className="ml-2">Succeeded: {counts?.SUCCEEDED}</p>
</div>
<div className="flex items-center">
<div className="w-12 h-12 bg-green-300 rounded-full flex items-center justify-center">
<span className="text-white text-sm font-bold">
{runningPercentage}%
</span>
</div>
<p className="ml-2">Running: {counts?.RUNNING}</p>
</div>
<div className="flex items-center">
<div className="w-12 h-12 bg-red-500 rounded-full flex items-center justify-center">
<span className="text-white text-sm font-bold">
{failedPercentage}%
</span>
</div>
<p className="ml-2">Failed: {counts?.FAILED}</p>
</div>
<div className="flex items-center">
<div className="w-12 h-12 bg-gray-400 rounded-full flex items-center justify-center">
<span className="text-white text-sm font-bold">
{pendingPercentage}%
</span>
</div>
<p className="ml-2">Pending: {counts?.PENDING}</p>
</div>
<div className="flex items-center">
<div className="w-12 h-12 bg-gray-400 rounded-full flex items-center justify-center">
<span className="text-white text-sm font-bold">
{queuedPercentage}%
</span>
</div>
<p className="ml-2">Queued: {counts?.QUEUED}</p>
</div>
</div>
<dl className="flex flex-row justify-start gap-6">
<Badge
variant="successful"
className="cursor-default text-sm px-2 py-1 w-fit"
>
{counts?.SUCCEEDED} Succeeded ({succeededPercentage}%)
</Badge>
<Badge
variant="inProgress"
className="cursor-default text-sm px-2 py-1 w-fit"
>
{counts?.RUNNING} Running ({runningPercentage}%)
</Badge>
<Badge
variant="failed"
className="cursor-default text-sm px-2 py-1 w-fit"
>
{counts?.FAILED} Failed ({failedPercentage}%)
</Badge>
<Badge
variant="outline"
className="cursor-default rounded-sm font-normal text-sm px-2 py-1 w-fit"
>
{counts?.PENDING} Pending ({pendingPercentage}%)
</Badge>
<Badge
variant="outline"
className="cursor-default rounded-sm font-normal text-sm px-2 py-1 w-fit"
>
{counts?.QUEUED} Queued ({queuedPercentage}%)
</Badge>
</dl>
);
};
@@ -107,8 +107,6 @@ export function WorkflowRunsTable({
refetchInterval,
});
console.log(metricsQuery.data);
const {
data: workflowKeys,
isLoading: workflowKeysIsLoading,
@@ -318,48 +318,6 @@ func (ns NullVcsProvider) Value() (driver.Value, error) {
return string(ns.VcsProvider), nil
}
type WorkerStatus string
const (
WorkerStatusACTIVE WorkerStatus = "ACTIVE"
WorkerStatusINACTIVE WorkerStatus = "INACTIVE"
)
func (e *WorkerStatus) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = WorkerStatus(s)
case string:
*e = WorkerStatus(s)
default:
return fmt.Errorf("unsupported scan type for WorkerStatus: %T", src)
}
return nil
}
type NullWorkerStatus struct {
WorkerStatus WorkerStatus `json:"WorkerStatus"`
Valid bool `json:"valid"` // Valid is true if WorkerStatus is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullWorkerStatus) Scan(value interface{}) error {
if value == nil {
ns.WorkerStatus, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.WorkerStatus.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullWorkerStatus) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.WorkerStatus), nil
}
type WorkflowRunStatus string
const (
@@ -813,7 +771,6 @@ type Worker struct {
TenantId pgtype.UUID `json:"tenantId"`
LastHeartbeatAt pgtype.Timestamp `json:"lastHeartbeatAt"`
Name string `json:"name"`
Status WorkerStatus `json:"status"`
DispatcherId pgtype.UUID `json:"dispatcherId"`
MaxRuns pgtype.Int4 `json:"maxRuns"`
}
@@ -19,9 +19,6 @@ CREATE TYPE "TenantMemberRole" AS ENUM ('OWNER', 'ADMIN', 'MEMBER');
-- CreateEnum
CREATE TYPE "VcsProvider" AS ENUM ('GITHUB');
-- CreateEnum
CREATE TYPE "WorkerStatus" AS ENUM ('ACTIVE', 'INACTIVE');
-- CreateEnum
CREATE TYPE "WorkflowRunStatus" AS ENUM ('PENDING', 'RUNNING', 'SUCCEEDED', 'FAILED', 'QUEUED');
@@ -486,7 +483,6 @@ CREATE TABLE "Worker" (
"tenantId" UUID NOT NULL,
"lastHeartbeatAt" TIMESTAMP(3),
"name" TEXT NOT NULL,
"status" "WorkerStatus" NOT NULL DEFAULT 'ACTIVE',
"dispatcherId" UUID,
"maxRuns" INTEGER,
@@ -51,7 +51,6 @@ INSERT INTO "Worker" (
"updatedAt",
"tenantId",
"name",
"status",
"dispatcherId",
"maxRuns"
) VALUES (
@@ -60,7 +59,6 @@ INSERT INTO "Worker" (
CURRENT_TIMESTAMP,
@tenantId::uuid,
@name::text,
'ACTIVE',
@dispatcherId::uuid,
sqlc.narg('maxRuns')::int
) RETURNING *;
@@ -79,7 +77,6 @@ UPDATE
"Worker"
SET
"updatedAt" = CURRENT_TIMESTAMP,
"status" = coalesce(sqlc.narg('status')::"WorkerStatus", "status"),
"dispatcherId" = coalesce(sqlc.narg('dispatcherId')::uuid, "dispatcherId"),
"maxRuns" = coalesce(sqlc.narg('maxRuns')::int, "maxRuns"),
"lastHeartbeatAt" = coalesce(sqlc.narg('lastHeartbeatAt')::timestamp, "lastHeartbeatAt")
@@ -18,7 +18,6 @@ INSERT INTO "Worker" (
"updatedAt",
"tenantId",
"name",
"status",
"dispatcherId",
"maxRuns"
) VALUES (
@@ -27,10 +26,9 @@ INSERT INTO "Worker" (
CURRENT_TIMESTAMP,
$1::uuid,
$2::text,
'ACTIVE',
$3::uuid,
$4::int
) RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, status, "dispatcherId", "maxRuns"
) RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns"
`
type CreateWorkerParams struct {
@@ -56,7 +54,6 @@ func (q *Queries) CreateWorker(ctx context.Context, db DBTX, arg CreateWorkerPar
&i.TenantId,
&i.LastHeartbeatAt,
&i.Name,
&i.Status,
&i.DispatcherId,
&i.MaxRuns,
)
@@ -90,7 +87,7 @@ DELETE FROM
"Worker"
WHERE
"id" = $1::uuid
RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, status, "dispatcherId", "maxRuns"
RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns"
`
func (q *Queries) DeleteWorker(ctx context.Context, db DBTX, id pgtype.UUID) (*Worker, error) {
@@ -104,7 +101,6 @@ func (q *Queries) DeleteWorker(ctx context.Context, db DBTX, id pgtype.UUID) (*W
&i.TenantId,
&i.LastHeartbeatAt,
&i.Name,
&i.Status,
&i.DispatcherId,
&i.MaxRuns,
)
@@ -185,7 +181,7 @@ func (q *Queries) LinkServicesToWorker(ctx context.Context, db DBTX, arg LinkSer
const listWorkersWithStepCount = `-- name: ListWorkersWithStepCount :many
SELECT
workers.id, workers."createdAt", workers."updatedAt", workers."deletedAt", workers."tenantId", workers."lastHeartbeatAt", workers.name, workers.status, workers."dispatcherId", workers."maxRuns",
workers.id, workers."createdAt", workers."updatedAt", workers."deletedAt", workers."tenantId", workers."lastHeartbeatAt", workers.name, workers."dispatcherId", workers."maxRuns",
COUNT(runs."id") FILTER (WHERE runs."status" = 'RUNNING') AS "runningStepRuns"
FROM
"Worker" workers
@@ -253,7 +249,6 @@ func (q *Queries) ListWorkersWithStepCount(ctx context.Context, db DBTX, arg Lis
&i.Worker.TenantId,
&i.Worker.LastHeartbeatAt,
&i.Worker.Name,
&i.Worker.Status,
&i.Worker.DispatcherId,
&i.Worker.MaxRuns,
&i.RunningStepRuns,
@@ -273,17 +268,15 @@ UPDATE
"Worker"
SET
"updatedAt" = CURRENT_TIMESTAMP,
"status" = coalesce($1::"WorkerStatus", "status"),
"dispatcherId" = coalesce($2::uuid, "dispatcherId"),
"maxRuns" = coalesce($3::int, "maxRuns"),
"lastHeartbeatAt" = coalesce($4::timestamp, "lastHeartbeatAt")
"dispatcherId" = coalesce($1::uuid, "dispatcherId"),
"maxRuns" = coalesce($2::int, "maxRuns"),
"lastHeartbeatAt" = coalesce($3::timestamp, "lastHeartbeatAt")
WHERE
"id" = $5::uuid
RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, status, "dispatcherId", "maxRuns"
"id" = $4::uuid
RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "lastHeartbeatAt", name, "dispatcherId", "maxRuns"
`
type UpdateWorkerParams struct {
Status NullWorkerStatus `json:"status"`
DispatcherId pgtype.UUID `json:"dispatcherId"`
MaxRuns pgtype.Int4 `json:"maxRuns"`
LastHeartbeatAt pgtype.Timestamp `json:"lastHeartbeatAt"`
@@ -292,7 +285,6 @@ type UpdateWorkerParams struct {
func (q *Queries) UpdateWorker(ctx context.Context, db DBTX, arg UpdateWorkerParams) (*Worker, error) {
row := db.QueryRow(ctx, updateWorker,
arg.Status,
arg.DispatcherId,
arg.MaxRuns,
arg.LastHeartbeatAt,
@@ -307,7 +299,6 @@ func (q *Queries) UpdateWorker(ctx context.Context, db DBTX, arg UpdateWorkerPar
&i.TenantId,
&i.LastHeartbeatAt,
&i.Name,
&i.Status,
&i.DispatcherId,
&i.MaxRuns,
)
-7
View File
@@ -271,13 +271,6 @@ func (w *workerEngineRepository) UpdateWorker(ctx context.Context, tenantId, wor
ID: sqlchelpers.UUIDFromStr(workerId),
}
if opts.Status != nil {
updateParams.Status = dbsqlc.NullWorkerStatus{
WorkerStatus: dbsqlc.WorkerStatus(*opts.Status),
Valid: true,
}
}
if opts.LastHeartbeatAt != nil {
updateParams.LastHeartbeatAt = sqlchelpers.TimestampFromTime(*opts.LastHeartbeatAt)
}
-3
View File
@@ -29,9 +29,6 @@ type UpdateWorkerOpts struct {
// The id of the dispatcher
DispatcherId *string `validate:"omitempty,uuid"`
// The status of the worker
Status *db.WorkerStatus
// When the last worker heartbeat was
LastHeartbeatAt *time.Time
+2 -16
View File
@@ -18,7 +18,6 @@ import (
"github.com/hatchet-dev/hatchet/internal/datautils"
"github.com/hatchet-dev/hatchet/internal/msgqueue"
"github.com/hatchet-dev/hatchet/internal/repository"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/dbsqlc"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/sqlchelpers"
"github.com/hatchet-dev/hatchet/internal/services/dispatcher/contracts"
@@ -681,21 +680,8 @@ func (s *DispatcherImpl) Unsubscribe(ctx context.Context, request *contracts.Wor
tenant := ctx.Value("tenant").(*dbsqlc.Tenant)
tenantId := sqlchelpers.UUIDToStr(tenant.ID)
// no matter what, remove the worker from the connection pool
defer s.workers.Delete(request.WorkerId)
inactive := db.WorkerStatusInactive
updateCtx, updateCtxCancel := context.WithTimeout(context.Background(), 5*time.Second)
defer updateCtxCancel()
_, err := s.repo.Worker().UpdateWorker(updateCtx, tenantId, request.WorkerId, &repository.UpdateWorkerOpts{
Status: &inactive,
})
if err != nil {
s.l.Error().Err(err).Msgf("could not update worker %s status to inactive", request.WorkerId)
}
// remove the worker from the connection pool
s.workers.Delete(request.WorkerId)
return &contracts.WorkerUnsubscribeResponse{
TenantId: tenantId,
@@ -0,0 +1,11 @@
/*
Warnings:
- You are about to drop the column `status` on the `Worker` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "Worker" DROP COLUMN "status";
-- DropEnum
DROP TYPE "WorkerStatus";
-8
View File
@@ -1036,11 +1036,6 @@ model Ticker {
groupKeyRuns GetGroupKeyRun[]
}
enum WorkerStatus {
ACTIVE
INACTIVE
}
model Worker {
// base fields
id String @id @unique @default(uuid()) @db.Uuid
@@ -1058,9 +1053,6 @@ model Worker {
// the worker name
name String
// the worker's status
status WorkerStatus @default(ACTIVE)
// the dispatcher the worker is connected to
dispatcher Dispatcher? @relation(fields: [dispatcherId], references: [id], onDelete: SetNull, onUpdate: Cascade)
dispatcherId String? @db.Uuid