mirror of
https://github.com/dagu-org/dagu.git
synced 2025-12-28 06:34:22 +00:00
feat: improve queue ui (#1222)
This commit is contained in:
parent
cab88838b1
commit
a002aa41c7
@ -599,7 +599,7 @@ type Pagination struct {
|
||||
|
||||
// Queue A queue/process group with its active DAG-runs
|
||||
type Queue struct {
|
||||
// MaxConcurrency Maximum number of concurrent runs allowed. Only present for 'global' type queues
|
||||
// MaxConcurrency Maximum number of concurrent runs allowed. For 'global' queues, this is the configured maxConcurrency. For 'dag-based' queues, this is the DAG's maxActiveRuns (default 1)
|
||||
MaxConcurrency *int `json:"maxConcurrency,omitempty"`
|
||||
|
||||
// Name Name of the queue (global queue name or DAG name if no queue specified)
|
||||
@ -6144,92 +6144,92 @@ var swaggerSpec = []string{
|
||||
"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",
|
||||
"Xf1PAAAA//8sH3d//9wAAA==",
|
||||
"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",
|
||||
}
|
||||
|
||||
// GetSwagger returns the content of the embedded swagger specification file
|
||||
|
||||
@ -2368,7 +2368,7 @@ components:
|
||||
description: "Type of queue - 'global' if explicitly defined, 'dag-based' if using DAG name"
|
||||
maxConcurrency:
|
||||
type: integer
|
||||
description: "Maximum number of concurrent runs allowed. Only present for 'global' type queues"
|
||||
description: "Maximum number of concurrent runs allowed. For 'global' queues, this is the configured maxConcurrency. For 'dag-based' queues, this is the DAG's maxActiveRuns (default 1)"
|
||||
minimum: 1
|
||||
running:
|
||||
type: array
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/dagu-org/dagu/api/v2"
|
||||
"github.com/dagu-org/dagu/internal/config"
|
||||
"github.com/dagu-org/dagu/internal/digraph"
|
||||
"github.com/dagu-org/dagu/internal/digraph/status"
|
||||
)
|
||||
|
||||
@ -43,17 +44,28 @@ func (a *API) ListQueues(ctx context.Context, _ api.ListQueuesRequestObject) (ap
|
||||
|
||||
// Process running DAG runs
|
||||
for groupName, dagRuns := range runningByGroup {
|
||||
// Group name is the queue name
|
||||
queue := getOrCreateQueue(queueMap, groupName, a.config)
|
||||
var dag *digraph.DAG
|
||||
var queue *queueInfo
|
||||
|
||||
// Convert each running DAG run to DAGRunSummary
|
||||
for _, dagRun := range dagRuns {
|
||||
// Get the DAG run status to convert to summary
|
||||
// Get the DAG run attempt
|
||||
attempt, err := a.dagRunStore.FindAttempt(ctx, dagRun)
|
||||
if err != nil {
|
||||
continue // Skip if we can't find the attempt
|
||||
}
|
||||
|
||||
// Get the DAG from the attempt (only once for the group)
|
||||
if dag == nil {
|
||||
dag, _ = attempt.ReadDAG(ctx)
|
||||
}
|
||||
|
||||
// Get or create queue with the DAG info (only once for the group)
|
||||
if queue == nil {
|
||||
queue = getOrCreateQueue(queueMap, groupName, a.config, dag)
|
||||
}
|
||||
|
||||
// Get the status and add to queue
|
||||
runStatus, err := attempt.ReadStatus(ctx)
|
||||
if err != nil {
|
||||
continue // Skip if we can't read status
|
||||
@ -90,7 +102,7 @@ func (a *API) ListQueues(ctx context.Context, _ api.ListQueuesRequestObject) (ap
|
||||
queueName = dag.Name
|
||||
}
|
||||
|
||||
queue := getOrCreateQueue(queueMap, queueName, a.config)
|
||||
queue := getOrCreateQueue(queueMap, queueName, a.config, dag)
|
||||
|
||||
// Get the DAG run status to convert to summary
|
||||
attempt, err := a.dagRunStore.FindAttempt(ctx, dagRunRef)
|
||||
@ -121,8 +133,8 @@ func (a *API) ListQueues(ctx context.Context, _ api.ListQueuesRequestObject) (ap
|
||||
Queued: q.queued,
|
||||
}
|
||||
|
||||
// Only include maxConcurrency for global queues
|
||||
if q.queueType == "global" && q.maxConcurrency > 0 {
|
||||
// Include maxConcurrency for both global and DAG-based queues
|
||||
if q.maxConcurrency > 0 {
|
||||
queue.MaxConcurrency = &q.maxConcurrency
|
||||
totalCapacity += q.maxConcurrency
|
||||
}
|
||||
@ -161,7 +173,7 @@ type queueInfo struct {
|
||||
}
|
||||
|
||||
// Helper function to get or create queue in the map
|
||||
func getOrCreateQueue(queueMap map[string]*queueInfo, queueName string, config *config.Config) *queueInfo {
|
||||
func getOrCreateQueue(queueMap map[string]*queueInfo, queueName string, config *config.Config, dag *digraph.DAG) *queueInfo {
|
||||
queue, exists := queueMap[queueName]
|
||||
if !exists {
|
||||
queue = &queueInfo{
|
||||
@ -175,6 +187,9 @@ func getOrCreateQueue(queueMap map[string]*queueInfo, queueName string, config *
|
||||
if isGlobalQueue(queueName, config) {
|
||||
queue.queueType = "global"
|
||||
queue.maxConcurrency = getQueueMaxConcurrency(queueName, config)
|
||||
} else if dag != nil {
|
||||
// For DAG-based queues, use the DAG's MaxActiveRuns
|
||||
queue.maxConcurrency = dag.MaxActiveRuns
|
||||
}
|
||||
|
||||
queueMap[queueName] = queue
|
||||
|
||||
@ -479,15 +479,11 @@ function DAGActions({
|
||||
return;
|
||||
}
|
||||
|
||||
// Navigate to DAG-run detail page
|
||||
if (data?.dagRunId && dag?.name) {
|
||||
navigate(`/dag-runs/${dag.name}/${data.dagRunId}`);
|
||||
} else {
|
||||
reloadData();
|
||||
// Navigate to status tab after execution
|
||||
if (navigateToStatusTab) {
|
||||
navigateToStatusTab();
|
||||
}
|
||||
// Just refresh the current page data
|
||||
reloadData();
|
||||
// Navigate to status tab after execution (if available)
|
||||
if (navigateToStatusTab) {
|
||||
navigateToStatusTab();
|
||||
}
|
||||
}}
|
||||
dismissModal={() => {
|
||||
|
||||
@ -41,13 +41,7 @@ type Props = {
|
||||
/**
|
||||
* Modal dialog for starting or enqueuing a DAG with parameters
|
||||
*/
|
||||
function StartDAGModal({
|
||||
visible,
|
||||
dag,
|
||||
dismissModal,
|
||||
onSubmit,
|
||||
action = 'start',
|
||||
}: Props) {
|
||||
function StartDAGModal({ visible, dag, dismissModal, onSubmit }: Props) {
|
||||
const ref = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
// Parse default parameters from the DAG definition
|
||||
|
||||
@ -149,12 +149,12 @@ const columnHelper = createColumnHelper<Data>();
|
||||
function getTzAndExp(exp: string) {
|
||||
const parts = exp.trim().split(/\s+/);
|
||||
|
||||
if (parts[0]?.startsWith("CRON_TZ=")) {
|
||||
const timezone = parts[0]?.split("=")[1];
|
||||
const cronExpr = parts?.slice(1).join(" ");
|
||||
if (parts[0]?.startsWith('CRON_TZ=')) {
|
||||
const timezone = parts[0]?.split('=')[1];
|
||||
const cronExpr = parts?.slice(1).join(' ');
|
||||
return [timezone, cronExpr];
|
||||
} else {
|
||||
return [parts.join(" ")];
|
||||
return [parts.join(' ')];
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,13 +167,14 @@ function getNextSchedule(
|
||||
}
|
||||
try {
|
||||
const datesToRun = schedules.map((schedule) => {
|
||||
const parsedCronExp = getTzAndExp(schedule.expression)
|
||||
const parsedCronExp = getTzAndExp(schedule.expression);
|
||||
const options = {
|
||||
tz: (parsedCronExp.length > 1 ? parsedCronExp[0] : getConfig().tz),
|
||||
tz: parsedCronExp.length > 1 ? parsedCronExp[0] : getConfig().tz,
|
||||
iterator: true,
|
||||
};
|
||||
// Assuming 'parseExpression' is the correct method name based on library docs
|
||||
const cronExp = (parsedCronExp.length > 1 ? parsedCronExp[1] : parsedCronExp[0])
|
||||
const cronExp =
|
||||
parsedCronExp.length > 1 ? parsedCronExp[1] : parsedCronExp[0];
|
||||
const interval = cronParser.parse(cronExp!, options);
|
||||
return interval.next();
|
||||
});
|
||||
@ -316,11 +317,17 @@ const defaultColumns = [
|
||||
}
|
||||
if (data.kind === ItemKind.DAG) {
|
||||
const name = data.dag.dag.name.toLowerCase();
|
||||
const fileName = data.dag.fileName.toLowerCase();
|
||||
const description = (data.dag.dag.description || '').toLowerCase();
|
||||
const searchValue = String(filterValue).toLowerCase();
|
||||
|
||||
// Search in name and description
|
||||
if (name.includes(searchValue) || description.includes(searchValue)) {
|
||||
console.log({ data });
|
||||
if (
|
||||
fileName.includes(searchValue) ||
|
||||
name.includes(searchValue) ||
|
||||
description.includes(searchValue)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -395,10 +402,10 @@ const defaultColumns = [
|
||||
if (finishedAt && finishedAt !== '-') {
|
||||
const start = dayjs(startedAt);
|
||||
const end = dayjs(finishedAt);
|
||||
|
||||
|
||||
if (start.isValid() && end.isValid()) {
|
||||
const durationMs = end.diff(start);
|
||||
|
||||
|
||||
if (durationMs > 0) {
|
||||
// Format duration manually without using the custom format function
|
||||
const duration = dayjs.duration(durationMs);
|
||||
@ -406,15 +413,15 @@ const defaultColumns = [
|
||||
const hours = duration.hours();
|
||||
const minutes = duration.minutes();
|
||||
const seconds = duration.seconds();
|
||||
|
||||
|
||||
const parts: string[] = [];
|
||||
if (days > 0) parts.push(`${days}d`);
|
||||
if (hours > 0) parts.push(`${hours}h`);
|
||||
if (minutes > 0) parts.push(`${minutes}m`);
|
||||
if (seconds > 0 && parts.length === 0) parts.push(`${seconds}s`);
|
||||
|
||||
|
||||
const formattedDuration = parts.join(' ');
|
||||
|
||||
|
||||
durationContent = (
|
||||
<div className="text-[10px] text-muted-foreground">
|
||||
{formattedDuration}
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
import React from 'react';
|
||||
import { Layers, Play, Clock, BarChart3, Settings, GitBranch } from 'lucide-react';
|
||||
import { Layers, Play, Clock, BarChart3, Activity } from 'lucide-react';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '../../../components/ui/tooltip';
|
||||
|
||||
interface QueueMetricsProps {
|
||||
metrics: {
|
||||
totalQueues: number;
|
||||
globalQueues: number;
|
||||
dagBasedQueues: number;
|
||||
activeQueues: number;
|
||||
totalRunning: number;
|
||||
totalQueued: number;
|
||||
totalActive: number;
|
||||
@ -18,34 +23,40 @@ function QueueMetrics({ metrics, isLoading }: QueueMetricsProps) {
|
||||
// Define metric cards data
|
||||
const metricCards = [
|
||||
{
|
||||
title: 'Total Queues',
|
||||
value: metrics.totalQueues,
|
||||
icon: <Layers className="h-5 w-5 text-muted-foreground" />,
|
||||
},
|
||||
{
|
||||
title: 'Global',
|
||||
title: 'Global Queues',
|
||||
value: metrics.globalQueues,
|
||||
icon: <Settings className="h-5 w-5 text-blue-500" />,
|
||||
icon: <Layers className="h-5 w-5 text-blue-500" />,
|
||||
tooltip: 'Number of global queues (shared across multiple DAGs with maxConcurrency limits)',
|
||||
},
|
||||
{
|
||||
title: 'DAG-based',
|
||||
title: 'DAG-based Queues',
|
||||
value: metrics.dagBasedQueues,
|
||||
icon: <GitBranch className="h-5 w-5 text-gray-500" />,
|
||||
icon: <Layers className="h-5 w-5 text-gray-500" />,
|
||||
tooltip: 'Number of DAG-based queues (each DAG has its own queue with maxActiveRuns limit, default 1)',
|
||||
},
|
||||
{
|
||||
title: 'Active Queues',
|
||||
value: metrics.activeQueues,
|
||||
icon: <Activity className="h-5 w-5 text-green-500" />,
|
||||
tooltip: 'Number of queues currently with running or queued DAG runs',
|
||||
},
|
||||
{
|
||||
title: 'Running',
|
||||
value: metrics.totalRunning,
|
||||
icon: <Play className="h-5 w-5 text-green-500" />,
|
||||
tooltip: 'Total number of DAG runs currently executing across all queues',
|
||||
},
|
||||
{
|
||||
title: 'Queued',
|
||||
value: metrics.totalQueued,
|
||||
icon: <Clock className="h-5 w-5 text-purple-500" />,
|
||||
tooltip: 'Total number of DAG runs waiting to be executed across all queues',
|
||||
},
|
||||
{
|
||||
title: 'Utilization',
|
||||
value: `${metrics.utilization}%`,
|
||||
icon: <BarChart3 className="h-5 w-5 text-orange-500" />,
|
||||
tooltip: 'Percentage of global queue capacity being used (total running DAG runs ÷ global queue maxConcurrency)',
|
||||
},
|
||||
];
|
||||
|
||||
@ -54,29 +65,33 @@ function QueueMetrics({ metrics, isLoading }: QueueMetricsProps) {
|
||||
{/* Dense metrics */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 divide-x divide-y lg:divide-y-0">
|
||||
{metricCards.map((card) => (
|
||||
<div
|
||||
key={card.title}
|
||||
className="p-2 sm:p-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-1 sm:gap-2"
|
||||
>
|
||||
<div className="flex items-center gap-1 sm:gap-2">
|
||||
{React.cloneElement(card.icon, {
|
||||
className: card.icon.props.className.replace(
|
||||
'h-5 w-5',
|
||||
'h-3 w-3'
|
||||
),
|
||||
})}
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
{card.title}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-lg font-bold">
|
||||
{isLoading ? '-' : card.value}
|
||||
</span>
|
||||
</div>
|
||||
<Tooltip key={card.title}>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="p-2 sm:p-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-1 sm:gap-2 cursor-help">
|
||||
<div className="flex items-center gap-1 sm:gap-2">
|
||||
{React.cloneElement(card.icon, {
|
||||
className: card.icon.props.className.replace(
|
||||
'h-5 w-5',
|
||||
'h-3 w-3'
|
||||
),
|
||||
})}
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
{card.title}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-lg font-bold">
|
||||
{isLoading ? '-' : card.value}
|
||||
</span>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p className="max-w-xs">{card.tooltip}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default QueueMetrics;
|
||||
export default QueueMetrics;
|
||||
|
||||
@ -2,6 +2,11 @@ import React from 'react';
|
||||
import { Layers, Activity, Search, RefreshCw } from 'lucide-react';
|
||||
import { Input } from '../../components/ui/input';
|
||||
import { Button } from '../../components/ui/button';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '../../components/ui/tooltip';
|
||||
import { AppBarContext } from '../../contexts/AppBarContext';
|
||||
import { useQuery } from '../../hooks/api';
|
||||
import type { components } from '../../api/v2/schema';
|
||||
@ -62,28 +67,37 @@ function Queues() {
|
||||
filtered = filtered.filter((queue) => queue.type === selectedQueueType);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
// Sort alphabetically by queue name for stable display
|
||||
return filtered.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}, [data?.queues, searchText, selectedQueueType]);
|
||||
|
||||
// Calculate metrics
|
||||
const metrics = React.useMemo(() => {
|
||||
const queues = data?.queues || [];
|
||||
const totalQueues = queues.length;
|
||||
|
||||
// Count queues by type
|
||||
const globalQueues = queues.filter(q => q.type === 'global').length;
|
||||
const dagBasedQueues = queues.filter(q => q.type === 'dag-based').length;
|
||||
|
||||
// Count active queues (those with running or queued items)
|
||||
const activeQueues = queues.filter(q =>
|
||||
(q.running?.length || 0) > 0 || (q.queued?.length || 0) > 0
|
||||
).length;
|
||||
|
||||
const totalRunning = queues.reduce((sum, q) => sum + (q.running?.length || 0), 0);
|
||||
const totalQueued = queues.reduce((sum, q) => sum + (q.queued?.length || 0), 0);
|
||||
const totalActive = totalRunning + totalQueued;
|
||||
|
||||
// Calculate utilization for custom queues
|
||||
const globalQueuesWithCapacity = queues.filter(q => q.type === 'global' && q.maxConcurrency);
|
||||
const totalCapacity = globalQueuesWithCapacity.reduce((sum, q) => sum + (q.maxConcurrency || 0), 0);
|
||||
const utilization = totalCapacity > 0 ? Math.round((totalRunning / totalCapacity) * 100) : 0;
|
||||
// Calculate utilization for global queues only (DAG-based queues are isolated and don't compete for shared capacity)
|
||||
const globalQueuesList = queues.filter(q => q.type === 'global');
|
||||
const globalRunning = globalQueuesList.reduce((sum, q) => sum + (q.running?.length || 0), 0);
|
||||
const globalCapacity = globalQueuesList.filter(q => q.maxConcurrency).reduce((sum, q) => sum + (q.maxConcurrency || 0), 0);
|
||||
const utilization = globalCapacity > 0 ? Math.round((globalRunning / globalCapacity) * 100) : 0;
|
||||
|
||||
return {
|
||||
totalQueues,
|
||||
globalQueues,
|
||||
dagBasedQueues,
|
||||
activeQueues,
|
||||
totalRunning,
|
||||
totalQueued,
|
||||
totalActive,
|
||||
@ -193,14 +207,28 @@ function Queues() {
|
||||
<div className="w-2 h-2 rounded-full bg-purple-500" />
|
||||
<span>Queued</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2 h-2 rounded-full bg-blue-500" />
|
||||
<span>Global</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="w-2 h-2 rounded-full bg-gray-500" />
|
||||
<span>DAG-based</span>
|
||||
</div>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center gap-1 cursor-help">
|
||||
<div className="w-2 h-2 rounded-full bg-blue-500" />
|
||||
<span>Global</span>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p className="max-w-xs">Shared queues with maxConcurrency limits that can process DAG runs from multiple DAGs</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center gap-1 cursor-help">
|
||||
<div className="w-2 h-2 rounded-full bg-gray-500" />
|
||||
<span>DAG-based</span>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p className="max-w-xs">Dedicated queues where each DAG has its own queue with maxActiveRuns limit (default 1)</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user