Hello Sergey,

There is an experimental module for nginx that adds fair upstream/proxy
balancing (note that this is a "third party" patch, not an optional
nginx module):

    http://wiki.codemongers.com/NginxHttpUpstreamFairModule

We've modified the nginx port to include the patch and added a knob for
enabling it. I wasn't sure if this was something that's acceptable to
include in the official nginx port but I've attached a patch in case
you're interested.

The patch should apply cleanly with:

    cd /usr/ports/www/nginx
    patch -p1 < nginx-port-upstream-fair.diff

I also have a git repo that tracks the nginx port tree with a branch for
the upstream_fair patch:

    http://github.com/rtomayko/nginx-port-upstream-fair/tree/master

If this is something that's acceptable for EXTRA_PATCHES, I'd be happy
to sign up for testing/maintaining the patch through new versions of nginx.

I apologize if I'm not correctly following any of the precepts for
submitting port change requests; I've seen different instructions. Some
say to submit a pr with a shar file and others say that most small
tweaks should be directed to the mailing list. Any direction here is
appreciated.

Thanks
--
Ryan Tomayko <[EMAIL PROTECTED]>

http://tomayko.com
http://lesscode.org

diff --git a/Makefile b/Makefile
index 988e18b..d61f985 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,7 @@ OPTIONS=      DEBUG "Enable nginx debugging" off \
                HTTP_ADDITION_MODULE "Enable http_addition module" off \
                HTTP_DAV_MODULE "Enable http_webdav module" off \
                HTTP_FLV_MODULE "Enable http_flv module" off \
+               HTTP_FAIR_MODULE "Enable upstream_fair module (alpha)" off \
                HTTP_PERL_MODULE "Enable http_perl module" off \
                HTTP_REALIP_MODULE "Enable http_realip module" off \
                HTTP_REWRITE_MODULE "Enable http_rewrite module" on \
@@ -78,6 +79,10 @@ CONFIGURE_ARGS+=--with-http_addition_module
 CONFIGURE_ARGS+=--with-http_dav_module
 .endif
 
+.if defined(WITH_HTTP_FAIR_MODULE)
+EXTRA_PATCHES= ${PATCHDIR}/extra-patch-upstream_fair.diff
+.endif
+
 .if defined(WITH_HTTP_FLV_MODULE)
 CONFIGURE_ARGS+=--with-http_flv_module
 .endif
