1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 1998-2019 The OpenLDAP Foundation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
9 * Public License.
10 *
11 * A copy of this license is available in the file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
14 */
15
16 #include "portable.h"
17
18 #include "lload.h"
19
20 static LloadTierInit roundrobin_init;
21 static LloadTierBackendCb roundrobin_add_backend;
22 static LloadTierBackendCb roundrobin_remove_backend;
23 static LloadTierSelect roundrobin_select;
24
25 struct lload_tier_type roundrobin_tier;
26
27 static LloadTier *
roundrobin_init(void)28 roundrobin_init( void )
29 {
30 LloadTier *tier;
31
32 tier = ch_calloc( 1, sizeof(LloadTier) );
33
34 tier->t_type = roundrobin_tier;
35 ldap_pvt_thread_mutex_init( &tier->t_mutex );
36 LDAP_CIRCLEQ_INIT( &tier->t_backends );
37
38 return tier;
39 }
40
41 static int
roundrobin_add_backend(LloadTier * tier,LloadBackend * b)42 roundrobin_add_backend( LloadTier *tier, LloadBackend *b )
43 {
44 assert( b->b_tier == tier );
45 LDAP_CIRCLEQ_INSERT_TAIL( &tier->t_backends, b, b_next );
46 if ( !tier->t_private ) {
47 tier->t_private = b;
48 }
49 tier->t_nbackends++;
50 return LDAP_SUCCESS;
51 }
52
53 static int
roundrobin_remove_backend(LloadTier * tier,LloadBackend * b)54 roundrobin_remove_backend( LloadTier *tier, LloadBackend *b )
55 {
56 LloadBackend *next = LDAP_CIRCLEQ_LOOP_NEXT( &tier->t_backends, b, b_next );
57
58 assert_locked( &tier->t_mutex );
59 assert_locked( &b->b_mutex );
60
61 assert( b->b_tier == tier );
62
63 LDAP_CIRCLEQ_REMOVE( &tier->t_backends, b, b_next );
64 if ( b == tier->t_private ) {
65 if ( tier->t_nbackends ) {
66 tier->t_private = next;
67 } else {
68 assert( b == next );
69 tier->t_private = NULL;
70 }
71 }
72 tier->t_nbackends--;
73 return LDAP_SUCCESS;
74 }
75
76 static int
roundrobin_select(LloadTier * tier,LloadOperation * op,LloadConnection ** cp,int * res,char ** message)77 roundrobin_select(
78 LloadTier *tier,
79 LloadOperation *op,
80 LloadConnection **cp,
81 int *res,
82 char **message )
83 {
84 LloadBackend *b, *first, *next;
85 int rc = 0;
86
87 checked_lock( &tier->t_mutex );
88 first = b = tier->t_private;
89 checked_unlock( &tier->t_mutex );
90
91 if ( !first ) return rc;
92
93 do {
94 int result;
95
96 checked_lock( &b->b_mutex );
97 next = LDAP_CIRCLEQ_LOOP_NEXT( &tier->t_backends, b, b_next );
98
99 result = backend_select( b, op, cp, res, message );
100 checked_unlock( &b->b_mutex );
101
102 rc |= result;
103 if ( result && *cp ) {
104 /*
105 * Round-robin step:
106 * Rotate the queue to put this backend at the end. The race here
107 * is acceptable.
108 */
109 checked_lock( &tier->t_mutex );
110 tier->t_private = next;
111 checked_unlock( &tier->t_mutex );
112 return rc;
113 }
114
115 b = next;
116 } while ( b != first );
117
118 return rc;
119 }
120
121 struct lload_tier_type roundrobin_tier = {
122 .tier_name = "roundrobin",
123
124 .tier_init = roundrobin_init,
125 .tier_startup = tier_startup,
126 .tier_reset = tier_reset,
127 .tier_destroy = tier_destroy,
128
129 .tier_oc = BER_BVC("olcBkLloadTierConfig"),
130 .tier_backend_oc = BER_BVC("olcBkLloadBackendConfig"),
131
132 .tier_add_backend = roundrobin_add_backend,
133 .tier_remove_backend = roundrobin_remove_backend,
134
135 .tier_select = roundrobin_select,
136 };
137