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 long 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 long 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 long getEventCount() {
223            return eventCount;
224        }
225
226        /**
227         * Returns a new instance of {@link CheckIntervalData} with the event counter incremented by the given delta. If the delta is 0, this object is
228         * returned.
229         * <p>
230         * If the increment would overflow, the event counter saturates at {@link Long#MAX_VALUE}.
231         * </p>
232         *
233         * @param delta The delta
234         * @return The updated instance
235         */
236        public CheckIntervalData increment(final int delta) {
237            if (delta == 0) {
238                return this;
239            }
240            final long nextCount = getEventCount() + delta;
241            return new CheckIntervalData(nextCount < getEventCount() ? Long.MAX_VALUE : nextCount, getCheckIntervalStart());
242        }
243    }
244
245    /**
246     * A specialized {@link AbstractStateStrategy} implementation for the state closed.
247     */
248    private static final class StateStrategyClosed extends AbstractStateStrategy {
249
250        /**
251         * {@inheritDoc}
252         */
253        @Override
254        protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
255            return breaker.getOpeningInterval();
256        }
257
258        /**
259         * {@inheritDoc}
260         */
261        @Override
262        public boolean isStateTransition(final EventCountCircuitBreaker breaker,
263                final CheckIntervalData currentData, final CheckIntervalData nextData) {
264            return nextData.getEventCount() > breaker.getOpeningThreshold();
265        }
266    }
267
268    /**
269     * A specialized {@link AbstractStateStrategy} implementation for the state open.
270     */
271    private static final class StateStrategyOpen extends AbstractStateStrategy {
272
273        /**
274         * {@inheritDoc}
275         */
276        @Override
277        protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) {
278            return breaker.getClosingInterval();
279        }
280
281        /**
282         * {@inheritDoc}
283         */
284        @Override
285        public boolean isStateTransition(final EventCountCircuitBreaker breaker,
286                final CheckIntervalData currentData, final CheckIntervalData nextData) {
287            return nextData.getCheckIntervalStart() != currentData
288                    .getCheckIntervalStart()
289                    && currentData.getEventCount() < breaker.getClosingThreshold();
290        }
291    }
292
293    /** A map for accessing the strategy objects for the different states. */
294    private static final Map<State, AbstractStateStrategy> STRATEGY_MAP = createStrategyMap();
295
296    /**
297     * Creates the map with strategy objects. It allows access for a strategy for a given
298     * state.
299     *
300     * @return The strategy map
301     */
302    private static Map<State, AbstractStateStrategy> createStrategyMap() {
303        final Map<State, AbstractStateStrategy> map = new EnumMap<>(State.class);
304        map.put(State.CLOSED, new StateStrategyClosed());
305        map.put(State.OPEN, new StateStrategyOpen());
306        return map;
307    }
308
309    /**
310     * Returns the {@link AbstractStateStrategy} object responsible for the given state.
311     *
312     * @param state The state
313     * @return The corresponding {@link AbstractStateStrategy}
314     * @throws CircuitBreakingException Thrown if the strategy cannot be resolved.
315     */
316    private static AbstractStateStrategy stateStrategy(final State state) {
317        return STRATEGY_MAP.get(state);
318    }
319
320    /** Stores information about the current check interval. */
321    private final AtomicReference<CheckIntervalData> checkIntervalData;
322
323    /** The threshold for opening the circuit breaker. */
324    private final int openingThreshold;
325
326    /** The time interval for opening the circuit breaker. */
327    private final long openingInterval;
328
329    /** The threshold for closing the circuit breaker. */
330    private final int closingThreshold;
331
332    /** The time interval for closing the circuit breaker. */
333    private final long closingInterval;
334
335    /**
336     * Creates a new instance of {@link EventCountCircuitBreaker} which uses the same parameters for
337     * opening and closing checks.
338     *
339     * @param threshold The threshold for changing the status of the circuit breaker; if
340     * the number of events received in a check interval is greater than this value, the
341     * circuit breaker is opened; if it is lower than this value, it is closed again
342     * @param checkInterval The check interval for opening or closing the circuit breaker
343     * @param checkUnit The {@link TimeUnit} defining the check interval
344     */
345    public EventCountCircuitBreaker(final int threshold, final long checkInterval, final TimeUnit checkUnit) {
346        this(threshold, checkInterval, checkUnit, threshold);
347    }
348
349    /**
350     * Creates a new instance of {@link EventCountCircuitBreaker} with the same interval for opening
351     * and closing checks.
352     *
353     * @param openingThreshold The threshold for opening the circuit breaker; if this
354     * number of events is received in the time span determined by the check interval, the
355     * circuit breaker is opened
356     * @param checkInterval The check interval for opening or closing the circuit breaker
357     * @param checkUnit The {@link TimeUnit} defining the check interval
358     * @param closingThreshold The threshold for closing the circuit breaker; if the
359     * number of events received in the time span determined by the check interval goes
360     * below this threshold, the circuit breaker is closed again
361     */
362    public EventCountCircuitBreaker(final int openingThreshold, final long checkInterval, final TimeUnit checkUnit,
363                                    final int closingThreshold) {
364        this(openingThreshold, checkInterval, checkUnit, closingThreshold, checkInterval,
365                checkUnit);
366    }
367
368    /**
369     * Creates a new instance of {@link EventCountCircuitBreaker} and initializes all properties for
370     * opening and closing it based on threshold values for events occurring in specific
371     * intervals.
372     *
373     * @param openingThreshold The threshold for opening the circuit breaker; if this
374     * number of events is received in the time span determined by the opening interval,
375     * the circuit breaker is opened
376     * @param openingInterval The interval for opening the circuit breaker
377     * @param openingUnit The {@link TimeUnit} defining the opening interval
378     * @param closingThreshold The threshold for closing the circuit breaker; if the
379     * number of events received in the time span determined by the closing interval goes
380     * below this threshold, the circuit breaker is closed again
381     * @param closingInterval The interval for closing the circuit breaker
382     * @param closingUnit The {@link TimeUnit} defining the closing interval
383     */
384    public EventCountCircuitBreaker(final int openingThreshold, final long openingInterval,
385                                    final TimeUnit openingUnit, final int closingThreshold, final long closingInterval,
386                                    final TimeUnit closingUnit) {
387        checkIntervalData = new AtomicReference<>(new CheckIntervalData(0, 0));
388        this.openingThreshold = openingThreshold;
389        this.openingInterval = openingUnit.toNanos(openingInterval);
390        this.closingThreshold = closingThreshold;
391        this.closingInterval = closingUnit.toNanos(closingInterval);
392    }
393
394    /**
395     * Changes the state of this circuit breaker and also initializes a new
396     * {@link CheckIntervalData} object.
397     *
398     * @param newState The new state to be set
399     */
400    private void changeStateAndStartNewCheckInterval(final State newState) {
401        changeState(newState);
402        checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
403    }
404
405    /**
406     * {@inheritDoc}
407     * <p>
408     * This implementation checks the internal event counter against the
409     * threshold values and the check intervals. This may cause a state change of this
410     * circuit breaker.
411     * </p>
412     */
413    @Override
414    public boolean checkState() {
415        return performStateCheck(0);
416    }
417
418    /**
419     * {@inheritDoc}
420     * <p>
421     * A new check interval is started. If too many events are received in
422     * this interval, the circuit breaker changes again to state open. If this circuit
423     * breaker is already closed, this method has no effect, except that a new check
424     * interval is started.
425     * </p>
426     */
427    @Override
428    public void close() {
429        super.close();
430        checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
431    }
432
433    /**
434     * Gets the interval (in nanoseconds) for checking for the closing threshold.
435     *
436     * @return The opening check interval
437     */
438    public long getClosingInterval() {
439        return closingInterval;
440    }
441
442    /**
443     * Gets the threshold value for closing the circuit breaker. If the number of
444     * events received in the time span determined by the closing interval goes below this
445     * threshold, the circuit breaker is closed again.
446     *
447     * @return The closing threshold
448     */
449    public int getClosingThreshold() {
450        return closingThreshold;
451    }
452
453    /**
454     * Gets the interval (in nanoseconds) for checking for the opening threshold.
455     *
456     * @return The opening check interval
457     */
458    public long getOpeningInterval() {
459        return openingInterval;
460    }
461
462    /**
463     * Gets the threshold value for opening the circuit breaker. If this number of
464     * events is received in the time span determined by the opening interval, the circuit
465     * breaker is opened.
466     *
467     * @return The opening threshold
468     */
469    public int getOpeningThreshold() {
470        return openingThreshold;
471    }
472
473    /**
474     * Increments the monitored value by <strong>1</strong> and performs a check of the current state of this
475     * circuit breaker. This method works like {@link #checkState()}, but the monitored
476     * value is incremented before the state check is performed.
477     *
478     * @return <strong>true</strong> if the circuit breaker is now closed;
479     * <strong>false</strong> otherwise
480     */
481    public boolean incrementAndCheckState() {
482        return incrementAndCheckState(1);
483    }
484
485    /**
486     * {@inheritDoc}
487     *
488     * <p>
489     * The event count is a protective counter: negative increments are rejected, and an increment that would overflow
490     * {@link Long#MAX_VALUE} saturates the counter at {@link Long#MAX_VALUE} instead of wrapping negative. Saturation prevents
491     * an overloaded interval from causing an automatic close. The counter resets when a new check interval starts or the
492     * circuit breaker changes state.
493     * </p>
494     *
495     * @throws IllegalArgumentException Thrown if the increment is negative.
496     */
497    @Override
498    public boolean incrementAndCheckState(final Integer increment) {
499        if (increment.intValue() < 0) {
500            throw new IllegalArgumentException("Increment must not be negative: " + increment);
501        }
502        return performStateCheck(increment);
503    }
504
505    /**
506     * Returns the current time in nanoseconds. This method is used to obtain the current
507     * time. This is needed to calculate the check intervals correctly.
508     *
509     * @return The current time in nanoseconds
510     */
511    long nanoTime() {
512        return System.nanoTime();
513    }
514
515    /**
516     * Calculates the next {@link CheckIntervalData} object based on the current data and
517     * the current state. The next data object takes the counter increment and the current
518     * time into account.
519     *
520     * @param increment The increment for the internal counter
521     * @param currentData The current check data object
522     * @param currentState The current state of the circuit breaker
523     * @param time The current time
524     * @return The updated {@link CheckIntervalData} object
525     */
526    private CheckIntervalData nextCheckIntervalData(final int increment,
527            final CheckIntervalData currentData, final State currentState, final long time) {
528        final CheckIntervalData nextData;
529        if (stateStrategy(currentState).isCheckIntervalFinished(this, currentData, time)) {
530            nextData = new CheckIntervalData(increment, time);
531        } else {
532            nextData = currentData.increment(increment);
533        }
534        return nextData;
535    }
536
537    /**
538     * {@inheritDoc}
539     * <p>
540     * This circuit breaker may close itself again if the number of events
541     * received during a check interval goes below the closing threshold. If this circuit
542     * breaker is already open, this method has no effect, except that a new check
543     * interval is started.
544     * </p>
545     */
546    @Override
547    public void open() {
548        super.open();
549        checkIntervalData.set(new CheckIntervalData(0, nanoTime()));
550    }
551
552    /**
553     * Actually checks the state of this circuit breaker and executes a state transition
554     * if necessary.
555     *
556     * @param increment The increment for the internal counter
557     * @return A flag whether the circuit breaker is now closed
558     */
559    private boolean performStateCheck(final int increment) {
560        CheckIntervalData currentData;
561        CheckIntervalData nextData;
562        State currentState;
563
564        do {
565            final long time = nanoTime();
566            currentState = state.get();
567            currentData = checkIntervalData.get();
568            nextData = nextCheckIntervalData(increment, currentData, currentState, time);
569        } while (!updateCheckIntervalData(currentData, nextData));
570
571        // This might cause a race condition if other changes happen in between!
572        // Refer to the header comment!
573        if (stateStrategy(currentState).isStateTransition(this, currentData, nextData)) {
574            currentState = currentState.oppositeState();
575            changeStateAndStartNewCheckInterval(currentState);
576        }
577        return !isOpen(currentState);
578    }
579
580    /**
581     * Updates the {@link CheckIntervalData} object. The current data object is replaced
582     * by the one modified by the last check. The return value indicates whether this was
583     * successful. If it is <strong>false</strong>, another thread interfered, and the
584     * whole operation has to be redone.
585     *
586     * @param currentData The current check data object
587     * @param nextData The replacing check data object
588     * @return A flag whether the update was successful
589     */
590    private boolean updateCheckIntervalData(final CheckIntervalData currentData,
591            final CheckIntervalData nextData) {
592        return currentData == nextData
593                || checkIntervalData.compareAndSet(currentData, nextData);
594    }
595
596}