diff --git a/files/extra-patch-upstream_fair.diff 
b/files/extra-patch-upstream_fair.diff
new file mode 100644
index 0000000..e0ec063
--- /dev/null
+++ b/files/extra-patch-upstream_fair.diff
@@ -0,0 +1,935 @@
+--- ./auto/modules.orig        2008-02-25 19:37:19.000000000 -0500
++++ ./auto/modules     2008-02-25 19:37:47.000000000 -0500
+@@ -270,6 +270,11 @@
+     HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS"
+ fi
+ 
++if [ $HTTP_UPSTREAM_FAIR = YES ]; then
++    HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_FAIR_MODULE"
++    HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_FAIR_SRCS"
++fi
++
+ # STUB
+ #USE_MD5=YES
+ #HTTP_SRCS="$HTTP_SRCS $HTPP_CACHE_SRCS"
+--- ./auto/options.orig        2008-02-25 19:37:19.000000000 -0500
++++ ./auto/options     2008-02-25 19:37:47.000000000 -0500
+@@ -76,6 +76,7 @@
+ HTTP_BROWSER=YES
+ HTTP_FLV=NO
+ HTTP_UPSTREAM_IP_HASH=YES
++HTTP_UPSTREAM_FAIR=YES
+ 
+ # STUB
+ HTTP_STUB_STATUS=NO
+@@ -181,6 +182,7 @@
+         --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO          ;;
+         --without-http_browser_module)   HTTP_BROWSER=NO            ;;
+         --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;;
++        --without-http_upstream_fair_module) HTTP_UPSTREAM_FAIR=NO ;;
+ 
+         --with-http_perl_module)         HTTP_PERL=YES              ;;
+         --with-perl_modules_path=*)      NGX_PERL_MODULES="$value"  ;;
+@@ -293,6 +295,8 @@
+   --without-http_browser_module      disable ngx_http_browser_module
+   --without-http_upstream_ip_hash_module
+                                      disable ngx_http_upstream_ip_hash_module
++  --without-http_upstream_fair_module
++                                     disable ngx_http_upstream_fair_module
+ 
+   --with-http_perl_module            enable ngx_http_perl_module
+   --with-perl_modules_path=PATH      set path to the perl modules
+--- ./auto/sources.orig        2008-02-25 19:37:19.000000000 -0500
++++ ./auto/sources     2008-02-25 19:37:47.000000000 -0500
+@@ -413,6 +413,8 @@
+ HTTP_UPSTREAM_IP_HASH_MODULE=ngx_http_upstream_ip_hash_module
+ HTTP_UPSTREAM_IP_HASH_SRCS=src/http/modules/ngx_http_upstream_ip_hash_module.c
+ 
++HTTP_UPSTREAM_FAIR_MODULE=ngx_http_upstream_fair_module
++HTTP_UPSTREAM_FAIR_SRCS=src/http/modules/ngx_http_upstream_fair_module.c
+ 
+ MAIL_INCS="src/mail"
+ 
+--- ./src/http/modules/ngx_http_upstream_fair_module.c.orig    2008-02-25 
19:37:47.000000000 -0500
++++ ./src/http/modules/ngx_http_upstream_fair_module.c 2008-02-25 
19:37:47.000000000 -0500
+@@ -0,0 +1,880 @@
++/*
++ * Copyright (C) 2007 Grzegorz Nosek
++ * Work sponsored by Ezra Zygmuntowicz & EngineYard.com
++ *
++ * Based on nginx source (C) Igor Sysoev
++ */
++
++#include <ngx_config.h>
++#include <ngx_core.h>
++#include <ngx_http.h>
++
++typedef struct {
++    ngx_atomic_t                        nreq;
++    ngx_atomic_t                        last_active;
++} ngx_http_upstream_fair_shared_t;
++
++
++typedef struct {
++    ngx_rbtree_node_t                   node;
++    ngx_cycle_t                        *cycle;
++    void                               *peers;      /* forms a unique cookie 
together with cycle */
++    ngx_int_t                           refcount;   /* accessed only under 
shmtx_lock */
++    ngx_http_upstream_fair_shared_t     stats[1];
++} ngx_http_upstream_fair_shm_block_t;
++
++
++typedef struct {
++    ngx_cycle_t                        *cycle;
++    ngx_http_upstream_fair_shm_block_t *shared;
++    ngx_http_upstream_rr_peers_t       *rrp;
++    ngx_uint_t                          current;
++    ngx_uint_t                          size_err:1;
++} ngx_http_upstream_fair_peers_t;
++
++
++#define NGX_PEER_INVALID (~0UL)
++
++
++typedef struct {
++    ngx_http_upstream_fair_shared_t    *shared;
++    ngx_http_upstream_rr_peers_t       *rrp;
++    ngx_http_upstream_fair_peers_t     *peers;
++    ngx_uint_t                          current;
++    uintptr_t                          *tried;
++    uintptr_t                           data;
++} ngx_http_upstream_fair_peer_data_t;
++
++
++static ngx_int_t ngx_http_upstream_fair_init_module(ngx_cycle_t *cycle);
++static ngx_int_t ngx_http_upstream_init_fair(ngx_conf_t *cf,
++    ngx_http_upstream_srv_conf_t *us);
++static ngx_int_t ngx_http_upstream_get_fair_peer(ngx_peer_connection_t *pc,
++    void *data);
++static void ngx_http_upstream_free_fair_peer(ngx_peer_connection_t *pc,
++    void *data, ngx_uint_t state);
++static ngx_int_t ngx_http_upstream_init_fair_peer(ngx_http_request_t *r,
++    ngx_http_upstream_srv_conf_t *us);
++static char *ngx_http_upstream_fair(ngx_conf_t *cf, ngx_command_t *cmd,
++    void *conf);
++static char *ngx_http_upstream_fair_set_shm_size(ngx_conf_t *cf,
++    ngx_command_t *cmd, void *conf);
++
++#if (NGX_HTTP_SSL)
++static ngx_int_t ngx_http_upstream_fair_set_session(ngx_peer_connection_t *pc,
++    void *data);
++static void ngx_http_upstream_fair_save_session(ngx_peer_connection_t *pc,
++    void *data);
++#endif
++
++static ngx_command_t  ngx_http_upstream_fair_commands[] = {
++
++    { ngx_string("fair"),
++      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
++      ngx_http_upstream_fair,
++      0,
++      0,
++      NULL },
++
++    { ngx_string("upstream_fair_shm_size"),
++      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
++      ngx_http_upstream_fair_set_shm_size,
++      0,
++      0,
++      NULL },
++
++      ngx_null_command
++};
++
++
++static ngx_http_module_t  ngx_http_upstream_fair_module_ctx = {
++    NULL,                                  /* preconfiguration */
++    NULL,                                  /* postconfiguration */
++
++    NULL,                                  /* create main configuration */
++    NULL,                                  /* init main configuration */
++
++    NULL,                                  /* create server configuration */
++    NULL,                                  /* merge server configuration */
++
++    NULL,                                  /* create location configuration */
++    NULL                                   /* merge location configuration */
++};
++
++
++ngx_module_t  ngx_http_upstream_fair_module = {
++    NGX_MODULE_V1,
++    &ngx_http_upstream_fair_module_ctx, /* module context */
++    ngx_http_upstream_fair_commands,    /* module directives */
++    NGX_HTTP_MODULE,                       /* module type */
++    NULL,                                  /* init master */
++    NULL,                                  /* init module */
++    NULL,                                  /* init process */
++    NULL,                                  /* init thread */
++    NULL,                                  /* exit thread */
++    NULL,                                  /* exit process */
++    NULL,                                  /* exit master */
++    NGX_MODULE_V1_PADDING
++};
++
++
++static ngx_uint_t ngx_http_upstream_fair_shm_size;
++static ngx_shm_zone_t * ngx_http_upstream_fair_shm_zone;
++static ngx_rbtree_t * ngx_http_upstream_fair_rbtree;
++
++static int
++ngx_http_upstream_fair_compare_rbtree_node(const ngx_rbtree_node_t *v_left,
++    const ngx_rbtree_node_t *v_right)
++{
++    ngx_http_upstream_fair_shm_block_t *left, *right;
++
++    left = (ngx_http_upstream_fair_shm_block_t *) v_left;
++    right = (ngx_http_upstream_fair_shm_block_t *) v_right;
++
++    if (left->cycle < right->cycle) {
++        return -1;
++    } else if (left->cycle > right->cycle) {
++        return 1;
++    } else { /* left->cycle == right->cycle */
++        if (left->peers < right->peers) {
++            return -1;
++        } else if (left->peers > right->peers) {
++            return 1;
++        } else {
++            return 0;
++        }
++    }
++}
++
++/*
++ * generic functions start here
++ */
++static void
++ngx_rbtree_generic_insert(ngx_rbtree_node_t *temp,
++    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel,
++    int (*compare)(const ngx_rbtree_node_t *left, const ngx_rbtree_node_t 
*right))
++{
++    for ( ;; ) {
++        if (node->key < temp->key) {
++
++            if (temp->left == sentinel) {
++                temp->left = node;
++                break;
++            }
++
++            temp = temp->left;
++
++        } else if (node->key > temp->key) {
++
++            if (temp->right == sentinel) {
++                temp->right = node;
++                break;
++            }
++
++            temp = temp->right;
++
++        } else { /* node->key == temp->key */
++            if (compare(node, temp) < 0) {
++
++                if (temp->left == sentinel) {
++                    temp->left = node;
++                    break;
++                }
++
++                temp = temp->left;
++
++            } else {
++
++                if (temp->right == sentinel) {
++                    temp->right = node;
++                    break;
++                }
++
++                temp = temp->right;
++            }
++        }
++    }
++
++    node->parent = temp;
++    node->left = sentinel;
++    node->right = sentinel;
++    ngx_rbt_red(node);
++}
++
++#define NGX_BITVECTOR_ELT_SIZE (sizeof(uintptr_t) * 8)
++
++static uintptr_t *
++ngx_bitvector_alloc(ngx_pool_t *pool, ngx_uint_t size, uintptr_t *small)
++{
++    ngx_uint_t nelts = (size + NGX_BITVECTOR_ELT_SIZE - 1) / 
NGX_BITVECTOR_ELT_SIZE;
++
++    if (small && nelts == 1) {
++        *small = 0;
++        return small;
++    }
++
++    return ngx_pcalloc(pool, nelts * NGX_BITVECTOR_ELT_SIZE);
++}
++
++static ngx_int_t
++ngx_bitvector_test(uintptr_t *bv, ngx_uint_t bit)
++{
++    ngx_uint_t                      n, m;
++
++    n = bit / NGX_BITVECTOR_ELT_SIZE;
++    m = 1 << (bit % NGX_BITVECTOR_ELT_SIZE);
++
++    return bv[n] & m;
++}
++
++static void
++ngx_bitvector_set(uintptr_t *bv, ngx_uint_t bit)
++{
++    ngx_uint_t                      n, m;
++
++    n = bit / NGX_BITVECTOR_ELT_SIZE;
++    m = 1 << (bit % NGX_BITVECTOR_ELT_SIZE);
++
++    bv[n] |= m;
++}
++
++/*
++ * generic functions end here
++ */
++
++static void
++ngx_http_upstream_fair_rbtree_insert(ngx_rbtree_node_t *temp,
++    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) {
++
++    ngx_rbtree_generic_insert(temp, node, sentinel,
++        ngx_http_upstream_fair_compare_rbtree_node);
++}
++
++
++static ngx_int_t
++ngx_http_upstream_fair_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data)
++{
++    ngx_slab_pool_t                *shpool;
++    ngx_rbtree_t                   *tree;
++    ngx_rbtree_node_t              *sentinel;
++
++    if (data) {
++        shm_zone->data = data;
++        return NGX_OK;
++    }
++
++    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
++    tree = ngx_slab_alloc(shpool, sizeof *tree);
++    if (tree == NULL) {
++        return NGX_ERROR;
++    }
++
++    sentinel = ngx_slab_alloc(shpool, sizeof *sentinel);
++    if (sentinel == NULL) {
++        return NGX_ERROR;
++    }
++
++    ngx_rbtree_sentinel_init(sentinel);
++    tree->root = sentinel;
++    tree->sentinel = sentinel;
++    tree->insert = ngx_http_upstream_fair_rbtree_insert;
++    shm_zone->data = tree;
++    ngx_http_upstream_fair_rbtree = tree;
++
++    return NGX_OK;
++}
++
++
++static char *
++ngx_http_upstream_fair_set_shm_size(ngx_conf_t *cf, ngx_command_t *cmd, void 
*conf)
++{
++    ssize_t                         new_shm_size;
++    ngx_str_t                      *value;
++
++    value = cf->args->elts;
++
++    new_shm_size = ngx_parse_size(&value[1]);
++    if (new_shm_size == NGX_ERROR) {
++        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "Invalid memory area size 
`%V'", &value[1]);
++        return NGX_CONF_ERROR;
++    }
++
++    new_shm_size = ngx_align(new_shm_size, ngx_pagesize);
++
++    if (new_shm_size < 8 * (ssize_t) ngx_pagesize) {
++        ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "The upstream_fair_shm_size 
value must be at least %udKiB", (8 * ngx_pagesize) >> 10);
++        new_shm_size = 8 * ngx_pagesize;
++    }
++
++    if (ngx_http_upstream_fair_shm_size &&
++        ngx_http_upstream_fair_shm_size != (ngx_uint_t) new_shm_size) {
++        ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "Cannot change memory area 
size without restart, ignoring change");
++    } else {
++        ngx_http_upstream_fair_shm_size = new_shm_size;
++    }
++    ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Using %udKiB of shared memory 
for upstream_fair", new_shm_size >> 10);
++
++    return NGX_CONF_OK;
++}
++
++
++static char *
++ngx_http_upstream_fair(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
++{
++    ngx_http_upstream_srv_conf_t  *uscf;
++
++    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
++
++    uscf->peer.init_upstream = ngx_http_upstream_init_fair;
++
++    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
++                  |NGX_HTTP_UPSTREAM_WEIGHT
++                  |NGX_HTTP_UPSTREAM_MAX_FAILS
++                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
++                  |NGX_HTTP_UPSTREAM_DOWN;
++
++    return NGX_CONF_OK;
++}
++
++
++static ngx_int_t
++ngx_http_upstream_init_fair(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
++{
++    ngx_http_upstream_fair_peers_t     *peers;
++    ngx_uint_t                          n;
++    ngx_str_t                          *shm_name;
++
++    /* do the dirty work using rr module */
++    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
++        return NGX_ERROR;
++    }
++
++    /* setup our wrapper around rr */
++    peers = ngx_palloc(cf->pool, sizeof *peers);
++    if (peers == NULL) {
++        return NGX_ERROR;
++    }
++    peers->rrp = us->peer.data;
++    us->peer.data = peers;
++    n = peers->rrp->number;
++
++    shm_name = ngx_palloc(cf->pool, sizeof *shm_name);
++    shm_name->len = sizeof("upstream_fair");
++    shm_name->data = (unsigned char *) "upstream_fair";
++
++    if (ngx_http_upstream_fair_shm_size == 0) {
++        ngx_http_upstream_fair_shm_size = 8 * ngx_pagesize;
++    }
++
++    ngx_http_upstream_fair_shm_zone = ngx_shared_memory_add(
++        cf, shm_name, ngx_http_upstream_fair_shm_size, 
&ngx_http_upstream_fair_module);
++    if (ngx_http_upstream_fair_shm_zone == NULL) {
++        return NGX_ERROR;
++    }
++    ngx_http_upstream_fair_shm_zone->init = 
ngx_http_upstream_fair_init_shm_zone;
++
++    peers->cycle = cf->cycle;
++    peers->shared = NULL;
++    peers->current = n - 1;
++    peers->size_err = 0;
++
++    us->peer.init = ngx_http_upstream_init_fair_peer;
++
++    return NGX_OK;
++}
++
++
++static void
++ngx_http_upstream_fair_update_nreq(ngx_http_upstream_fair_peer_data_t *fp, 
int delta, ngx_log_t *log)
++{
++    ngx_http_upstream_fair_shared_t     *fs;
++
++    fs = &fp->shared[fp->current];
++
++    ngx_atomic_fetch_add(&fs->nreq, delta);
++
++    fs->last_active = ngx_current_msec;
++
++    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, "[upstream_fair] nreq for peer 
%ui now %d", fp->current, fs->nreq);
++}
++
++/*
++ * SCHED_TIME_BITS is the portion of an ngx_uint_t which represents the
++ * last_time_delta part (time since last activity in msec). The rest
++ * (top bits) represents the number of currently processed requests.
++ *
++ * The value is not too critical because overflow is handled via
++ * saturation. With the default value of 24, scheduling is exact for
++ * requests shorter than 16777 sec and for less than 256 requests per
++ * backend (on 32-bit architectures). Beyond these limits, the algorithm
++ * essentially falls back to pure weighted round-robin
++ *
++ * A higher score means less suitable -- this changed from previous
++ * releases.
++ *
++ * The `delta' parameter is bit-negated so that high values yield low
++ * scores and get chosen more often.
++ */
++
++#define SCHED_TIME_BITS 24
++#define SCHED_NREQ_MAX ((~0UL) >> SCHED_TIME_BITS)
++#define SCHED_TIME_MAX ((1 << SCHED_TIME_BITS) - 1)
++#define SCHED_SCORE(nreq,delta) (((nreq) << SCHED_TIME_BITS) | (~(delta)))
++#define ngx_upstream_fair_min(a,b) (((a) < (b)) ? (a) : (b))
++
++static ngx_uint_t
++ngx_http_upstream_fair_sched_score(ngx_peer_connection_t *pc,
++    ngx_http_upstream_fair_shared_t *fs,
++    ngx_http_upstream_rr_peer_t *peer, ngx_uint_t n)
++{
++    ngx_msec_t                          last_active_delta;
++
++    last_active_delta = ngx_current_msec - fs->last_active;
++    if ((ngx_int_t) last_active_delta < 0) {
++        ngx_log_error(NGX_LOG_WARN, pc->log, 0, "[upstream_fair] Clock skew 
of at least %i msec detected", -(ngx_int_t) last_active_delta);
++
++        /* a pretty arbitrary value */
++        last_active_delta = abs(last_active_delta);
++    }
++
++    /* sanity check */
++    if ((ngx_int_t)fs->nreq < 0) {
++        ngx_log_error(NGX_LOG_WARN, pc->log, 0, "[upstream_fair] upstream %ui 
has negative nreq (%i)", n, fs->nreq);
++        return SCHED_SCORE(0, last_active_delta);
++    }
++
++    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] nreq = 
%i, last_active_delta = %ui", fs->nreq, last_active_delta);
++
++    return SCHED_SCORE(
++        ngx_upstream_fair_min(fs->nreq, SCHED_NREQ_MAX),
++        ngx_upstream_fair_min(last_active_delta, SCHED_TIME_MAX));
++}
++
++/*
++ * the core of load balancing logic
++ */
++
++static ngx_int_t
++ngx_http_upstream_fair_try_peer(ngx_peer_connection_t *pc,
++    ngx_http_upstream_fair_peer_data_t *fp,
++    ngx_uint_t peer_id,
++    time_t now)
++{
++    ngx_http_upstream_rr_peer_t        *peer;
++
++    if (ngx_bitvector_test(fp->tried, peer_id))
++        return NGX_BUSY;
++
++    peer = &fp->rrp->peer[peer_id];
++
++    if (!peer->down) {
++        if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
++            return NGX_OK;
++        }
++
++        if (now - peer->accessed > peer->fail_timeout) {
++            peer->fails = 0;
++            return NGX_OK;
++        }
++    }
++
++    ngx_bitvector_set(fp->tried, peer_id);
++
++    if (pc)
++        pc->tries--;
++    return NGX_BUSY;
++}
++
++static ngx_int_t
++ngx_http_upstream_choose_fair_peer(ngx_peer_connection_t *pc,
++    ngx_http_upstream_fair_peer_data_t *fp, ngx_uint_t *peer_id)
++{
++    ngx_uint_t                          i, n;
++    ngx_uint_t                          npeers;
++    ngx_http_upstream_fair_shared_t     fsc;
++    time_t                              now;
++    ngx_uint_t                          best_sched_score = UINT_MAX, 
sched_score;
++
++    npeers = fp->rrp->number;
++
++    /* just a single backend */
++    if (npeers == 1) {
++        *peer_id = 0;
++        return NGX_OK;
++    }
++
++    now = ngx_time();
++
++    /* any idle backends? */
++    for (i = 0, n = fp->current; i < npeers; i++, n = (n + 1) % npeers) {
++        if (ngx_atomic_fetch_add(&fp->shared[n].nreq, 0) == 0 &&
++            ngx_http_upstream_fair_try_peer(pc, fp, n, now) == NGX_OK) {
++
++            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] 
peer %i is idle", n);
++            *peer_id = n;
++            return NGX_OK;
++        }
++    }
++
++    /*
++     * calculate sched scores for all the peers, choosing the lowest one
++     */
++    for (i = 0; i < npeers; i++, n = (n + 1) % npeers) {
++        ngx_http_upstream_rr_peer_t *peer;
++
++        if (ngx_http_upstream_fair_try_peer(pc, fp, n, now) != NGX_OK) {
++            if (!pc->tries) {
++                ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, 
"[upstream_fair] all backends exhausted");
++                return NGX_BUSY;
++            }
++
++            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] 
backend %d is dead", n);
++            continue;
++        }
++
++        peer = &fp->rrp->peer[n];
++
++        if (peer->current_weight-- == 0) {
++            peer->current_weight = peer->weight;
++            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] 
peer %d expired weight, reset to %d", n, peer->weight);
++            continue;
++        }
++
++        fsc = fp->shared[n];
++        sched_score = ngx_http_upstream_fair_sched_score(pc, &fsc, peer, n);
++
++        /*
++         * take peer weight into account
++         */
++        if (peer->current_weight > 0) {
++            sched_score /= peer->current_weight;
++        }
++
++        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] bss = 
%i, ss = %i (n = %d)", best_sched_score, sched_score, n);
++
++        if (sched_score <= best_sched_score) {
++            *peer_id = n;
++            best_sched_score = sched_score;
++        }
++    }
++
++    return NGX_OK;
++}
++
++ngx_int_t
++ngx_http_upstream_get_fair_peer(ngx_peer_connection_t *pc, void *data)
++{
++    ngx_int_t                           ret;
++    ngx_uint_t                          peer_id, i;
++    ngx_http_upstream_fair_peer_data_t *fp = data;
++    ngx_http_upstream_rr_peer_t        *peer;
++
++    peer_id = fp->current;
++    fp->current = (fp->current + 1) % fp->rrp->number;
++
++    ret = ngx_http_upstream_choose_fair_peer(pc, fp, &peer_id);
++    ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] 
fp->current = %d, peer_id = %d, ret = %d",
++        fp->current, peer_id, ret);
++
++    if (ret == NGX_BUSY) {
++        for (i = 0; i < fp->rrp->number; i++) {
++            fp->rrp->peer[i].fails = 0;
++        }
++
++        pc->name = fp->rrp->name;
++        fp->current = NGX_PEER_INVALID;
++        if (pc->tries > 0) {
++            pc->tries--;
++        }
++        return NGX_BUSY;
++    }
++
++    /* assert(ret == NGX_OK); */
++    peer = &fp->rrp->peer[peer_id];
++    fp->current = peer_id;
++    fp->peers->current = peer_id;
++    pc->sockaddr = peer->sockaddr;
++    pc->socklen = peer->socklen;
++    pc->name = &peer->name;
++
++    ngx_http_upstream_fair_update_nreq(data, 1, pc->log);
++    return ret;
++}
++
++
++void
++ngx_http_upstream_free_fair_peer(ngx_peer_connection_t *pc, void *data,
++    ngx_uint_t state)
++{
++    ngx_http_upstream_fair_peer_data_t     *fp = data;
++    ngx_http_upstream_rr_peer_t            *peer;
++    ngx_uint_t                              weight_delta;
++
++    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, pc->log, 0, "[upstream_fair] 
fp->current = %d, state = %ui, pc->tries = %d, pc->data = %p",
++        fp->current, state, pc->tries, pc->data);
++
++    if (fp->current == NGX_PEER_INVALID) {
++        return;
++    }
++
++    ngx_http_upstream_fair_update_nreq(data, -1, pc->log);
++
++    if (state == 0 && pc->tries == 0) {
++        return;
++    }
++
++    if (fp->rrp->number == 1) {
++        pc->tries = 0;
++    }
++
++    if (state & NGX_PEER_FAILED) {
++        peer = &fp->rrp->peer[fp->current];
++
++        peer->fails++;
++        peer->accessed = ngx_time();
++
++        weight_delta = peer->weight / peer->max_fails;
++
++        if ((ngx_uint_t) peer->current_weight < weight_delta) {
++            peer->current_weight = 0;
++        } else {
++            peer->current_weight -= weight_delta;
++        }
++    }
++}
++
++/*
++ * walk through the rbtree, removing old entries and looking for
++ * a matching one -- compared by (cycle, peers) pair
++ *
++ * no attempt at optimisation is made, for two reasons:
++ *  - the tree will be quite small, anyway
++ *  - being called once per worker startup per upstream block,
++ *    this code isn't really the hot path
++ */
++static ngx_http_upstream_fair_shm_block_t *
++ngx_http_upstream_fair_walk_shm(
++    ngx_slab_pool_t *shpool,
++    ngx_rbtree_node_t *node,
++    ngx_rbtree_node_t *sentinel,
++    ngx_cycle_t *cycle, void *peers)
++{
++    ngx_http_upstream_fair_shm_block_t     *uf_node;
++    ngx_http_upstream_fair_shm_block_t     *found_node = NULL;
++    ngx_http_upstream_fair_shm_block_t     *tmp_node;
++
++    if (node == sentinel) {
++        return NULL;
++    }
++
++    /* visit left node */
++    if (node->left != sentinel) {
++        tmp_node = ngx_http_upstream_fair_walk_shm(shpool, node->left,
++            sentinel, cycle, peers);
++        if (tmp_node) {
++            found_node = tmp_node;
++        }
++    }
++
++    /* visit right node */
++    if (node->right != sentinel) {
++        tmp_node = ngx_http_upstream_fair_walk_shm(shpool, node->right,
++            sentinel, cycle, peers);
++        if (tmp_node) {
++            found_node = tmp_node;
++        }
++    }
++
++    /* visit current node */
++    uf_node = (ngx_http_upstream_fair_shm_block_t *) node;
++    if (uf_node->cycle != cycle) {
++        if (--uf_node->refcount == 0) {
++            ngx_rbtree_delete(ngx_http_upstream_fair_rbtree, node);
++            ngx_slab_free_locked(shpool, node);
++        }
++    } else if (uf_node->peers == peers) {
++        found_node = uf_node;
++    }
++
++    return found_node;
++}
++
++static ngx_int_t
++ngx_http_upstream_fair_shm_alloc(ngx_http_upstream_fair_peers_t *usfp, 
ngx_log_t *log)
++{
++    ngx_slab_pool_t                        *shpool;
++    ngx_uint_t                              i;
++
++    if (usfp->shared) {
++        return NGX_OK;
++    }
++
++    shpool = (ngx_slab_pool_t *)ngx_http_upstream_fair_shm_zone->shm.addr;
++
++    ngx_shmtx_lock(&shpool->mutex);
++
++    usfp->shared = ngx_http_upstream_fair_walk_shm(shpool,
++        ngx_http_upstream_fair_rbtree->root,
++        ngx_http_upstream_fair_rbtree->sentinel,
++        usfp->cycle, usfp);
++
++    if (usfp->shared) {
++        usfp->shared->refcount++;
++        ngx_shmtx_unlock(&shpool->mutex);
++        return NGX_OK;
++    }
++
++    usfp->shared = ngx_slab_alloc_locked(shpool,
++        sizeof(ngx_http_upstream_fair_shm_block_t) +
++        (usfp->rrp->number - 1) * sizeof(ngx_http_upstream_fair_shared_t));
++
++    if (!usfp->shared) {
++        ngx_shmtx_unlock(&shpool->mutex);
++        if (!usfp->size_err) {
++            ngx_log_error(NGX_LOG_EMERG, log, 0,
++                "upstream_fair_shm_size too small (current value is %udKiB)",
++                ngx_http_upstream_fair_shm_size >> 10);
++            usfp->size_err = 1;
++        }
++        return NGX_ERROR;
++    }
++
++    usfp->shared->node.key = ngx_crc32_short((u_char *) &usfp->cycle, sizeof 
usfp->cycle) ^
++        ngx_crc32_short((u_char *) &usfp, sizeof(usfp));
++
++    usfp->shared->refcount = 1;
++    usfp->shared->cycle = usfp->cycle;
++    usfp->shared->peers = usfp;
++
++    for (i = 0; i < usfp->rrp->number; i++) {
++            usfp->shared->stats[i].nreq = 0;
++            usfp->shared->stats[i].last_active = ngx_current_msec;
++    }
++
++    ngx_rbtree_insert(ngx_http_upstream_fair_rbtree, &usfp->shared->node);
++
++    ngx_shmtx_unlock(&shpool->mutex);
++    return NGX_OK;
++}
++
++ngx_int_t
++ngx_http_upstream_init_fair_peer(ngx_http_request_t *r,
++    ngx_http_upstream_srv_conf_t *us)
++{
++    ngx_http_upstream_fair_peer_data_t     *fp;
++    ngx_http_upstream_fair_peers_t         *usfp;
++
++    fp = r->upstream->peer.data;
++
++    if (fp == NULL) {
++        fp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_fair_peer_data_t));
++        if (fp == NULL) {
++            return NGX_ERROR;
++        }
++
++        r->upstream->peer.data = fp;
++    }
++
++    usfp = us->peer.data;
++
++    fp->tried = ngx_bitvector_alloc(r->pool, usfp->rrp->number, &fp->data);
++
++    if (fp->tried == NULL) {
++        return NGX_ERROR;
++    }
++
++    /* set up shared memory area */
++    ngx_http_upstream_fair_shm_alloc(usfp, r->connection->log);
++
++    fp->shared = &usfp->shared->stats[0];
++    fp->rrp = usfp->rrp;
++    fp->current = usfp->current;
++    fp->peers = usfp;
++
++    r->upstream->peer.get = ngx_http_upstream_get_fair_peer;
++    r->upstream->peer.free = ngx_http_upstream_free_fair_peer;
++    r->upstream->peer.tries = usfp->rrp->number;
++#if (NGX_HTTP_SSL)
++    r->upstream->peer.set_session =
++                               ngx_http_upstream_fair_set_session;
++    r->upstream->peer.save_session =
++                               ngx_http_upstream_fair_save_session;
++#endif
++
++    return NGX_OK;
++}
++
++#if (NGX_HTTP_SSL)
++static ngx_int_t
++ngx_http_upstream_fair_set_session(ngx_peer_connection_t *pc, void *data)
++{
++    ngx_http_upstream_fair_peer_data_t  *fp = data;
++
++    ngx_int_t                     rc;
++    ngx_ssl_session_t            *ssl_session;
++    ngx_http_upstream_rr_peer_t  *peer;
++
++    if (fp->current == NGX_PEER_INVALID)
++        return NGX_OK;
++
++    peer = &fp->rrp->peer[fp->current];
++
++    /* TODO: threads only mutex */
++    /* ngx_lock_mutex(fp->rrp->peers->mutex); */
++
++    ssl_session = peer->ssl_session;
++
++    rc = ngx_ssl_set_session(pc->connection, ssl_session);
++
++    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
++                   "set session: %p:%d",
++                   ssl_session, ssl_session ? ssl_session->references : 0);
++
++    /* ngx_unlock_mutex(fp->rrp->peers->mutex); */
++
++    return rc;
++}
++
++static void
++ngx_http_upstream_fair_save_session(ngx_peer_connection_t *pc, void *data)
++{
++    ngx_http_upstream_fair_peer_data_t  *fp = data;
++
++    ngx_ssl_session_t            *old_ssl_session, *ssl_session;
++    ngx_http_upstream_rr_peer_t  *peer;
++
++    if (fp->current == NGX_PEER_INVALID)
++        return;
++
++    ssl_session = ngx_ssl_get_session(pc->connection);
++
++    if (ssl_session == NULL) {
++        return;
++    }
++
++    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
++                   "save session: %p:%d", ssl_session, 
ssl_session->references);
++
++    peer = &fp->rrp->peer[fp->current];
++
++    /* TODO: threads only mutex */
++    /* ngx_lock_mutex(fp->rrp->peers->mutex); */
++
++    old_ssl_session = peer->ssl_session;
++    peer->ssl_session = ssl_session;
++
++    /* ngx_unlock_mutex(fp->rrp->peers->mutex); */
++
++    if (old_ssl_session) {
++
++        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
++                       "old session: %p:%d",
++                       old_ssl_session, old_ssl_session->references);
++
++        /* TODO: may block */
++
++        ngx_ssl_free_session(old_ssl_session);
++    }
++}
++
++#endif
_______________________________________________
freebsd-ports@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-ports
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to