2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.controller.cluster.access.client;
11 import java.util.concurrent.TimeUnit;
12 import javax.annotation.concurrent.NotThreadSafe;
15 * A ProgressTracker subclass which uses {@code ticksWorkedPerClosedTask} to compute delays.
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.
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.
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.
27 * @author Vratko Polak
30 final class AveragingProgressTracker extends ProgressTracker {
31 private static final long DEFAULT_TICKS_PER_TASK = TimeUnit.MILLISECONDS.toNanos(500);
34 * The implementation will avoid having more that this number of tasks open.
36 private final long tasksOpenLimit;
39 * We do not delay tasks until their count hits this threshold.
41 private final long noDelayThreshold;
44 * Create an idle tracker with limit and specified ticks per task value to use as default.
46 * @param limit of open tasks to avoid exceeding
47 * @param ticksPerTask value to use as default
49 private AveragingProgressTracker(final long limit, final long ticksPerTask) {
51 tasksOpenLimit = limit;
52 noDelayThreshold = limit / 2;
56 * Create a default idle tracker with given limit.
58 * @param limit of open tasks to avoid exceeding
60 AveragingProgressTracker(final long limit) {
61 this(limit, DEFAULT_TICKS_PER_TASK);
65 * Construct a new tracker suitable for a new task queue related to a "reconnect".
67 * <p>The limit is set independently of the old tracker.
69 * @param oldTracker the tracker used for the previously used backend
70 * @param limit of open tasks to avoid exceeding
71 * @param now tick number corresponding to caller's present
73 AveragingProgressTracker(final ProgressTracker oldTracker, final long limit, final long now) {
74 super(oldTracker, now);
75 tasksOpenLimit = limit;
76 noDelayThreshold = limit / 2;
80 * Construct a new tracker suitable for a new task queue related to a "reconnect".
82 * <p>The limit is copied from the old tracker.
84 * @param oldTracker the tracker used for the previously used backend
85 * @param now tick number corresponding to caller's present
87 AveragingProgressTracker(final AveragingProgressTracker oldTracker, final long now) {
88 this(oldTracker, oldTracker.tasksOpenLimit, now);
91 // Protected read-only methods
94 * Give an estimate of a fair delay, assuming delays caused by other opened tasks are ignored.
96 * <p>This implementation returns zero delay if number of open tasks is half of limit or less.
97 * Else the delay is computed, aiming to keep number of open tasks at 3/4 of limit,
98 * assuming backend throughput remains constant.
100 * <p>As the number of open tasks approaches the limit,
101 * the computed delay increases, but it never exceeds defaultTicksPerTask.
102 * That means the actual number of open tasks can exceed the limit.
104 * @param now tick number corresponding to caller's present
105 * @return delay (in ticks) after which another openTask() would be fair to be called by the same thread again
108 protected long estimateIsolatedDelay(final long now) {
109 final long open = tasksOpen();
110 if (open <= noDelayThreshold) {
113 if (open >= tasksOpenLimit) {
114 return defaultTicksPerTask();
118 * Calculate the task capacity relative to the limit on open tasks. In real terms this value can be
119 * in the open interval (0.0, 0.5).
121 final double relativeRemainingCapacity = 1.0 - (double) open / tasksOpenLimit;
124 * Calculate delay coefficient. It increases in inverse proportion to relative remaining capacity, approaching
125 * infinity as remaining capacity approaches 0.0.
127 final double delayCoefficient = (0.5 - relativeRemainingCapacity) / relativeRemainingCapacity;
128 final long delay = (long) (ticksWorkedPerClosedTask(now) * delayCoefficient);
131 * Cap the result to defaultTicksPerTask, since the calculated delay may overstep it.
133 return Math.min(delay, defaultTicksPerTask());