001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2010 Sun Microsystems, Inc.
025 *      Portions copyright 2013 ForgeRock AS.
026 */
027
028package org.forgerock.opendj.ldap;
029
030import java.util.Collection;
031import java.util.concurrent.ScheduledExecutorService;
032import java.util.concurrent.TimeUnit;
033import java.util.concurrent.atomic.AtomicInteger;
034
035/**
036 * A round robin load balancing algorithm distributes connection requests across
037 * a list of connection factories one at a time. When the end of the list is
038 * reached, the algorithm starts again from the beginning.
039 * <p>
040 * This algorithm is typically used for load-balancing <i>within</i> data
041 * centers, where load must be distributed equally across multiple data sources.
042 * This algorithm contrasts with the {@link FailoverLoadBalancingAlgorithm}
043 * which is used for load-balancing <i>between</i> data centers.
044 * <p>
045 * If a problem occurs that temporarily prevents connections from being obtained
046 * for one of the connection factories, then this algorithm automatically
047 * "fails over" to the next operational connection factory in the list. If none
048 * of the connection factories are operational then a
049 * {@code ConnectionException} is returned to the client.
050 * <p>
051 * The implementation periodically attempts to connect to failed connection
052 * factories in order to determine if they have become available again.
053 *
054 * @see FailoverLoadBalancingAlgorithm
055 * @see Connections#newLoadBalancer(LoadBalancingAlgorithm)
056 */
057public final class RoundRobinLoadBalancingAlgorithm extends AbstractLoadBalancingAlgorithm {
058    private final int maxIndex;
059    private final AtomicInteger nextIndex = new AtomicInteger(-1);
060
061    /**
062     * Creates a new round robin load balancing algorithm which will monitor
063     * offline connection factories every 1 second using the default scheduler.
064     *
065     * @param factories
066     *            The ordered collection of connection factories.
067     */
068    public RoundRobinLoadBalancingAlgorithm(final Collection<? extends ConnectionFactory> factories) {
069        this(factories, null, 1, TimeUnit.SECONDS, null);
070    }
071
072    /**
073     * Creates a new round robin load balancing algorithm which will monitor
074     * offline connection factories every 1 second using the default scheduler.
075     *
076     * @param factories
077     *            The ordered collection of connection factories.
078     * @param listener
079     *            The event listener which should be notified whenever a
080     *            connection factory changes state from online to offline or
081     *            vice-versa.
082     */
083    public RoundRobinLoadBalancingAlgorithm(
084            final Collection<? extends ConnectionFactory> factories,
085            final LoadBalancerEventListener listener) {
086        this(factories, listener, 1, TimeUnit.SECONDS, null);
087    }
088
089    /**
090     * Creates a new round robin load balancing algorithm which will monitor
091     * offline connection factories using the specified frequency using the
092     * default scheduler.
093     *
094     * @param factories
095     *            The connection factories.
096     * @param listener
097     *            The event listener which should be notified whenever a
098     *            connection factory changes state from online to offline or
099     *            vice-versa.
100     * @param interval
101     *            The interval between attempts to poll offline factories.
102     * @param unit
103     *            The time unit for the interval between attempts to poll
104     *            offline factories.
105     */
106    public RoundRobinLoadBalancingAlgorithm(
107            final Collection<? extends ConnectionFactory> factories,
108            final LoadBalancerEventListener listener, final long interval, final TimeUnit unit) {
109        this(factories, null, interval, unit, null);
110    }
111
112    /**
113     * Creates a new round robin load balancing algorithm which will monitor
114     * offline connection factories using the specified frequency and scheduler.
115     *
116     * @param factories
117     *            The connection factories.
118     * @param listener
119     *            The event listener which should be notified whenever a
120     *            connection factory changes state from online to offline or
121     *            vice-versa.
122     * @param interval
123     *            The interval between attempts to poll offline factories.
124     * @param unit
125     *            The time unit for the interval between attempts to poll
126     *            offline factories.
127     * @param scheduler
128     *            The scheduler which should for periodically monitoring dead
129     *            connection factories to see if they are usable again.
130     */
131    public RoundRobinLoadBalancingAlgorithm(
132            final Collection<? extends ConnectionFactory> factories,
133            final LoadBalancerEventListener listener, final long interval, final TimeUnit unit,
134            final ScheduledExecutorService scheduler) {
135        super(factories, listener, interval, unit, scheduler);
136        this.maxIndex = factories.size();
137    }
138
139    /**
140     * Creates a new round robin load balancing algorithm which will monitor
141     * offline connection factories using the specified frequency using the
142     * default scheduler.
143     *
144     * @param factories
145     *            The connection factories.
146     * @param interval
147     *            The interval between attempts to poll offline factories.
148     * @param unit
149     *            The time unit for the interval between attempts to poll
150     *            offline factories.
151     */
152    public RoundRobinLoadBalancingAlgorithm(
153            final Collection<? extends ConnectionFactory> factories, final long interval,
154            final TimeUnit unit) {
155        this(factories, null, interval, unit, null);
156    }
157
158    /**
159     * Creates a new round robin load balancing algorithm which will monitor
160     * offline connection factories using the specified frequency and scheduler.
161     *
162     * @param factories
163     *            The connection factories.
164     * @param interval
165     *            The interval between attempts to poll offline factories.
166     * @param unit
167     *            The time unit for the interval between attempts to poll
168     *            offline factories.
169     * @param scheduler
170     *            The scheduler which should for periodically monitoring dead
171     *            connection factories to see if they are usable again.
172     */
173    public RoundRobinLoadBalancingAlgorithm(
174            final Collection<? extends ConnectionFactory> factories, final long interval,
175            final TimeUnit unit, final ScheduledExecutorService scheduler) {
176        this(factories, null, interval, unit, scheduler);
177    }
178
179    /** {@inheritDoc} */
180    @Override
181    String getAlgorithmName() {
182        return "RoundRobin";
183    }
184
185    /** {@inheritDoc} */
186    @Override
187    int getInitialConnectionFactoryIndex() {
188        // A round robin pool of one connection factories is unlikely in
189        // practice and requires special treatment.
190        if (maxIndex == 1) {
191            return 0;
192        }
193
194        // Determine the next factory to use: avoid blocking algorithm.
195        int oldNextIndex;
196        int newNextIndex;
197        do {
198            oldNextIndex = nextIndex.get();
199            newNextIndex = oldNextIndex + 1;
200            if (newNextIndex == maxIndex) {
201                newNextIndex = 0;
202            }
203        } while (!nextIndex.compareAndSet(oldNextIndex, newNextIndex));
204
205        // There's a potential, but benign, race condition here: other threads
206        // could jump in and rotate through the list before we return the
207        // connection factory.
208        return newNextIndex;
209    }
210
211}