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