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