/*
* 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_uint_t nreq;
ngx_uint_t total_req;
ngx_uint_t last_req_id;
ngx_uint_t fails;
ngx_uint_t current_weight;
} ngx_http_upstream_fair_shared_t;
typedef struct ngx_http_upstream_fair_peers_s ngx_http_upstream_fair_peers_t;
typedef struct {
ngx_rbtree_node_t node;
ngx_uint_t generation;
uintptr_t peers; /* forms a unique cookie together with generation */
ngx_uint_t total_nreq;
ngx_uint_t total_requests;
ngx_atomic_t lock;
ngx_http_upstream_fair_shared_t stats[1];
} ngx_http_upstream_fair_shm_block_t;
/* ngx_spinlock is defined without a matching unlock primitive */
#define ngx_spinlock_unlock(lock) (void) ngx_atomic_cmp_set(lock, ngx_pid, 0)
typedef struct {
ngx_http_upstream_fair_shared_t *shared;
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t name;
ngx_uint_t weight;
ngx_uint_t max_fails;
time_t fail_timeout;
time_t accessed;
ngx_uint_t down:1;
#if (NGX_HTTP_SSL)
ngx_ssl_session_t *ssl_session; /* local to a process */
#endif
} ngx_http_upstream_fair_peer_t;
#define NGX_HTTP_UPSTREAM_FAIR_NO_RR (1<<26)
#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_IDLE (1<<27)
#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_PEAK (1<<28)
#define NGX_HTTP_UPSTREAM_FAIR_WEIGHT_MODE_MASK ((1<<27) | (1<<28))
enum { WM_DEFAULT = 0, WM_IDLE, WM_PEAK };
struct ngx_http_upstream_fair_peers_s {
ngx_http_upstream_fair_shm_block_t *shared;
ngx_uint_t current;
ngx_uint_t size_err:1;
ngx_uint_t no_rr:1;
ngx_uint_t weight_mode:2;
ngx_uint_t number;
ngx_str_t *name;
ngx_http_upstream_fair_peers_t *next; /* for backup peers support, not really used yet */
ngx_http_upstream_fair_peer_t peer[1];
};
#define NGX_PEER_INVALID (~0UL)
typedef struct {
ngx_http_upstream_fair_peers_t *peers;
ngx_uint_t current;
uintptr_t *tried;
uintptr_t *done;
uintptr_t data;
uintptr_t data2;
} ngx_http_upstream_fair_peer_data_t;
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);
static ngx_int_t ngx_http_upstream_fair_init_module(ngx_cycle_t *cycle);
#if (NGX_HTTP_EXTENDED_STATUS)
static ngx_chain_t *ngx_http_upstream_fair_report_status(ngx_http_request_t *r,
ngx_int_t *length);
#endif
#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_ANY,
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 */
#if (NGX_HTTP_EXTENDED_STATUS)
ngx_http_upstream_fair_report_status,
#endif
};
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 */
ngx_http_upstream_fair_init_module, /* 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 ngx_uint_t ngx_http_upstream_fair_generation;
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->generation < right->generation) {
return -1;
} else if (left->generation > right->generation) {
return 1;
} else { /* left->generation == right->generation */
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;