From a05f459b2b046482fca1ba020527d51cb7172317 Mon Sep 17 00:00:00 2001 From: "roberto@debian32" Date: Mon, 19 Sep 2011 07:13:45 +0200 Subject: [PATCH] preliminary cheaper implementation --- master.c | 72 +++++++++++++++++++++++++++++++++ master_utils.c | 1 + plugins/python/uwsgi_pymodule.c | 17 ++++++++ utils.c | 3 ++ uwsgi.c | 30 +++++++++++--- uwsgi.h | 10 ++++- welcome.py | 27 ++++++++++--- 7 files changed, 147 insertions(+), 13 deletions(-) diff --git a/master.c b/master.c index 6de13430..94e9af4b 100644 --- a/master.c +++ b/master.c @@ -289,6 +289,7 @@ int master_loop(char **argv, char **environ) { int rlen; int check_interval = 1; + uint64_t overload_count = 0; struct uwsgi_rb_timer *min_timeout; struct rb_root *rb_timers = uwsgi_init_rb_timer(); @@ -360,6 +361,9 @@ int master_loop(char **argv, char **environ) { if (uwsgi.cheap) { uwsgi_add_sockets_to_queue(uwsgi.master_queue); + for(i=1;i<=uwsgi.numproc;i++) { + uwsgi.workers[i].cheaped = 1; + } uwsgi_log("cheap mode enabled: waiting for socket connection...\n"); } @@ -513,6 +517,7 @@ int master_loop(char **argv, char **environ) { uwsgi.lazy_respawned = 0; } } + if (uwsgi.master_mercy) { if (uwsgi.master_mercy < time(NULL)) { for(i=1;i<=uwsgi.numproc;i++) { @@ -569,6 +574,67 @@ int master_loop(char **argv, char **environ) { uwsgi.restore_snapshot = 0; continue; } + + // cheaper management + if (uwsgi.cheaper) { + for(i=1;i<=uwsgi.numproc;i++) { + if (uwsgi.workers[i].cheaped == 0) { + if (uwsgi.workers[i].busy == 0) { + if (overload_count > 0) overload_count--; + goto healthy; + } + } + } + overload_count++; + } + +healthy: + if (uwsgi.cheaper) { + if (overload_count > 3) { + // activate the first available worker + for(i=1;i<=uwsgi.numproc;i++) { + if (uwsgi.workers[i].cheaped == 1 && uwsgi.workers[i].pid == 0) { + if (uwsgi_respawn_worker(i)) return 0; + overload_count = 0; + break; + } + } + } + else if (overload_count == 0) { + // how many active workers ? + int active_workers = 0; + for(i=1;i<=uwsgi.numproc;i++) { + if (uwsgi.workers[i].cheaped == 0 && uwsgi.workers[i].pid > 0) { + active_workers++; + } + } + + uwsgi_log("%d active workers\n", active_workers); + // find the oldest worker and cheap it + if (active_workers > uwsgi.cheaper_count) { + time_t oldest_worker_spawn = LONG_MAX; + int oldest_worker = 0; + for(i=1;i<=uwsgi.numproc;i++) { + if (uwsgi.workers[i].cheaped == 0) { + if (uwsgi.workers[i].last_spawn < oldest_worker_spawn) { + oldest_worker_spawn = uwsgi.workers[i].last_spawn; + oldest_worker = i; + } + } + } + if (oldest_worker > 0) { + uwsgi_log("worker %d should die...\n", oldest_worker); + uwsgi.workers[oldest_worker].cheaped = 1; + uwsgi.workers[oldest_worker].manage_next_request = 0; + // wakeup task in case of wait + (void) kill(uwsgi.workers[oldest_worker].pid, SIGWINCH); + overload_count = 0; + } + } + } + } + + if ((uwsgi.cheap || ready_to_die >= uwsgi.numproc) && uwsgi.to_hell) { // call a series of waitpid to ensure all processes (gateways and daemons) are dead for(i=0;i<(uwsgi.gateways_cnt+ushared->daemons_cnt);i++) { @@ -1355,6 +1421,12 @@ int master_loop(char **argv, char **environ) { if (uwsgi.workers[uwsgi.mywid].manage_next_request) { uwsgi_log( "DAMN ! worker %d (pid: %d) died :( trying respawn ...\n", uwsgi.mywid, (int)diedpid); } + + if (uwsgi.workers[uwsgi.mywid].cheaped == 1) { + uwsgi.workers[uwsgi.mywid].pid = 0; + uwsgi_log("uWSGI worker %d cheaped.\n", uwsgi.mywid); + continue; + } gettimeofday(&last_respawn, NULL); if (last_respawn.tv_sec == uwsgi.respawn_delta) { last_respawn_rate++; diff --git a/master_utils.c b/master_utils.c index 81462d63..a4b833b2 100644 --- a/master_utils.c +++ b/master_utils.c @@ -59,6 +59,7 @@ int uwsgi_respawn_worker(int wid) { uwsgi.workers[uwsgi.mywid].respawn_count++; uwsgi.workers[uwsgi.mywid].last_spawn = uwsgi.current_time; uwsgi.workers[uwsgi.mywid].manage_next_request = 1; + uwsgi.workers[uwsgi.mywid].cheaped = 0; // reset the apps count with a copy from the master uwsgi.workers[uwsgi.mywid].apps_cnt = uwsgi.workers[0].apps_cnt; diff --git a/plugins/python/uwsgi_pymodule.c b/plugins/python/uwsgi_pymodule.c index 38e7b4d3..f25e9b3e 100644 --- a/plugins/python/uwsgi_pymodule.c +++ b/plugins/python/uwsgi_pymodule.c @@ -2232,6 +2232,23 @@ PyObject *py_uwsgi_workers(PyObject * self, PyObject * args) { } Py_DECREF(zero); + if (uwsgi.workers[i + 1].cheaped) { + zero = PyString_FromString("cheap"); + } + else { + if (uwsgi.workers[i + 1].busy) { + zero = PyString_FromString("busy"); + } + else { + zero = PyString_FromString("idle"); + } + } + if (PyDict_SetItemString(worker_dict, "status", zero)) { + goto clear; + } + + Py_DECREF(zero); + zero = PyInt_FromLong(uwsgi.workers[i + 1].rss_size); if (PyDict_SetItemString(worker_dict, "rss", zero)) { goto clear; diff --git a/utils.c b/utils.c index 1fa1d033..845261e2 100644 --- a/utils.c +++ b/utils.c @@ -764,12 +764,14 @@ void wsgi_req_setup(struct wsgi_request *wsgi_req, int async_id, struct uwsgi_so } uwsgi.core[wsgi_req->async_id]->in_request = 0; + uwsgi.workers[uwsgi.mywid].busy = 0; } #ifdef UWSGI_ASYNC int wsgi_req_async_recv(struct wsgi_request *wsgi_req) { uwsgi.core[wsgi_req->async_id]->in_request = 1; + uwsgi.workers[uwsgi.mywid].busy = 1; gettimeofday(&wsgi_req->start_of_request, NULL); @@ -795,6 +797,7 @@ int wsgi_req_async_recv(struct wsgi_request *wsgi_req) { int wsgi_req_recv(struct wsgi_request *wsgi_req) { uwsgi.core[wsgi_req->async_id]->in_request = 1; + uwsgi.workers[uwsgi.mywid].busy = 1; gettimeofday(&wsgi_req->start_of_request, NULL); diff --git a/uwsgi.c b/uwsgi.c index 3f0809c4..6a311d27 100644 --- a/uwsgi.c +++ b/uwsgi.c @@ -206,6 +206,7 @@ static struct option long_base_options[] = { {"chdir2", required_argument, 0, LONG_ARGS_CHDIR2}, {"lazy", no_argument, &uwsgi.lazy, 1}, {"cheap", no_argument, &uwsgi.cheap, 1}, + {"cheaper", required_argument, 0, LONG_ARGS_CHEAPER}, {"idle", required_argument, 0, LONG_ARGS_IDLE}, {"die-on-idle", no_argument, &uwsgi.die_on_idle, 1}, {"mount", required_argument, 0, LONG_ARGS_MOUNT}, @@ -2300,11 +2301,25 @@ skipzero: uwsgi.current_time = time(NULL); if (!uwsgi.cheap) { - for (i = 2 - uwsgi.master_process; i < uwsgi.numproc + 1; i++) { - if (uwsgi_respawn_worker(i)) - break; - gettimeofday(&last_respawn, NULL); - uwsgi.respawn_delta = last_respawn.tv_sec; + if (uwsgi.cheaper && uwsgi.cheaper_count) { + for(i=1;i<=uwsgi.numproc;i++) { + if (i <= uwsgi.cheaper_count) { + if (uwsgi_respawn_worker(i)) break; + gettimeofday(&last_respawn, NULL); + uwsgi.respawn_delta = last_respawn.tv_sec; + } + else { + uwsgi.workers[i].cheaped = 1; + } + } + } + else { + for (i = 2 - uwsgi.master_process; i < uwsgi.numproc + 1; i++) { + if (uwsgi_respawn_worker(i)) + break; + gettimeofday(&last_respawn, NULL); + uwsgi.respawn_delta = last_respawn.tv_sec; + } } } @@ -2785,6 +2800,11 @@ static int manage_base_opt(int i, char *optarg) { case LONG_ARGS_IDLE: uwsgi.idle = atoi(optarg); return 1; + case LONG_ARGS_CHEAPER: + uwsgi.master_process = 1; + uwsgi.cheaper = 1; + uwsgi.cheaper_count = atoi(optarg); + return 1; case LONG_ARGS_CHDIR: uwsgi.chdir = optarg; return 1; diff --git a/uwsgi.h b/uwsgi.h index 6ecd773b..d4d6c3e6 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -150,10 +150,10 @@ extern char UWSGI_EMBED_CONFIG_END; #include #endif extern int pivot_root(const char *new_root, const char *put_old); -#else -#include #endif +#include + #include #ifdef UWSGI_SCTP @@ -508,6 +508,7 @@ struct uwsgi_opt { #define LONG_ARGS_RELOAD 17137 #define LONG_ARGS_REGEXP_MOUNT 17138 #define LONG_ARGS_MIMEFILE 17139 +#define LONG_ARGS_CHEAPER 17140 #define UWSGI_OK 0 @@ -964,6 +965,8 @@ struct uwsgi_server { int lazy; int cheap; + int cheaper; + int cheaper_count; int idle; int die_on_idle; @@ -1579,6 +1582,9 @@ struct uwsgi_worker { uint64_t tx; + int busy; + int cheaped; + }; diff --git a/welcome.py b/welcome.py index a16d83af..8c70ca60 100644 --- a/welcome.py +++ b/welcome.py @@ -5,6 +5,17 @@ import sys gc.set_debug(gc.DEBUG_SAVEALL) print sys.modules +print sys.argv + +try: + if sys.argv[1] == 'debug': + DEBUG = True + else: + raise +except: + DEBUG = False + + def xsendfile(e, sr): sr('200 OK', [('Content-Type', 'image/png'), ('X-Sendfile', os.path.abspath('logo_uWSGI.png'))]) @@ -33,17 +44,21 @@ routes['/options'] = serve_options def application(env, start_response): gc.collect(2) - print env['wsgi.input'].fileno() + if DEBUG: + print env['wsgi.input'].fileno() if routes.has_key(env['PATH_INFO']): return routes[env['PATH_INFO']](env, start_response) start_response('200 OK', [('Content-Type', 'text/html')]) - print env['wsgi.input'].fileno() + if DEBUG: + print env['wsgi.input'].fileno() + gc.collect(2) - print len(gc.get_objects()) + if DEBUG: + print len(gc.get_objects()) workers = '' for w in uwsgi.workers(): @@ -53,9 +68,9 @@ def application(env, start_response): apps += '' workers += """ -%d%d%d%s +%d%d%s%d%s - """ % (w['id'], w['pid'], w['tx'], apps) + """ % (w['id'], w['pid'], w['status'], w['tx'], apps) return """ version %s
@@ -73,7 +88,7 @@ Dynamic options
Workers and applications
- + %s
widpidtxappswidpidstatustxapps