    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.lang3.concurrent;
    018
    019import java.beans.PropertyChangeListener;
    020import java.util.EnumMap;
    021import java.util.Map;
    022import java.util.concurrent.TimeUnit;
    023import java.util.concurrent.atomic.AtomicReference;
    024
    025/**
    026 * A simple implementation of the <a
    027 * href="https://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> pattern
    028 * that counts specific events.
    029 *
    030 * <p>
    031 * A <em>circuit breaker</em> can be used to protect an application against unreliable
    032 * services or unexpected load. A newly created {@link EventCountCircuitBreaker} object is
    033 * initially in state <em>closed</em> meaning that no problem has been detected. When the
    034 * application encounters specific events (like errors or service timeouts), it tells the
    035 * circuit breaker to increment an internal counter. If the number of events reported in a
    036 * specific time interval exceeds a configurable threshold, the circuit breaker changes
    037 * into state <em>open</em>. This means that there is a problem with the associated sub
    038 * system; the application should no longer call it, but give it some time to settle down.
    039 * The circuit breaker can be configured to switch back to <em>closed</em> state after a
    040 * certain time frame if the number of events received goes below a threshold.
    041 * </p>
    042 * <p>
    043 * When a {@link EventCountCircuitBreaker} object is constructed the following parameters
    044 * can be provided:
    045 * </p>
    046 * <ul>
    047 * <li>A threshold for the number of events that causes a state transition to
    048 * <em>open</em> state. If more events are received in the configured check interval, the
    049 * circuit breaker switches to <em>open</em> state.</li>
    050 * <li>The interval for checks whether the circuit breaker should open. So it is possible
    051 * to specify something like "The circuit breaker should open if more than 10 errors are
    052 * encountered in a minute."</li>
    053 * <li>The same parameters can be specified for automatically closing the circuit breaker
    054 * again, as in "If the number of requests goes down to 100 per minute, the circuit
    055 * breaker should close itself again". Depending on the use case, it may make sense to use
    056 * a slightly lower threshold for closing the circuit breaker than for opening it to avoid
    057 * continuously flipping when the number of events received is close to the threshold.</li>
    058 * </ul>
    059 * <p>
    060 * This class supports the following typical use cases:
    061 * </p>
    062 * <p>
    063 * <strong>Protecting against load peaks</strong>
    064 * </p>
    065 * <p>
    066 * Imagine you have a server which can handle a certain number of requests per minute.
    067 * Suddenly, the number of requests increases significantly - maybe because a connected
    068 * partner system is going mad or due to a denial of service attack. A
    069 * {@link EventCountCircuitBreaker} can be configured to stop the application from
    070 * processing requests when a sudden peak load is detected and to start request processing
    071 * again when things calm down. The following code fragment shows a typical example of
    072 * such a scenario. Here the {@link EventCountCircuitBreaker} allows up to 1000 requests
    073 * per minute before it interferes. When the load goes down again to 800 requests per
    074 * second it switches back to state <em>closed</em>:
    075 * </p>
    076 *
    077 * <pre>
    078 * EventCountCircuitBreaker breaker = new EventCountCircuitBreaker(1000, 1, TimeUnit.MINUTE, 800);
    079 * ...
    080 * public void handleRequest(Request request) {
    081 *     if (breaker.incrementAndCheckState()) {
    082 *         // actually handle this request
    083 *     } else {
    084 *         // do something else, e.g. send an error code
    085 *     }
    086 * }
    087 * </pre>
    088 * <p>
    089 * <strong>Deal with an unreliable service</strong>
    090 * </p>
    091 * <p>
    092 * In this scenario, an application uses an external service which may fail from time to
    093 * time. If there are too many errors, the service is considered down and should not be
    094 * called for a while. This can be achieved using the following pattern - in this concrete
    095 * example we accept up to 5 errors in 2 minutes; if this limit is reached, the service is
    096 * given a rest time of 10 minutes:
    097 * </p>
    098 *
    099 * <pre>
    100 * EventCountCircuitBreaker breaker = new EventCountCircuitBreaker(5, 2, TimeUnit.MINUTE, 5, 10, TimeUnit.MINUTE);
    101 * ...
    102 * public void handleRequest(Request request) {
    103 *     if (breaker.checkState()) {
    104 *         try {
    105 *             service.doSomething();
    106 *         } catch (ServiceException ex) {
    107 *             breaker.incrementAndCheckState();
    108 *         }
    109 *     } else {
    110 *         // return an error code, use an alternative service, etc.
    111 *     }
    112 * }
    113 * </pre>
    114 * <p>
    115 * In addition to automatic state transitions, the state of a circuit breaker can be
    116 * changed manually using the methods {@link #open()} and {@link #close()}. It is also
    117 * possible to register {@link PropertyChangeListener} objects that get notified whenever
    118 * a state transition occurs. This is useful, for instance to directly react on a freshly
    119 * detected error condition.
    120 * </p>
    121 * <p>
    122 * <em>Implementation notes:</em>
    123 * </p>
    124 * <ul>
    125 * <li>This implementation uses non-blocking algorithms to update the internal counter and
    126 * state. This should be pretty efficient if there is not too much contention.</li>
    127 * <li>This implementation is not intended to operate as a high-precision timer in very
    128 * short check intervals. It is deliberately kept simple to avoid complex and
    129 * time-consuming state checks. It should work well in time intervals from a few seconds
    130 * up to minutes and longer. If the intervals become too short, there might be race
    131 * conditions causing spurious state transitions.</li>
    132 * <li>The handling of check intervals is a bit simplistic. Therefore, there is no
    133 * guarantee that the circuit breaker is triggered at a specific point in time; there may
    134 * be some delay (less than a check interval).</li>
    135 * </ul>
    136 * @since 3.5
    137 */
    138public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> {
    139
    140    /** A map for accessing the strategy objects for the different states. */
    141    private static final Map<State, StateStrategy> STRATEGY_MAP = createStrategyMap();
    142
    143    /** Stores information about the current check interval. */
    144    private final AtomicReference<CheckIntervalData> checkIntervalData;
    145
    146    /** The threshold for opening the circuit breaker. */
    147    private final int openingThreshold;
    148
    149    /** The time interval for opening the circuit breaker. */
    150    private final long openingInterval;
    151
    152    /** The threshold for closing the circuit breaker. */
    153    private final int closingThreshold;
    154
    155    /** The time interval for closing the circuit breaker. */
    156    private final long closingInterval;
    157
    158    /**
    159     * Creates a new instance of {@link EventCountCircuitBreaker} and initializes all properties for
    160     * opening and closing it based on threshold values for events occurring in specific
    161     * intervals.
    162     *
    163     * @param openingThreshold the threshold for opening the circuit breaker; if this
    164     * number of events is received in the time span determined by the opening interval,
    165     * the circuit breaker is opened
    166     * @param openingInterval the interval for opening the circuit breaker
    167     * @param openingUnit the {@link TimeUnit} defining the opening interval
    168     * @param closingThreshold the threshold for closing the circuit breaker; if the
    169     * number of events received in the time span determined by the closing interval goes
    170     * below this threshold, the circuit breaker is closed again
    171     * @param closingInterval the interval for closing the circuit breaker
    172     * @param closingUnit the {@link TimeUnit} defining the closing interval
    173     */
    174    public EventCountCircuitBreaker(final int openingThreshold, final long openingInterval,
    175                                    final TimeUnit openingUnit, final int closingThreshold, final long closingInterval,
    176                                    final TimeUnit closingUnit) {
    177        checkIntervalData = new AtomicReference<>(new CheckIntervalData(0, 0));
    178        this.openingThreshold = openingThreshold;
    179        this.openingInterval = openingUnit.toNanos(openingInterval);
    180        this.closingThreshold = closingThreshold;
    181        this.closingInterval = closingUnit.toNanos(closingInterval);
    182    }
    183
    184    /**
    185     * Creates a new instance of {@link EventCountCircuitBreaker} with the same interval for opening
    186     * and closing checks.
    187     *
    188     * @param openingThreshold the threshold for opening the circuit breaker; if this
    189     * number of events is received in the time span determined by the check interval, the
    190     * circuit breaker is opened
    191     * @param checkInterval the check interval for opening or closing the circuit breaker
    192     * @param checkUnit the {@link TimeUnit} defining the check interval
    193     * @param closingThreshold the threshold for closing the circuit breaker; if the
    194     * number of events received in the time span determined by the check interval goes
    195     * below this threshold, the circuit breaker is closed again
    196     */
    197    public EventCountCircuitBreaker(final int openingThreshold, final long checkInterval, final TimeUnit checkUnit,
    198                                    final int closingThreshold) {
    199        this(openingThreshold, checkInterval, checkUnit, closingThreshold, checkInterval,
    200                checkUnit);
    201    }
    202
    203    /**
    204     * Creates a new instance of {@link EventCountCircuitBreaker} which uses the same parameters for
    205     * opening and closing checks.
    206     *
    207     * @param threshold the threshold for changing the status of the circuit breaker; if
    208     * the number of events received in a check interval is greater than this value, the
    209     * circuit breaker is opened; if it is lower than this value, it is closed again
    210     * @param checkInterval the check interval for opening or closing the circuit breaker
    211     * @param checkUnit the {@link TimeUnit} defining the check interval
    212     */
    213    public EventCountCircuitBreaker(final int threshold, final long checkInterval, final TimeUnit checkUnit) {
    214        this(threshold, checkInterval, checkUnit, threshold);
    215    }
    216
    217    /**
    218     * Returns the threshold value for opening the circuit breaker. If this number of
    219     * events is received in the time span determined by the opening interval, the circuit
    220     * breaker is opened.
    221     *
    222     * @return the opening threshold
    223     */
    224    public int getOpeningThreshold() {
    225        return openingThreshold;
    226    }
    227
    228    /**
    229     * Returns the interval (in nanoseconds) for checking for the opening threshold.
    230     *
    231     * @return the opening check interval
    232     */
    233    public long getOpeningInterval() {
    234        return openingInterval;
    235    }
    236
    237    /**
    238     * Returns the threshold value for closing the circuit breaker. If the number of
    239     * events received in the time span determined by the closing interval goes below this
    240     * threshold, the circuit breaker is closed again.
    241     *
    242     * @return the closing threshold
    243     */
    244    public int getClosingThreshold() {
    245        return closingThreshold;
    246    }
    247
    248    /**
    249     * Returns the interval (in nanoseconds) for checking for the closing threshold.
    250     *
    251     * @return the opening check interval
    252     */
    253    public long getClosingInterval() {
    254        return closingInterval;
    255    }
    256
    257    /**
    258     * {@inheritDoc} This implementation checks the internal event counter against the
    259     * threshold values and the check intervals. This may cause a state change of this
    260     * circuit breaker.
    261     */
    262    @Override
    263    public boolean checkState() {
    264        return performStateCheck(0);
    265    }
    266
    267    /**
    268     * {@inheritDoc}
    269     */
    270    @Override
    271    public boolean incrementAndCheckState(final Integer increment) {
    272        return performStateCheck(increment);
    273    }
    274
    275    /**
    276     * Increments the monitored value by <strong>1</strong> and performs a check of the current state of this
    277     * circuit breaker. This method works like {@link #checkState()}, but the monitored
    278     * value is incremented before the state check is performed.
    279     *
    280     * @return <strong>true</strong> if the circuit breaker is now closed;
    281     * <strong>false</strong> otherwise
    282     */
    283    public boolean incrementAndCheckState() {
    284        return incrementAndCheckState(1);
    285    }
    286
    287    /**
    288     * {@inheritDoc} This circuit breaker may close itself again if the number of events
    289     * received during a check interval goes below the closing threshold. If this circuit
    290     * breaker is already open, this method has no effect, except that a new check
    291     * interval is started.
    292     */
    293    @Override
    294    public void open() {
    295        super.open();
    296        checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
    297    }
    298
    299    /**
    300     * {@inheritDoc} A new check interval is started. If too many events are received in
    301     * this interval, the circuit breaker changes again to state open. If this circuit
    302     * breaker is already closed, this method has no effect, except that a new check
    303     * interval is started.
    304     */
    305    @Override
    306    public void close() {
    307        super.close();
    308        checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
    309    }
    310
    311    /**
    312     * Actually checks the state of this circuit breaker and executes a state transition
    313     * if necessary.
    314     *
    315     * @param increment the increment for the internal counter
    316     * @return a flag whether the circuit breaker is now closed
    317     */
    318    private boolean performStateCheck(final int increment) {
    319        CheckIntervalData currentData;
    320        CheckIntervalData nextData;
    321        State currentState;
    322
    323        do {
    324            final long time = nanoTime();
    325            currentState = state.get();
    326            currentData = checkIntervalData.get();
    327            nextData = nextCheckIntervalData(increment, currentData, currentState, time);
    328        } while (!updateCheckIntervalData(currentData, nextData));
    329
    330        // This might cause a race condition if other changes happen in between!
    331        // Refer to the header comment!
    332        if (stateStrategy(currentState).isStateTransition(this, currentData, nextData)) {
    333            currentState = currentState.oppositeState();
    334            changeStateAndStartNewCheckInterval(currentState);
    335        }
    336        return !isOpen(currentState);
    337    }
    338
    339    /**
    340     * Updates the {@link CheckIntervalData} object. The current data object is replaced
    341     * by the one modified by the last check. The return value indicates whether this was
    342     * successful. If it is <strong>false</strong>, another thread interfered, and the
    343     * whole operation has to be redone.
    344     *
    345     * @param currentData the current check data object
    346     * @param nextData the replacing check data object
    347     * @return a flag whether the update was successful
    348     */
    349    private boolean updateCheckIntervalData(final CheckIntervalData currentData,
    350            final CheckIntervalData nextData) {
    351        return currentData == nextData
    352                || checkIntervalData.compareAndSet(currentData, nextData);
    353    }
    354
    355    /**
    356     * Changes the state of this circuit breaker and also initializes a new
    357     * {@link CheckIntervalData} object.
    358     *
    359     * @param newState the new state to be set
    360     */
    361    private void changeStateAndStartNewCheckInterval(final State newState) {
    362        changeState(newState);
    363        checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
    364    }
    365
    366    /**
    367     * Calculates the next {@link CheckIntervalData} object based on the current data and
    368     * the current state. The next data object takes the counter increment and the current
    369     * time into account.
    370     *
    371     * @param increment the increment for the internal counter
    372     * @param currentData the current check data object
    373     * @param currentState the current state of the circuit breaker
    374     * @param time the current time
    375     * @return the updated {@link CheckIntervalData} object
    376     */
    377    private CheckIntervalData nextCheckIntervalData(final int increment,
    378            final CheckIntervalData currentData, final State currentState, final long time) {
    379        final CheckIntervalData nextData;
    380        if (stateStrategy(currentState).isCheckIntervalFinished(this, currentData, time)) {
    381            nextData = new CheckIntervalData(increment, time);
    382        } else {
    383            nextData = currentData.increment(increment);
    384        }
    385        return nextData;
    386    }
    387
    388    /**
    389     * Returns the current time in nanoseconds. This method is used to obtain the current
    390     * time. This is needed to calculate the check intervals correctly.
    391     *
    392     * @return the current time in nanoseconds
    393     */
    394    long nanoTime() {
    395        return System.nanoTime();
    396    }
    397
    398    /**
    399     * Returns the {@link StateStrategy} object responsible for the given state.
    400     *
    401     * @param state the state
    402     * @return the corresponding {@link StateStrategy}
    403     * @throws CircuitBreakingException if the strategy cannot be resolved
    404     */
    405    private static StateStrategy stateStrategy(final State state) {
    406        return STRATEGY_MAP.get(state);
    407    }
    408
    409    /**
    410     * Creates the map with strategy objects. It allows access for a strategy for a given
    411     * state.
    412     *
    413     * @return the strategy map
    414     */
    415    private static Map<State, StateStrategy> createStrategyMap() {
    416        final Map<State, StateStrategy> map = new EnumMap<>(State.class);
    417        map.put(State.CLOSED, new StateStrategyClosed());
    418        map.put(State.OPEN, new StateStrategyOpen());
    419        return map;
    420    }
    421
    422    /**
    423     * An internally used data class holding information about the checks performed by
    424     * this class. Basically, the number of received events and the start time of the
    425     * current check interval are stored.
    426     */
    427    private static class CheckIntervalData {
    428        /** The counter for events. */
    429        private final int eventCount;
    430
    431        /** The start time of the current check interval. */
    432        private final long checkIntervalStart;
    433
    434        /**
    435         * Creates a new instance of {@link CheckIntervalData}.
    436         *
    437         * @param count the current count value
    438         * @param intervalStart the start time of the check interval
    439         */
    440        CheckIntervalData(final int count, final long intervalStart) {
    441            eventCount = count;
    442            checkIntervalStart = intervalStart;
    443        }
    444
    445        /**
    446         * Returns the event counter.
    447         *
    448         * @return the number of received events
    449         */
    450        public int getEventCount() {
    451            return eventCount;
    452        }
    453
    454        /**
    455         * Returns the start time of the current check interval.
    456         *
    457         * @return the check interval start time
    458         */
    459        public long getCheckIntervalStart() {
    460            return checkIntervalStart;
    461        }
    462
    463        /**
    464         * Returns a new instance of {@link CheckIntervalData} with the event counter
    465         * incremented by the given delta. If the delta is 0, this object is returned.
    466         *
    467         * @param delta the delta
    468         * @return the updated instance
    469         */
    470        public CheckIntervalData increment(final int delta) {
    471            return (delta == 0) ? this : new CheckIntervalData(getEventCount() + delta,
    472                    getCheckIntervalStart());
    473        }
    474    }
    475
    476    /**
    477     * Internally used class for executing check logic based on the current state of the
    478     * circuit breaker. Having this logic extracted into special classes avoids complex
    479     * if-then-else cascades.
    480     */
    481    private abstract static class StateStrategy {
    482        /**
    483         * Returns a flag whether the end of the current check interval is reached.
    484         *
    485         * @param breaker the {@link CircuitBreaker}
    486         * @param currentData the current state object
    487         * @param now the current time
    488         * @return a flag whether the end of the current check interval is reached
    489         */
    490        public boolean isCheckIntervalFinished(final EventCountCircuitBreaker breaker,
    491                final CheckIntervalData currentData, final long now) {
    492            return now - currentData.getCheckIntervalStart() > fetchCheckInterval(breaker);
    493        }
    494
    495        /**
    496         * Checks whether the specified {@link CheckIntervalData} objects indicate that a
    497         * state transition should occur. Here the logic which checks for thresholds
    498         * depending on the current state is implemented.
    499         *
    500         * @param breaker the {@link CircuitBreaker}
    501         * @param currentData the current {@link CheckIntervalData} object
    502         * @param nextData the updated {@link CheckIntervalData} object
    503         * @return a flag whether a state transition should be performed
    504         */
    505        public abstract boolean isStateTransition(EventCountCircuitBreaker breaker,
    506                CheckIntervalData currentData, CheckIntervalData nextData);
    507
    508        /**
    509         * Obtains the check interval to applied for the represented state from the given
    510         * {@link CircuitBreaker}.
    511         *
    512         * @param breaker the {@link CircuitBreaker}
    513         * @return the check interval to be applied
    514         */
    515        protected abstract long fetchCheckInterval(EventCountCircuitBreaker breaker);
    516    }
    517
    518    /**
    519     * A specialized {@link StateStrategy} implementation for the state closed.
    520     */
    521    private static class StateStrategyClosed extends StateStrategy {
    522
    523        /**
    524         * {@inheritDoc}
    525         */
    526        @Override
    527        public boolean isStateTransition(final EventCountCircuitBreaker breaker,
    528                final CheckIntervalData currentData, final CheckIntervalData nextData) {
    529            return nextData.getEventCount() > breaker.getOpeningThreshold();
    530        }
    531
    532        /**
    533         * {@inheritDoc}
    534         */
    535        @Override
    536        protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
    537            return breaker.getOpeningInterval();
    538        }
    539    }
    540
    541    /**
    542     * A specialized {@link StateStrategy} implementation for the state open.
    543     */
    544    private static class StateStrategyOpen extends StateStrategy {
    545        /**
    546         * {@inheritDoc}
    547         */
    548        @Override
    549        public boolean isStateTransition(final EventCountCircuitBreaker breaker,
    550                final CheckIntervalData currentData, final CheckIntervalData nextData) {
    551            return nextData.getCheckIntervalStart() != currentData
    552                    .getCheckIntervalStart()
    553                    && currentData.getEventCount() < breaker.getClosingThreshold();
    554        }
    555
    556        /**
    557         * {@inheritDoc}
    558         */
    559        @Override
    560        protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
    561            return breaker.getClosingInterval();
    562        }
    563    }
    564
    565}