Repository: netty/netty
File: common/src/main/java/io/netty/util/concurrent/AutoScalingEventExecutorChooserFactory.java (Java)

Code before:
 * An {@code EventLoop} can be suspended if its utilization is consistently below the
 * {@code scaleDownThreshold}. Conversely, if the group's average utilization is consistently
 * above the {@code scaleUpThreshold}, a suspended thread will be automatically resumed to handle
 * the increased load.
 * <p>
 * To control the aggressiveness of scaling actions, the {@code maxRampUpStep} and {@code maxRampDownStep}
 * parameters limit the maximum number of threads that can be activated or suspended in a single scaling cycle.
 * Furthermore, to ensure decisions are based on sustained trends rather than transient spikes, the
 * {@code scalingPatienceCycles} defines how many consecutive monitoring windows a condition must be met
 * before a scaling action is triggered.
 */
public final class AutoScalingEventExecutorChooserFactory implements EventExecutorChooserFactory {

    /**
     * A container for the utilization metric of a single EventExecutor.
     * This object is intended to be created once and have its {@code utilization}
     * field updated periodically.
     */
    public static final class AutoScalingUtilizationMetric {
        final EventExecutor executor;
        private final AtomicLong utilizationBits = new AtomicLong();

        AutoScalingUtilizationMetric(EventExecutor executor) {
            this.executor = executor;
        }

        /**
         * Returns the most recently calculated utilization for the associated executor.
         * @return a value from 0.0 to 1.0.
         */
        public double utilization() {
            return Double.longBitsToDouble(utilizationBits.get());
        }

        void setUtilization(double utilization) {
            long bits = Double.doubleToRawLongBits(utilization);
            utilizationBits.lazySet(bits);
        }
    }

    private static final Runnable NO_OOP_TASK = () -> { };
    private final int minChildren;
    private final int maxChildren;
    private final long utilizationCheckPeriodNanos;
    private final double scaleDownThreshold;
    private final double scaleUpThreshold;
    private final int maxRampUpStep;
    private final int maxRampDownStep;
    private final int scalingPatienceCycles;

    /**

Review comment (suggestion):
I think we also need to make the executor accessible as otherwise the user does not know which executor has which utilization. 

```suggestion

        /**
         * Returns the {@link EventExecutor} this metric belongs too.
         * return the executor.
         */
        public EventExecutor executor() {
            return executor;
        }

```

Code after:
 * An {@code EventLoop} can be suspended if its utilization is consistently below the
 * {@code scaleDownThreshold}. Conversely, if the group's average utilization is consistently
 * above the {@code scaleUpThreshold}, a suspended thread will be automatically resumed to handle
 * the increased load.
 * <p>
 * To control the aggressiveness of scaling actions, the {@code maxRampUpStep} and {@code maxRampDownStep}
 * parameters limit the maximum number of threads that can be activated or suspended in a single scaling cycle.
 * Furthermore, to ensure decisions are based on sustained trends rather than transient spikes, the
 * {@code scalingPatienceCycles} defines how many consecutive monitoring windows a condition must be met
 * before a scaling action is triggered.
 */
public final class AutoScalingEventExecutorChooserFactory implements EventExecutorChooserFactory {

    /**
     * A container for the utilization metric of a single EventExecutor.
     * This object is intended to be created once and have its {@code utilization}
     * field updated periodically.
     */
    public static final class AutoScalingUtilizationMetric {
        private final EventExecutor executor;
        private final AtomicLong utilizationBits = new AtomicLong();

        AutoScalingUtilizationMetric(EventExecutor executor) {
            this.executor = executor;
        }

        /**
         * Returns the most recently calculated utilization for the associated executor.
         * @return a value from 0.0 to 1.0.
         */
        public double utilization() {
            return Double.longBitsToDouble(utilizationBits.get());
        }

        /**
         * Returns the {@link EventExecutor} this metric belongs too.
         * @return the executor.
         */
        public EventExecutor executor() {
            return executor;
        }

        void setUtilization(double utilization) {
            long bits = Double.doubleToRawLongBits(utilization);
            utilizationBits.lazySet(bits);
        }
    }

    private static final Runnable NO_OOP_TASK = () -> { };
    private final int minChildren;
    private final int maxChildren;