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