Back to Hadoop

Extending Scheduler Properties

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-capacity-scheduler-ui/src/main/webapp/docs/development/extending-scheduler-properties.md

2.0.5-alpha-rc213.6 KB
Original Source
<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -->

Extending Scheduler Properties

This guide explains how to make new Capacity Scheduler properties editable in the UI and how to plug them into the validation system. Follow the relevant section depending on whether you are working with global scheduler settings or queue-level configuration.

Key modules

  • src/config/properties/global-properties.ts: property descriptors that drive the Global Settings page.
  • src/config/properties/queue-properties.ts: queue-level descriptors used by the property editor, queue dialogs, and staged changes.
  • src/config/schemas/validation.ts: shared Zod helpers for common formats (capacity values, ACLs, percentages, etc.).
  • src/config/validation-rules.ts: declarative business-validation rules evaluated for both global and queue properties.
  • src/contexts/ValidationContext.tsx: React provider that keeps validation issues in sync with staged edits.
  • src/features/validation/service.ts: utility entry points (validateField, validateQueue) used by hooks and slices.
  • src/features/validation/ruleCategories.ts: maps rule IDs to blocking/non-blocking behavior.
  • src/config/__tests__/propertyDefinitions.test.ts: regression tests that assert descriptor consistency.

Adding a global property

  1. Define the descriptor in src/config/properties/global-properties.ts.
    • Use the fully qualified key (for example, yarn.scheduler.capacity.maximum-applications).
    • Populate displayName, description, type, category, defaultValue, and required.
    • Use validationRules for field-level checks (range, pattern, or custom). Import helpers from src/config/schemas/validation.ts when possible so rules stay consistent.
    • Supply enumValues when type is enum. Each option should provide { value, label, description? }. Use enumDisplay to control the visual presentation: choiceCard for prominent cards or toggle for compact buttons (see below for guidance).
  • Use displayFormat to add user-friendly suffixes to numeric inputs.
  • Add conditional logic with showWhen / enableWhen when the property should only appear or be interactive under specific scheduler states. Each condition receives the merged configuration context (global + queue values, staged changes, scheduler metadata).

Available property categories (defined in src/types/property-descriptor.ts):

  • 'resource' - Resource allocation settings
  • 'scheduling' - Scheduling policies and behavior
  • 'security' - ACLs and permissions
  • 'core' - Core scheduler settings
  • 'application-limits' - Application count and resource limits
  • 'placement' - Placement rules and policies
  • 'container-allocation' - Container sizing and allocation
  • 'async-scheduling' - Asynchronous scheduling configuration
  • 'capacity' - Capacity values and modes
  • 'dynamic-queues' - Auto-created queue settings
  • 'node-labels' - Node label and partition configuration
  • 'preemption' - Preemption policies
ts
{
  name: 'yarn.scheduler.capacity.sample-property',
  displayName: 'Sample Property',
  description: 'What this property controls.',
  type: 'number',
  category: 'core',
  defaultValue: '0',
  required: false,
  validationRules: [
    { type: 'range', message: 'Must be between 0 and 10', min: 0, max: 10 },
  ],
},

Choosing the right enumDisplay variant

For enum type properties, you can control the visual presentation using the enumDisplay field:

  • choiceCard - Large bordered radio cards with labels, descriptions, and "Selected" badge. Best for 2-3 options where descriptions are important and visual prominence is desired.
ts
{
  name: 'yarn.scheduler.capacity.resource-calculator',
  type: 'enum',
  enumValues: [
    {
      value: 'org.apache.hadoop.yarn.util.resource.DefaultResourceCalculator',
      label: 'Default (Memory Only)',
      description: 'Memory-based calculator suitable for clusters without CPU enforcement.',
    },
    {
      value: 'org.apache.hadoop.yarn.util.resource.DominantResourceCalculator',
      label: 'Dominant Resource',
      description: 'Considers the dominant resource usage across memory and CPU.',
    },
  ],
  enumDisplay: 'choiceCard',  // Renders as large cards in a grid
}
  • toggle (default) - Compact toggle group (pill buttons) in horizontal layout. Space-efficient for 2-4 options with self-explanatory labels. This is the default when enumDisplay is omitted.
