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