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}