BUG-8445: check sessionId before propagating failures
[controller.git] / opendaylight / md-sal / cds-access-client / src / main / java / org / opendaylight / controller / cluster / access / client / AveragingProgressTracker.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.cluster.access.client;
10
11 import java.util.concurrent.TimeUnit;
12 import javax.annotation.concurrent.NotThreadSafe;
13
14 /**
15  * A ProgressTracker subclass which uses {@code ticksWorkedPerClosedTask} to compute delays.
16  *
17  * <p>This class has {@code tasksOpenLimit} used as a (weak) limit,
18  * as number of open tasks approaches that value, delays computed are increasing.
19  *
20  * <p>In order to keep {@code estimateIsolatedDelay} values from raising unreasonably high,
21  * {@code defaultTicksPerTask} acts as a maximal value. {@code openTask} may return
22  * higher value if there are tasks above the limit.
23  *
24  * <p>On the other hand, there is no delay when number of open tasks is half the limit or less,
25  * in order to prevent backend from running out of tasks while there may be waiting frontend threads.
26  *
27  * @author Vratko Polak
28  */
29 @NotThreadSafe
30 final class AveragingProgressTracker extends ProgressTracker {
31     private static final long DEFAULT_TICKS_PER_TASK = TimeUnit.MILLISECONDS.toNanos(500);
32
33     /**
34      * The implementation will avoid having more that this number of tasks open.
35      */
36     private final long tasksOpenLimit;
37
38     /**
39      * We do not delay tasks until their count hits this threshold.
40      */
41     private final long noDelayThreshold;
42
43     /**
44      * Create an idle tracker with limit and specified ticks per task value to use as default.
45      *
46      * @param limit of open tasks to avoid exceeding
47      * @param ticksPerTask value to use as default
48      */
49     private AveragingProgressTracker(final int limit, final long ticksPerTask) {
50         super(ticksPerTask);
51         tasksOpenLimit = limit;
52         noDelayThreshold = limit / 2;
53     }
54
55     /**
56      * Create a default idle tracker with given limit.
57      *
58      * @param limit of open tasks to avoid exceeding
59      */
60     AveragingProgressTracker(final int limit) {
61         this(limit, DEFAULT_TICKS_PER_TASK);
62     }
63
64     /**
65      * Create a copy of an existing tracker, all future tracking is fully independent.
66      *
67      * @param tracker the instance to copy state from
68      */
69     AveragingProgressTracker(final AveragingProgressTracker tracker) {
70         super(tracker);
71         this.tasksOpenLimit = tracker.tasksOpenLimit;
72         this.noDelayThreshold = tracker.noDelayThreshold;
73     }
74
75     // Public shared access (read-only) accessor-like methods
76
77     /**
78      * Give an estimate of a fair delay, assuming delays caused by other opened tasks are ignored.
79      *
80      * <p>This implementation returns zero delay if number of open tasks is half of limit or less.
81      * Else the delay is computed, aiming to keep number of open tasks at 3/4 of limit,
82      * assuming backend throughput remains constant.
83      *
84      * <p>As the number of open tasks approaches the limit,
85      * the computed delay increases, but it never exceeds defaultTicksPerTask.
86      * That means the actual number of open tasks can exceed the limit.
87      *
88      * @param now tick number corresponding to caller's present
89      * @return delay (in ticks) after which another openTask() would be fair to be called by the same thread again
90      */
91     @Override
92     public long estimateIsolatedDelay(final long now) {
93         final long open = tasksOpen();
94         if (open <= noDelayThreshold) {
95             return 0L;
96         }
97         if (open >= tasksOpenLimit) {
98             return defaultTicksPerTask();
99         }
100
101         /*
102          * Calculate the task capacity relative to the limit on open tasks. In real terms this value can be
103          * in the open interval (0.0, 0.5).
104          */
105         final double relativeRemainingCapacity = 1.0 - (((double) open) / tasksOpenLimit);
106
107         /*
108          * Calculate delay coefficient. It increases in inverse proportion to relative remaining capacity, approaching
109          * infinity as remaining capacity approaches 0.0.
110          */
111         final double delayCoefficient = (0.5 - relativeRemainingCapacity) / relativeRemainingCapacity;
112         final long delay = (long) (ticksWorkedPerClosedTask(now) * delayCoefficient);
113
114         /*
115          * Cap the result to defaultTicksPerTask, since the calculated delay may overstep it.
116          */
117         return Math.min(delay, defaultTicksPerTask());
118     }
119 }