    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.util.EnumMap;
    020import java.util.Map;
    021import java.util.concurrent.TimeUnit;
    022import java.util.concurrent.atomic.AtomicReference;
    023
    024/**
    025 * <p>
    026 * A simple implementation of the <a
    027 * href="http://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> pattern
    028 * that counts specific events.
    029 * </p>
    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 {@code 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 {@code 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 * {@code 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 {@code 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 {@code 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 {@code 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 {@code 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 {@code 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        super();
    178        checkIntervalData = new AtomicReference<>(new CheckIntervalData(0, 0));
    179        this.openingThreshold = openingThreshold;
    180        this.openingInterval = openingUnit.toNanos(openingInterval);
    181        this.closingThreshold = closingThreshold;
    182        this.closingInterval = closingUnit.toNanos(closingInterval);
    183    }
    184
    185    /**
    186     * Creates a new instance of {@code EventCountCircuitBreaker} with the same interval for opening
    187     * and closing checks.
    188     *
    189     * @param openingThreshold the threshold for opening the circuit breaker; if this
    190     * number of events is received in the time span determined by the check interval, the
    191     * circuit breaker is opened
    192     * @param checkInterval the check interval for opening or closing the circuit breaker
    193     * @param checkUnit the {@code TimeUnit} defining the check interval
    194     * @param closingThreshold the threshold for closing the circuit breaker; if the
    195     * number of events received in the time span determined by the check interval goes
    196     * below this threshold, the circuit breaker is closed again
    197     */
    198    public EventCountCircuitBreaker(final int openingThreshold, final long checkInterval, final TimeUnit checkUnit,
    199                                    final int closingThreshold) {
    200        this(openingThreshold, checkInterval, checkUnit, closingThreshold, checkInterval,
    201                checkUnit);
    202    }
    203
    204    /**
    205     * Creates a new instance of {@code EventCountCircuitBreaker} which uses the same parameters for
    206     * opening and closing checks.
    207     *
    208     * @param threshold the threshold for changing the status of the circuit breaker; if
    209     * the number of events received in a check interval is greater than this value, the
    210     * circuit breaker is opened; if it is lower than this value, it is closed again
    211     * @param checkInterval the check interval for opening or closing the circuit breaker
    212     * @param checkUnit the {@code TimeUnit} defining the check interval
    213     */
    214    public EventCountCircuitBreaker(final int threshold, final long checkInterval, final TimeUnit checkUnit) {
    215        this(threshold, checkInterval, checkUnit, threshold);
    216    }
    217
    218    /**
    219     * Returns the threshold value for opening the circuit breaker. If this number of
    220     * events is received in the time span determined by the opening interval, the circuit
    221     * breaker is opened.
    222     *
    223     * @return the opening threshold
    224     */
    225    public int getOpeningThreshold() {
    226        return openingThreshold;
    227    }
    228
    229    /**
    230     * Returns the interval (in nanoseconds) for checking for the opening threshold.
    231     *
    232     * @return the opening check interval
    233     */
    234    public long getOpeningInterval() {
    235        return openingInterval;
    236    }
    237
    238    /**
    239     * Returns the threshold value for closing the circuit breaker. If the number of
    240     * events received in the time span determined by the closing interval goes below this
    241     * threshold, the circuit breaker is closed again.
    242     *
    243     * @return the closing threshold
    244     */
    245    public int getClosingThreshold() {
    246        return closingThreshold;
    247    }
    248
    249    /**
    250     * Returns the interval (in nanoseconds) for checking for the closing threshold.
    251     *
    252     * @return the opening check interval
    253     */
    254    public long getClosingInterval() {
    255        return closingInterval;
    256    }
    257
    258    /**
    259     * {@inheritDoc} This implementation checks the internal event counter against the
    260     * threshold values and the check intervals. This may cause a state change of this
    261     * circuit breaker.
    262     */
    263    @Override
    264    public boolean checkState() {
    265        return performStateCheck(0);
    266    }
    267
    268    /**
    269     * {@inheritDoc}
    270     */
    271    @Override
    272    public boolean incrementAndCheckState(final Integer increment) {
    273        return performStateCheck(increment);
    274    }
    275
    276    /**
    277     * Increments the monitored value by <strong>1</strong> and performs a check of the current state of this
    278     * circuit breaker. This method works like {@link #checkState()}, but the monitored
    279     * value is incremented before the state check is performed.
    280     *
    281     * @return <strong>true</strong> if the circuit breaker is now closed;
    282     * <strong>false</strong> otherwise
    283     */
    284    public boolean incrementAndCheckState() {
    285        return incrementAndCheckState(1);
    286    }
    287
    288    /**
    289     * {@inheritDoc} This circuit breaker may close itself again if the number of events
    290     * received during a check interval goes below the closing threshold. If this circuit
    291     * breaker is already open, this method has no effect, except that a new check
    292     * interval is started.
    293     */
    294    @Override
    295    public void open() {
    296        super.open();
    297        checkIntervalData.set(new CheckIntervalData(0, now()));
    298    }
    299
    300    /**
    301     * {@inheritDoc} A new check interval is started. If too many events are received in
    302     * this interval, the circuit breaker changes again to state open. If this circuit
    303     * breaker is already closed, this method has no effect, except that a new check
    304     * interval is started.
    305     */
    306    @Override
    307    public void close() {
    308        super.close();
    309        checkIntervalData.set(new CheckIntervalData(0, now()));
    310    }
    311
    312    /**
    313     * Actually checks the state of this circuit breaker and executes a state transition
    314     * if necessary.
    315     *
    316     * @param increment the increment for the internal counter
    317     * @return a flag whether the circuit breaker is now closed
    318     */
    319    private boolean performStateCheck(final int increment) {
    320        CheckIntervalData currentData;
    321        CheckIntervalData nextData;
    322        State currentState;
    323
    324        do {
    325            final long time = now();
    326            currentState = state.get();
    327            currentData = checkIntervalData.get();
    328            nextData = nextCheckIntervalData(increment, currentData, currentState, time);
    329        } while (!updateCheckIntervalData(currentData, nextData));
    330
    331        // This might cause a race condition if other changes happen in between!
    332        // Refer to the header comment!
    333        if (stateStrategy(currentState).isStateTransition(this, currentData, nextData)) {
    334            currentState = currentState.oppositeState();
    335            changeStateAndStartNewCheckInterval(currentState);
    336        }
    337        return !isOpen(currentState);
    338    }
    339
    340    /**
    341     * Updates the {@code CheckIntervalData} object. The current data object is replaced
    342     * by the one modified by the last check. The return value indicates whether this was
    343     * successful. If it is <strong>false</strong>, another thread interfered, and the
    344     * whole operation has to be redone.
    345     *
    346     * @param currentData the current check data object
    347     * @param nextData the replacing check data object
    348     * @return a flag whether the update was successful
    349     */
    350    private boolean updateCheckIntervalData(final CheckIntervalData currentData,
    351            final CheckIntervalData nextData) {
    352        return currentData == nextData
    353                || checkIntervalData.compareAndSet(currentData, nextData);
    354    }
    355
    356    /**
    357     * Changes the state of this circuit breaker and also initializes a new
    358     * {@code CheckIntervalData} object.
    359     *
    360     * @param newState the new state to be set
    361     */
    362    private void changeStateAndStartNewCheckInterval(final State newState) {
    363        changeState(newState);
    364        checkIntervalData.set(new CheckIntervalData(0, now()));
    365    }
    366
    367    /**
    368     * Calculates the next {@code CheckIntervalData} object based on the current data and
    369     * the current state. The next data object takes the counter increment and the current
    370     * time into account.
    371     *
    372     * @param increment the increment for the internal counter
    373     * @param currentData the current check data object
    374     * @param currentState the current state of the circuit breaker
    375     * @param time the current time
    376     * @return the updated {@code CheckIntervalData} object
    377     */
    378    private CheckIntervalData nextCheckIntervalData(final int increment,
    379            final CheckIntervalData currentData, final State currentState, final long time) {
    380        CheckIntervalData nextData;
    381        if (stateStrategy(currentState).isCheckIntervalFinished(this, currentData, time)) {
    382            nextData = new CheckIntervalData(increment, time);
    383        } else {
    384            nextData = currentData.increment(increment);
    385        }
    386        return nextData;
    387    }
    388
    389    /**
    390     * Returns the current time in nanoseconds. This method is used to obtain the current
    391     * time. This is needed to calculate the check intervals correctly.
    392     *
    393     * @return the current time in nanoseconds
    394     */
    395    long now() {
    396        return System.nanoTime();
    397    }
    398
    399    /**
    400     * Returns the {@code StateStrategy} object responsible for the given state.
    401     *
    402     * @param state the state
    403     * @return the corresponding {@code StateStrategy}
    404     * @throws CircuitBreakingException if the strategy cannot be resolved
    405     */
    406    private static StateStrategy stateStrategy(final State state) {
    407        return STRATEGY_MAP.get(state);
    408    }
    409
    410    /**
    411     * Creates the map with strategy objects. It allows access for a strategy for a given
    412     * state.
    413     *
    414     * @return the strategy map
    415     */
    416    private static Map<State, StateStrategy> createStrategyMap() {
    417        final Map<State, StateStrategy> map = new EnumMap<>(State.class);
    418        map.put(State.CLOSED, new StateStrategyClosed());
    419        map.put(State.OPEN, new StateStrategyOpen());
    420        return map;
    421    }
    422
    423    /**
    424     * An internally used data class holding information about the checks performed by
    425     * this class. Basically, the number of received events and the start time of the
    426     * current check interval are stored.
    427     */
    428    private static class CheckIntervalData {
    429        /** The counter for events. */
    430        private final int eventCount;
    431
    432        /** The start time of the current check interval. */
    433        private final long checkIntervalStart;
    434
    435        /**
    436         * Creates a new instance of {@code CheckIntervalData}.
    437         *
    438         * @param count the current count value
    439         * @param intervalStart the start time of the check interval
    440         */
    441        CheckIntervalData(final int count, final long intervalStart) {
    442            eventCount = count;
    443            checkIntervalStart = intervalStart;
    444        }
    445
    446        /**
    447         * Returns the event counter.
    448         *
    449         * @return the number of received events
    450         */
    451        public int getEventCount() {
    452            return eventCount;
    453        }
    454
    455        /**
    456         * Returns the start time of the current check interval.
    457         *
    458         * @return the check interval start time
    459         */
    460        public long getCheckIntervalStart() {
    461            return checkIntervalStart;
    462        }
    463
    464        /**
    465         * Returns a new instance of {@code CheckIntervalData} with the event counter
    466         * incremented by the given delta. If the delta is 0, this object is returned.
    467         *
    468         * @param delta the delta
    469         * @return the updated instance
    470         */
    471        public CheckIntervalData increment(final int delta) {
    472            return (delta == 0) ? this : new CheckIntervalData(getEventCount() + delta,
    473                    getCheckIntervalStart());
    474        }
    475    }
    476
    477    /**
    478     * Internally used class for executing check logic based on the current state of the
    479     * circuit breaker. Having this logic extracted into special classes avoids complex
    480     * if-then-else cascades.
    481     */
    482    private abstract static class StateStrategy {
    483        /**
    484         * Returns a flag whether the end of the current check interval is reached.
    485         *
    486         * @param breaker the {@code CircuitBreaker}
    487         * @param currentData the current state object
    488         * @param now the current time
    489         * @return a flag whether the end of the current check interval is reached
    490         */
    491        public boolean isCheckIntervalFinished(final EventCountCircuitBreaker breaker,
    492                final CheckIntervalData currentData, final long now) {
    493            return now - currentData.getCheckIntervalStart() > fetchCheckInterval(breaker);
    494        }
    495
    496        /**
    497         * Checks whether the specified {@code CheckIntervalData} objects indicate that a
    498         * state transition should occur. Here the logic which checks for thresholds
    499         * depending on the current state is implemented.
    500         *
    501         * @param breaker the {@code CircuitBreaker}
    502         * @param currentData the current {@code CheckIntervalData} object
    503         * @param nextData the updated {@code CheckIntervalData} object
    504         * @return a flag whether a state transition should be performed
    505         */
    506        public abstract boolean isStateTransition(EventCountCircuitBreaker breaker,
    507                CheckIntervalData currentData, CheckIntervalData nextData);
    508
    509        /**
    510         * Obtains the check interval to applied for the represented state from the given
    511         * {@code CircuitBreaker}.
    512         *
    513         * @param breaker the {@code CircuitBreaker}
    514         * @return the check interval to be applied
    515         */
    516        protected abstract long fetchCheckInterval(EventCountCircuitBreaker breaker);
    517    }
    518
    519    /**
    520     * A specialized {@code StateStrategy} implementation for the state closed.
    521     */
    522    private static class StateStrategyClosed extends StateStrategy {
    523
    524        /**
    525         * {@inheritDoc}
    526         */
    527        @Override
    528        public boolean isStateTransition(final EventCountCircuitBreaker breaker,
    529                final CheckIntervalData currentData, final CheckIntervalData nextData) {
    530            return nextData.getEventCount() > breaker.getOpeningThreshold();
    531        }
    532
    533        /**
    534         * {@inheritDoc}
    535         */
    536        @Override
    537        protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
    538            return breaker.getOpeningInterval();
    539        }
    540    }
    541
    542    /**
    543     * A specialized {@code StateStrategy} implementation for the state open.
    544     */
    545    private static class StateStrategyOpen extends StateStrategy {
    546        /**
    547         * {@inheritDoc}
    548         */
    549        @Override
    550        public boolean isStateTransition(final EventCountCircuitBreaker breaker,
    551                final CheckIntervalData currentData, final CheckIntervalData nextData) {
    552            return nextData.getCheckIntervalStart() != currentData
    553                    .getCheckIntervalStart()
    554                    && currentData.getEventCount() < breaker.getClosingThreshold();
    555        }
    556
    557        /**
    558         * {@inheritDoc}
    559         */
    560        @Override
    561        protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
    562            return breaker.getClosingInterval();
    563        }
    564    }
    565
    566}