Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,26 @@
"name": "yarn.scheduler.capacity.schedule-asynchronously.enable",
"value": "true"
},
{
"name": "yarn.scheduler.capacity.minimum-user-limit-percent",
"value": "100"
},
{
"name": "yarn.scheduler.capacity.user-limit-factor",
"value": "1"
},
{
"name": "yarn.scheduler.capacity.maximum-am-resource-percent",
"value": "0.1"
},
{
"name": "yarn.scheduler.capacity.root.production.maximum-applications",
"value": "10000"
},
{
"name": "yarn.scheduler.capacity.root.production.disable_preemption",
"value": "true"
},
{
"name": "yarn.scheduler.capacity.root.marketing.test.auto-create-child-queue.enabled",
"value": "true"
Expand Down Expand Up @@ -343,6 +363,10 @@
{
"name": "yarn.scheduler.capacity.queue-mappings",
"value": "u:user1:root.default,u:user2:root.production.prod02"
},
{
"name": "yarn.scheduler.capacity.global-queue-max-application",
"value": "5000"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export default {
// Server-side render by default, to enable SPA mode set this to `false`
ssr: false,
appDirectory: "src/app",
basename: "/scheduler-ui",
basename: "/scheduler-ui/",
} satisfies Config;
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ async function enableMocking() {
// Start the worker
return worker.start({
onUnhandledRequest: 'bypass',
serviceWorker: {
url: '/scheduler-ui/mockServiceWorker.js',
},
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,27 @@ export const globalPropertyDefinitions: PropertyDescriptor[] = [
},
],
},
{
name: 'yarn.scheduler.capacity.global-queue-max-application',
displayName: 'Max Applications per Queue (Global)',
description:
'Global per-queue maximum applications. When set, each queue is capped at this flat value (no capacity scaling). When unset, per-queue limits are calculated from Maximum Applications scaled by queue capacity.',
type: 'number' as PropertyType,
category: 'application-limits' as PropertyCategory,
defaultValue: '',
required: false,
validationRules: [
{
type: 'custom',
message: 'Must be a positive integer or empty',
validator: (value: string) => {
if (!value.trim()) return true;
const num = parseFloat(value);
return !isNaN(num) && Number.isInteger(num) && num > 0;
},
},
],
},
{
name: 'yarn.scheduler.capacity.application.fail-fast',
displayName: 'Application Fail Fast',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@ import type {
PropertyType,
PropertyCondition,
PropertyEvaluationContext,
InheritanceResolverContext,
} from '~/types';
import { getCapacityType } from '~/utils/capacityUtils';
import {
getGlobalValue,
parentChainResolver,
globalOnlyResolver,
} from '~/utils/resolveInheritedValue';

const LEGACY_QUEUE_MODE_PROPERTY = 'yarn.scheduler.capacity.legacy-queue-mode.enabled';

Expand Down Expand Up @@ -182,6 +188,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: globalOnlyResolver,
validationRules: [
{
type: 'range',
Expand All @@ -201,6 +208,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: globalOnlyResolver,
validationRules: [
{
type: 'custom',
Expand All @@ -223,6 +231,19 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: ({ configData, stagedChanges }: InheritanceResolverContext) => {
// YARN checks global-queue-max-application first.
// Only when that is unset does it fall back to maximum-applications (scaled by capacity).
const perQueueGlobal = getGlobalValue('global-queue-max-application', configData, stagedChanges);
if (perQueueGlobal !== undefined) {
return { value: perQueueGlobal, source: 'global' };
}
const globalMax = getGlobalValue('maximum-applications', configData, stagedChanges);
if (globalMax !== undefined) {
return { value: globalMax, source: 'global', isScaled: true };
}
return null;
},
validationRules: [
{
type: 'custom',
Expand All @@ -245,6 +266,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: globalOnlyResolver,
validationRules: [
{
type: 'range',
Expand All @@ -266,6 +288,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
category: 'application-limits' as PropertyCategory,
defaultValue: '',
required: false,
inheritanceResolver: globalOnlyResolver,
validationRules: [
{
type: 'custom',
Expand Down Expand Up @@ -387,6 +410,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: parentChainResolver,
validationRules: [
{
type: 'custom',
Expand All @@ -404,6 +428,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: parentChainResolver,
validationRules: [
{
type: 'custom',
Expand All @@ -422,6 +447,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: parentChainResolver,
validationRules: [
{
type: 'custom',
Expand All @@ -444,6 +470,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: parentChainResolver,
validationRules: [
{
type: 'custom',
Expand All @@ -466,6 +493,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: parentChainResolver,
},
{
name: 'intra-queue-preemption.disable_preemption',
Expand All @@ -476,6 +504,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: parentChainResolver,
},
{
name: 'priority',
Expand Down Expand Up @@ -557,6 +586,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
defaultValue: '',
required: false,
templateSupport: true,
inheritanceResolver: parentChainResolver,
validationRules: [
{
type: 'custom',
Expand Down Expand Up @@ -584,6 +614,7 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
category: 'node-labels' as PropertyCategory,
defaultValue: '',
required: false,
inheritanceResolver: parentChainResolver,
validationRules: [
{
type: 'custom',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const createMockPropertyEditor = () => ({
handleFieldBlur: vi.fn(),
getFieldErrors: vi.fn(() => []),
getFieldWarnings: vi.fn(() => []),
getInheritanceInfo: vi.fn(() => null),
properties: [
{
name: 'capacity',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export const PropertyEditorTab = ({
getFieldErrors,
getFieldWarnings,
properties,
getInheritanceInfo,
} = usePropertyEditor({
queuePath: queue.queuePath,
});
Expand Down Expand Up @@ -443,6 +444,7 @@ export const PropertyEditorTab = ({
queueName={queue.queueName}
parentQueuePath={parentQueuePath}
currentValues={watchedValues}
inheritanceInfo={getInheritanceInfo(prop.originalName || prop.name)}
/>
{shouldRenderTemplateButton && (
<div className="pt-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
*/

import React from 'react';
import { Info, AlertTriangle } from 'lucide-react';
import { Info, AlertTriangle, ArrowDown, ArrowUp } from 'lucide-react';
import type { InheritedValueInfo } from '~/utils/resolveInheritedValue';
import { Badge } from '~/components/ui/badge';
import { FieldLabel, FieldMessage } from '~/components/ui/field';
import { Tooltip, TooltipContent, TooltipTrigger } from '~/components/ui/tooltip';
Expand Down Expand Up @@ -138,3 +139,56 @@ export const FieldErrorMessage: React.FC<FieldErrorMessageProps> = ({
}
return <FieldMessage>{error ? String(error.message ?? '') : inlineBusinessError}</FieldMessage>;
};

export interface InheritedValueIndicatorProps {
inheritanceInfo: InheritedValueInfo | null;
hasExplicitValue?: boolean;
}

export const InheritedValueIndicator: React.FC<InheritedValueIndicatorProps> = ({
inheritanceInfo,
hasExplicitValue = false,
}) => {
if (!inheritanceInfo) return null;

const sourceLabel =
inheritanceInfo.source === 'queue'
? inheritanceInfo.sourcePath
: 'global default';

const scaledSuffix = inheritanceInfo.isScaled ? ' (scaled by queue capacity)' : '';

if (hasExplicitValue) {
return (
<div
className={cn(
'mt-1 flex items-center gap-1.5 rounded-sm border-l-2 px-2 py-1 text-xs',
'border-l-amber-500 bg-amber-500/10 text-amber-700 dark:text-amber-400',
)}
>
<ArrowUp className="h-3 w-3 shrink-0" />
<span>
Overrides {sourceLabel}:{' '}
<span className="font-medium">{inheritanceInfo.value}</span>
{scaledSuffix}
</span>
</div>
);
}

return (
<div
className={cn(
'mt-1 flex items-center gap-1.5 rounded-sm border-l-2 px-2 py-1 text-xs',
'border-l-blue-500 bg-blue-500/10 text-blue-700 dark:text-blue-400',
)}
>
<ArrowDown className="h-3 w-3 shrink-0" />
<span>
<span className="font-medium">{inheritanceInfo.value}</span>
{' '}&mdash; inherited from {sourceLabel}
{scaledSuffix}
</span>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ import { FormField } from '~/components/ui/form';
import { Field, FieldControl, FieldDescription } from '~/components/ui/field';
import type { PropertyDescriptor } from '~/types/property-descriptor';
import { SPECIAL_VALUES } from '~/types';
import type { InheritedValueInfo } from '~/utils/resolveInheritedValue';
import { EnumPropertyField } from './EnumPropertyField';
import { CapacityPropertyField } from './CapacityPropertyField';
import {
PropertyLabel,
PropertyWarnings,
BusinessErrorsList,
FieldErrorMessage,
InheritedValueIndicator,
} from './PropertyFieldHelpers';
import { getCommonFieldClassName, parseFieldErrors } from '../utils/fieldHelpers';

Expand All @@ -57,6 +59,7 @@ interface PropertyFormFieldProps {
parentQueuePath?: string;
currentValues?: Partial<Record<string, string>>;
setFormValue?: UseFormSetValue<Record<string, string>>;
inheritanceInfo?: InheritedValueInfo | null;
}

export const PropertyFormField: React.FC<PropertyFormFieldProps> = ({
Expand All @@ -72,6 +75,7 @@ export const PropertyFormField: React.FC<PropertyFormFieldProps> = ({
parentQueuePath,
currentValues,
setFormValue: _setFormValue,
inheritanceInfo,
}) => {
void _setFormValue;

Expand Down Expand Up @@ -136,6 +140,9 @@ export const PropertyFormField: React.FC<PropertyFormFieldProps> = ({
)}
message={error ? String(error.message ?? '') : effectiveInlineError}
/>
<InheritedValueIndicator
inheritanceInfo={inheritanceInfo ?? null}
/>
<BusinessErrorsList fieldName={fieldName} messages={effectiveRemainingErrors} />
</>
);
Expand Down Expand Up @@ -175,6 +182,7 @@ export const PropertyFormField: React.FC<PropertyFormFieldProps> = ({
disabled={!isEnabled}
aria-invalid={Boolean(error)}
className={commonClassName}
placeholder={inheritanceInfo?.value || undefined}
/>
{property.displayFormat?.suffix && (
<span className="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-muted-foreground">
Expand All @@ -183,6 +191,10 @@ export const PropertyFormField: React.FC<PropertyFormFieldProps> = ({
)}
</div>
</FieldControl>
<InheritedValueIndicator
inheritanceInfo={inheritanceInfo ?? null}
hasExplicitValue={Boolean(field.value)}
/>
{property.description && (
<FieldDescription className="text-xs text-muted-foreground">
{property.description}
Expand Down Expand Up @@ -251,7 +263,7 @@ export const PropertyFormField: React.FC<PropertyFormFieldProps> = ({
onBlur?.(property.name, e.target.value);
}}
rows={2}
placeholder={property.defaultValue || undefined}
placeholder={inheritanceInfo?.value || property.defaultValue || undefined}
className={cn(
'flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
commonClassName,
Expand All @@ -268,7 +280,7 @@ export const PropertyFormField: React.FC<PropertyFormFieldProps> = ({
field.onBlur();
onBlur?.(property.name, e.target.value);
}}
placeholder={property.defaultValue || undefined}
placeholder={inheritanceInfo?.value || property.defaultValue || undefined}
disabled={!isEnabled}
aria-invalid={Boolean(error)}
className={commonClassName}
Expand All @@ -295,6 +307,10 @@ export const PropertyFormField: React.FC<PropertyFormFieldProps> = ({
)}
</div>
)}
<InheritedValueIndicator
inheritanceInfo={inheritanceInfo ?? null}
hasExplicitValue={Boolean(field.value)}
/>
{property.description && (
<FieldDescription className="text-xs text-muted-foreground">
{property.description}
Expand Down
Loading
Loading