ts
{
  name: 'yarn.scheduler.capacity.queue-state',
  type: 'enum',
  enumValues: [
    { value: 'RUNNING', label: 'Running' },
    { value: 'STOPPED', label: 'Stopped' },
    { value: 'DRAINING', label: 'Draining' },
  ],
  // enumDisplay defaults to 'toggle' - no need to specify
}

Using displayFormat for numeric inputs

The displayFormat object adds visual hints and formatting to numeric input fields:

ts
export type DisplayFormat = {
  suffix?: string; // Text appended inside input (e.g., "(0.0-1.0)")
  prefix?: string; // Text prepended to input
  multiplier?: number; // Value multiplier for display
  decimals?: number; // Number of decimal places (affects step)
};

Example:

ts
{
  name: 'maximum-am-resource-percent',
  type: 'number',
  displayFormat: {
    suffix: ' (0.0-1.0)',  // Shows range hint inside the input
    decimals: 2,           // Allows 0.01 step increments
  },
}

The suffix renders as muted gray text positioned inside the right side of the input field, providing an inline hint about the expected range or format. The decimals value controls the input's step attribute (0.01 for 2 decimals, 0.001 for 3 decimals, etc.).

  1. Adjust the UI if needed. The global settings form renders inputs based on PropertyDescriptor.type. For bespoke widgets, extend src/features/global-settings/components/PropertyInput.tsx.
  2. Update tests. Extend src/config/__tests__/propertyDefinitions.test.ts if you need coverage for descriptor metadata.
  3. Verify by running the app or unit tests (npm run test) and confirming the new field renders with the expected validation feedback.

Using the useGlobalPropertyValidation hook

The useGlobalPropertyValidation hook provides validation for global-level properties. It's a simple wrapper around the validation context that uses the special GLOBAL_QUEUE_PATH identifier to validate properties at the scheduler level rather than the queue level.

API:

ts
const { validateGlobalProperty } = useGlobalPropertyValidation();
const issues = validateGlobalProperty(propertyKey, value);

Usage Example (from src/features/global-settings/components/GlobalSettings.tsx):

ts
import { useGlobalPropertyValidation } from '~/features/global-settings/hooks/useGlobalPropertyValidation';

const { validateGlobalProperty } = useGlobalPropertyValidation();

const handlePropertyChange = (propertyKey: string, value: string) => {
  const validationErrors = validateGlobalProperty(propertyKey, value);
  stageGlobalChange(propertyKey, value, validationErrors);
};

The hook returns a validateGlobalProperty function that takes a property key and value, and returns an array of ValidationIssue objects.

Adding a queue-level property

  1. Add a descriptor in src/config/properties/queue-properties.ts.
    • Queue descriptors use the short key (capacity, maximum-capacity, etc.).
  • Populate the same core fields as global descriptors. Add showWhen when the field should be hidden until prerequisites are met and enableWhen to keep the field visible but read-only. Both take arrays of predicates that receive the current queue/global context.
  • For enum properties, keep the { value, label, description? } shape and select an enumDisplay variant if the default toggle group is not ideal.
  • Set required: true if the field must be provided when adding new queues.
ts
{
  name: 'example-threshold',
  displayName: 'Example Threshold',
  description: 'Upper bound applied per queue.',
  type: 'number',
  category: 'application-limits',
  defaultValue: '',
  required: false,
  validationRules: [
    {
      type: 'custom',
      message: 'Must be zero or greater',
      validator: (value) => {
        if (!value.trim()) return true;
        const parsed = Number(value);
        return !Number.isNaN(parsed) && parsed >= 0;
      },
    },
  ],
},
  1. Wire dependent UI. Components such as PropertyFormField and PropertyEditorTab already read descriptors; only extend them if you need new interaction patterns.
  2. Tests. Update propertyDefinitions.test.ts or add targeted tests under src/features/property-editor / src/stores when the new field affects staged-change flows or reducers.

Working with validation

The validation pipeline has two layers that run automatically once descriptors and rules are defined.

Form-level checks

  • The validationRules array on a descriptor is compiled into Zod validators inside src/features/property-editor/hooks/usePropertyEditor.ts.
  • Reuse the helpers in src/config/schemas/validation.ts whenever possible. Create new helpers there if the same rule will be reused by multiple properties.
  • For global properties, useGlobalPropertyValidation invokes the same pipeline using the global queue path, so no extra wiring is required.

Declarative business rules

Cross-field and cross-queue logic lives in src/config/validation-rules.ts. To add or modify a rule:

  1. Declare the rule in the QUEUE_VALIDATION_RULES array (the name is historical; the same engine runs for global settings).
    ts
    {
      id: 'EXAMPLE_RULE',
      description: 'Describe the constraint',
      level: 'error',             // or 'warning'
      triggers: ['capacity'],     // fields that should cause this rule to re-run
      evaluate: (context) => {
        // context includes queuePath, fieldName, fieldValue, merged config, stagedChanges, etc.
        if (/* invalid */) {
          return [
            {
              queuePath: context.queuePath,
              field: 'capacity',
              message: 'Explain the problem.',
              severity: 'error',
              rule: 'example-rule',
            },
          ];
        }
        return [];
      },
    }
    
  2. Share utilities by adding helpers in src/features/validation/utils when the logic is complex.
  3. Categorize severity (optional). If the rule’s outcome should be treated as non-blocking despite returning severity: 'error', update src/features/validation/ruleCategories.ts so isBlockingError reflects the desired behavior.
  4. Surface to the UI. The ValidationContext automatically merges new rule output. Field-level components read ValidationIssue[] through useValidation, so no extra wiring is required beyond returning the correct rule ID and severity.
  5. Test it. Add unit tests near the rule implementation (for example, under src/config/__tests__ or a new *.test.ts beside the helper) and run npm run test.

Property condition utilities

The src/utils/propertyConditions.ts module provides two utility functions for evaluating showWhen and enableWhen conditions outside of the form rendering pipeline. These are useful when you need to programmatically check property visibility or enabled state:

shouldShowProperty(property, options)

Evaluates all showWhen conditions for a property. Returns true if the property should be visible.

ts
import { shouldShowProperty } from '~/utils/propertyConditions';

const visible = shouldShowProperty(property, {
  scope: 'global',
  property,
  propertyValue: currentValue,
  values: formValues,
  globalValues,
  stagedChanges,
  configData,
  schedulerInfo,
  // ... other context
});

isPropertyEnabled(property, options)

Evaluates all enableWhen conditions for a property. Returns true if the property should be interactive (not disabled).

ts
import { isPropertyEnabled } from '~/utils/propertyConditions';

const enabled = isPropertyEnabled(property, {
  scope: 'queue',
  queuePath: 'root.production',
  property,
  propertyValue: currentValue,
  // ... other context
});

Error handling: Both functions catch and log errors from condition evaluation, defaulting to true (show/enable) on failure to ensure the UI remains functional even with misconfigured conditions.

When to use these utilities:

  • In component logic that needs to conditionally render UI based on property visibility
  • When computing available properties for autocomplete or validation
  • For debugging or testing property condition behavior
  • Use inline predicates in property definitions when the condition is simple and self-contained

Sanity checklist

  • Descriptor added to the appropriate file with accurate metadata.
  • UI renders the expected input type (extend components only if necessary).
  • Form-level validationRules cover formatting and basic range checks.
  • Declarative rule added to src/config/validation-rules.ts (and helper utilities or categories updated when needed).
  • Tests updated or added, and npm run test completes successfully.