preliminary refactoring for the spooler

This commit is contained in:
Unbit
2014-01-04 19:28:01 +01:00
parent f18ceea9bf
commit 15fa2be4ee
5 changed files with 189 additions and 220 deletions
+107 -29
View File
@@ -72,12 +72,12 @@ struct uwsgi_spooler *uwsgi_new_spooler(char *dir) {
}
struct uwsgi_spooler *uwsgi_get_spooler_by_name(char *name) {
struct uwsgi_spooler *uwsgi_get_spooler_by_name(char *name, size_t name_len) {
struct uwsgi_spooler *uspool = uwsgi.spoolers;
while (uspool) {
if (!strcmp(uspool->dir, name)) {
if (!uwsgi_strncmp(uspool->dir, strlen(uspool->dir), name, name_len)) {
return uspool;
}
uspool = uspool->next;
@@ -167,47 +167,122 @@ void destroy_spool(char *dir, char *file) {
}
struct spooler_req {
char *spooler;
size_t spooler_len;
char *priority;
size_t priority_len;
time_t at;
};
int spool_request(struct uwsgi_spooler *uspool, char *filename, int rn, int core_id, char *buffer, int size, char *priority, time_t at, char *body, size_t body_len) {
static void spooler_req_parser_hook(char *key, uint16_t key_len, char *value, uint16_t value_len, void *data) {
struct spooler_req *sr = (struct spooler_req *) data;
if (!uwsgi_strncmp(key, key_len, "spooler", 7)) {
sr->spooler = value;
sr->spooler_len = value_len;
return;
}
if (!uwsgi_strncmp(key, key_len, "priority", 8)) {
sr->priority = value;
sr->priority_len = value_len;
return;
}
if (!uwsgi_strncmp(key, key_len, "at", 2)) {
sr->at = uwsgi_str_num(value, value_len);
return;
}
}
char *uwsgi_spool_request(struct wsgi_request *wsgi_req, char *buf, size_t len, char *body, size_t body_len) {
struct timeval tv;
int fd;
struct uwsgi_header uh;
static uint64_t internal_counter = 0;
int core_id = 0;
int fd = -1;
struct spooler_req sr;
if (len > 0xffff) {
uwsgi_log("[uwsgi-spooler] args buffer is limited to 64k, use the 'body' for bigger values\n");
return NULL;
}
// parse the request buffer
memset(&sr, 0, sizeof(struct spooler_req));
uwsgi_hooked_parse(buf, len, spooler_req_parser_hook, &sr);
struct uwsgi_spooler *uspool = uwsgi.spoolers;
if (!uspool) {
uspool = uwsgi.spoolers;
uwsgi_log("[uwsgi-spooler] no spooler available\n");
return NULL;
}
// if it is a number, get the spooler by id instead of by name
if (sr.spooler && sr.spooler_len) {
uspool = uwsgi_get_spooler_by_name(sr.spooler, sr.spooler_len);
if (!uspool) {
uwsgi_log("[uwsgi-spooler] unable to find spooler \"%.*s\"\n", sr.spooler_len, sr.spooler);
return NULL;
}
}
if (wsgi_req) {
core_id = wsgi_req->async_id;
}
// this lock is for threads, the pid value in filename will avoid multiprocess races
uwsgi_lock(uspool->lock);
// we increase it even if the request fails
internal_counter++;
gettimeofday(&tv, NULL);
if (priority) {
if (snprintf(filename, 1024, "%s/%s", uspool->dir, priority) <= 0) {
char *filename = NULL;
size_t filename_len = 0;
if (sr.priority && sr.priority_len) {
filename_len = strlen(uspool->dir) + sr.priority_len + strlen(uwsgi.hostname) + 256;
filename = uwsgi_malloc(filename_len);
int ret = snprintf(filename, filename_len, "%s/%.*s", uspool->dir, sr.priority_len, sr.priority);
if (ret <= 0 || ret >= (int) filename_len) {
uwsgi_log("[uwsgi-spooler] error generating spooler filename\n");
free(filename);
uwsgi_unlock(uspool->lock);
return 0;
return NULL;
}
// no need to check for errors...
(void) mkdir(filename, 0777);
if (snprintf(filename, 1024, "%s/%s/uwsgi_spoolfile_on_%s_%d_%d_%d_%llu_%llu", uspool->dir, priority, uwsgi.hostname, (int) getpid(), rn, core_id, (unsigned long long) tv.tv_sec, (unsigned long long) tv.tv_usec) <= 0) {
ret = snprintf(filename, filename_len, "%s/%.*s/uwsgi_spoolfile_on_%s_%d_%d_%d_%llu_%llu", uspool->dir, sr.priority_len, sr.priority, uwsgi.hostname, (int) getpid(), internal_counter, core_id,
(unsigned long long) tv.tv_sec, (unsigned long long) tv.tv_usec);
if (ret <= 0 || ret >=(int) filename_len) {
uwsgi_log("[uwsgi-spooler] error generating spooler filename\n");
free(filename);
uwsgi_unlock(uspool->lock);
return 0;
return NULL;
}
}
else {
if (snprintf(filename, 1024, "%s/uwsgi_spoolfile_on_%s_%d_%d_%d_%llu_%llu", uspool->dir, uwsgi.hostname, (int) getpid(), rn, core_id, (unsigned long long) tv.tv_sec, (unsigned long long) tv.tv_usec) <= 0) {
filename_len = strlen(uspool->dir) + strlen(uwsgi.hostname) + 256;
filename = uwsgi_malloc(filename_len);
int ret = snprintf(filename, filename_len, "%s/uwsgi_spoolfile_on_%s_%d_%d_%d_%llu_%llu", uspool->dir, uwsgi.hostname, (int) getpid(), internal_counter, core_id,
(unsigned long long) tv.tv_sec, (unsigned long long) tv.tv_usec);
if (ret <= 0 || ret >= (int) filename_len) {
uwsgi_log("[uwsgi-spooler] error generating spooler filename\n");
free(filename);
uwsgi_unlock(uspool->lock);
return 0;
return NULL;
}
}
fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd < 0) {
uwsgi_error_open(filename);
free(filename);
uwsgi_unlock(uspool->lock);
return 0;
return NULL;
}
// now lock the file, it will no be runnable, until the lock is not removed
@@ -215,13 +290,15 @@ int spool_request(struct uwsgi_spooler *uspool, char *filename, int rn, int core
// in such case the spooler will detect a zeroed file and will retry later
if (uwsgi_fcntl_lock(fd)) {
close(fd);
free(filename);
uwsgi_unlock(uspool->lock);
return 0;
return NULL;
}
struct uwsgi_header uh;
uh.modifier1 = 17;
uh.modifier2 = 0;
uh.pktsize = (uint16_t) size;
uh.pktsize = (uint16_t) len;
#ifdef __BIG_ENDIAN__
uh.pktsize = uwsgi_swap16(uh.pktsize);
#endif
@@ -231,7 +308,7 @@ int spool_request(struct uwsgi_spooler *uspool, char *filename, int rn, int core
goto clear;
}
if (write(fd, buffer, size) != size) {
if (write(fd, buf, len) != (ssize_t) len) {
uwsgi_log("[spooler] unable to write args for %s\n", filename);
goto clear;
}
@@ -243,28 +320,28 @@ int spool_request(struct uwsgi_spooler *uspool, char *filename, int rn, int core
}
}
if (at > 0) {
if (sr.at > 0) {
#ifdef __UCLIBC__
struct timespec ts[2];
ts[0].tv_sec = at;
ts[0].tv_sec = sr.at;
ts[0].tv_nsec = 0;
ts[1].tv_sec = at;
ts[1].tv_sec = sr.at;
ts[1].tv_nsec = 0;
if (futimens(fd, ts)) {
uwsgi_error("futimens()");
uwsgi_error("uwsgi_spooler_request()/futimens()");
}
#else
struct timeval tv[2];
tv[0].tv_sec = at;
tv[0].tv_sec = sr.at;
tv[0].tv_usec = 0;
tv[1].tv_sec = at;
tv[1].tv_sec = sr.at;
tv[1].tv_usec = 0;
#ifdef __sun__
if (futimesat(fd, NULL, tv)) {
#else
if (futimes(fd, tv)) {
#endif
uwsgi_error("futimes()");
uwsgi_error("uwsgi_spooler_request()/futimes()");
}
#endif
}
@@ -273,7 +350,7 @@ int spool_request(struct uwsgi_spooler *uspool, char *filename, int rn, int core
close(fd);
if (!uwsgi.spooler_quiet)
uwsgi_log("[spooler] written %lu bytes to file %s\n", (unsigned long) size + body_len + 4, filename);
uwsgi_log("[spooler] written %lu bytes to file %s\n", (unsigned long) len + body_len + 4, filename);
// and here waiting threads can continue
uwsgi_unlock(uspool->lock);
@@ -293,18 +370,19 @@ int spool_request(struct uwsgi_spooler *uspool, char *filename, int rn, int core
spoolers = spoolers->next;
}
return 1;
return filename;
clear:
if (filename) free(filename);
uwsgi_unlock(uspool->lock);
uwsgi_error("write()");
uwsgi_error("uwsgi_spool_request()/write()");
if (unlink(filename)) {
uwsgi_error("unlink()");
uwsgi_error("uwsgi_spool_request()/unlink()");
}
// unlock the file too
close(fd);
return 0;
return NULL;
}
+44 -111
View File
@@ -1745,25 +1745,13 @@ PyObject *py_uwsgi_send_spool(PyObject * self, PyObject * args, PyObject *kw) {
PyObject *spool_dict, *spool_vars;
PyObject *zero, *key, *val;
uint16_t keysize, valsize;
char *cur_buf;
int i;
char spool_filename[1024];
struct wsgi_request *wsgi_req = py_current_wsgi_req();
char *priority = NULL;
long numprio = 0;
time_t at = 0;
char *body = NULL;
size_t body_len= 0;
// this is a counter for non-request-related tasks (like threads or greenlet)
static int internal_counter = 0xffff;
struct uwsgi_spooler *uspool = uwsgi.spoolers;
spool_dict = PyTuple_GetItem(args, 0);
if (spool_dict) {
if (!PyDict_Check(spool_dict)) {
return PyErr_Format(PyExc_ValueError, "The argument of spooler callable must be a dictionary");
}
@@ -1774,46 +1762,10 @@ PyObject *py_uwsgi_send_spool(PyObject * self, PyObject * args, PyObject *kw) {
spool_dict = kw;
}
if (!spool_dict) {
return PyErr_Format(PyExc_ValueError, "The argument of spooler callable must be a dictionary");
}
// TODO if "spooler" is a num, get the spooler by id, otherwise get it by directory
PyObject *py_spooler = uwsgi_py_dict_get(spool_dict, "spooler");
if (py_spooler) {
if (PyString_Check(py_spooler)) {
uspool = uwsgi_get_spooler_by_name(PyString_AsString(py_spooler));
if (!uspool) {
return PyErr_Format(PyExc_ValueError, "Unknown spooler requested");
}
}
}
PyObject *pyprio = uwsgi_py_dict_get(spool_dict, "priority");
if (pyprio) {
if (PyInt_Check(pyprio)) {
numprio = PyInt_AsLong(pyprio);
uwsgi_py_dict_del(spool_dict, "priority");
}
}
PyObject *pyat = uwsgi_py_dict_get(spool_dict, "at");
if (pyat) {
if (PyInt_Check(pyat)) {
at = (time_t) PyInt_AsLong(pyat);
uwsgi_py_dict_del(spool_dict, "at");
}
else if (PyLong_Check(pyat)) {
at = (time_t) PyLong_AsLong(pyat);
uwsgi_py_dict_del(spool_dict, "at");
}
else if (PyFloat_Check(pyat)) {
at = (time_t) PyFloat_AsDouble(pyat);
uwsgi_py_dict_del(spool_dict, "at");
}
}
PyObject *pybody = uwsgi_py_dict_get(spool_dict, "body");
if (pybody) {
if (PyString_Check(pybody)) {
@@ -1830,9 +1782,8 @@ PyObject *py_uwsgi_send_spool(PyObject * self, PyObject * args, PyObject *kw) {
return Py_None;
}
char *spool_buffer = uwsgi_malloc(UMAX16);
cur_buf = spool_buffer;
int i;
struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size);
for (i = 0; i < PyList_Size(spool_vars); i++) {
zero = PyList_GetItem(spool_vars, i);
@@ -1841,98 +1792,80 @@ PyObject *py_uwsgi_send_spool(PyObject * self, PyObject * args, PyObject *kw) {
key = PyTuple_GetItem(zero, 0);
val = PyTuple_GetItem(zero, 1);
#ifdef UWSGI_DEBUG
uwsgi_log("ob_type %s %s\n", key->ob_type->tp_name, val->ob_type->tp_name);
#endif
if (PyString_Check(key) && PyString_Check(val)) {
if (PyString_Check(key)) {
keysize = PyString_Size(key);
valsize = PyString_Size(val);
if (cur_buf + keysize + 2 + valsize + 2 <= spool_buffer + UMAX16) {
*cur_buf++ = (uint8_t) (keysize & 0xff);
*cur_buf++ = (uint8_t) ((keysize >> 8) & 0xff);
memcpy(cur_buf, PyString_AsString(key), keysize);
cur_buf += keysize;
*cur_buf++ = (uint8_t) (valsize & 0xff);
*cur_buf++ = (uint8_t) ((valsize >> 8) & 0xff);
memcpy(cur_buf, PyString_AsString(val), valsize);
cur_buf += valsize;
if (PyString_Check(val)) {
valsize = PyString_Size(val);
if (uwsgi_buffer_append_keyval(ub, PyString_AsString(key), keysize, PyString_AsString(val), valsize)) {
Py_DECREF(zero);
uwsgi_buffer_destroy(ub);
goto error;
}
}
else {
Py_DECREF(zero);
free(spool_buffer);
return PyErr_Format(PyExc_ValueError, "spooler packet cannot be more than %d bytes", UMAX16);
PyObject *str = PyObject_Str(val);
if (!str) {
Py_DECREF(zero);
uwsgi_buffer_destroy(ub);
goto error;
}
if (uwsgi_buffer_append_keyval(ub, PyString_AsString(key), keysize, PyString_AsString(str), PyString_Size(str))) {
Py_DECREF(zero);
Py_DECREF(str);
uwsgi_buffer_destroy(ub);
goto error;
}
Py_DECREF(str);
}
}
else {
Py_DECREF(zero);
free(spool_buffer);
return PyErr_Format(PyExc_ValueError, "spooler callable dictionary must contains only strings");
uwsgi_buffer_destroy(ub);
goto error;
}
}
else {
free(spool_buffer);
Py_DECREF(zero);
Py_INCREF(Py_None);
return Py_None;
uwsgi_buffer_destroy(ub);
goto error;
}
Py_DECREF(zero);
}
else {
free(spool_buffer);
Py_INCREF(Py_None);
return Py_None;
uwsgi_buffer_destroy(ub);
goto error;
}
}
if (numprio) {
priority = uwsgi_num2str(numprio);
}
int async_id;
if (wsgi_req) {
async_id = wsgi_req->async_id;
}
else {
// the GIL is protecting this counter...
async_id = internal_counter;
internal_counter++;
}
UWSGI_RELEASE_GIL
i = spool_request(uspool, spool_filename, uwsgi.workers[0].requests + 1, async_id, spool_buffer, cur_buf - spool_buffer, priority, at, body, body_len);
char *filename = uwsgi_spool_request(wsgi_req, ub->buf, ub->pos, body, body_len);
uwsgi_buffer_destroy(ub);
UWSGI_GET_GIL
if (pybody) {
Py_DECREF(pybody);
}
if (priority) {
free(priority);
}
free(spool_buffer);
Py_DECREF(spool_vars);
if (i > 0) {
char *slash = uwsgi_get_last_char(spool_filename, '/');
if (slash) {
return PyString_FromString(slash+1);
}
return PyString_FromString(spool_filename);
if (filename) {
PyObject *ret = PyString_FromString(filename);
free(filename);
return ret;
}
return PyErr_Format(PyExc_ValueError, "unable to spool job");
error:
#ifdef PYTHREE
return PyErr_Format(PyExc_ValueError, "spooler callable dictionary must contains only bytes");
#else
return PyErr_Format(PyExc_ValueError, "spooler callable dictionary must contains only strings");
#endif
}
PyObject *py_uwsgi_spooler_pid(PyObject * self, PyObject * args) {
+29 -68
View File
@@ -932,35 +932,35 @@ static VALUE uwsgi_ruby_signal(int argc, VALUE *argv, VALUE *class) {
return Qtrue;
}
int rack_uwsgi_build_spool(VALUE rbkey, VALUE rbval, VALUE argv) {
char **sa = (char **) argv;
static int rack_uwsgi_build_spool(VALUE rbkey, VALUE rbval, VALUE argv) {
struct uwsgi_buffer *ub = (struct uwsgi_buffer *) argv;
char *cur_buf = sa[0];
char *watermark = sa[1];
if (TYPE(rbkey) != T_STRING || TYPE(rbval) != T_STRING) {
if (TYPE(rbkey) != T_STRING) {
rb_raise(rb_eRuntimeError, "spool hash must contains only strings");
return ST_STOP;
}
char *key = RSTRING_PTR(rbkey); uint16_t keylen = RSTRING_LEN(rbkey);
char *val = RSTRING_PTR(rbval); uint16_t vallen = RSTRING_LEN(rbval);
if (cur_buf + (2+keylen+2+vallen) > watermark) {
rb_raise(rb_eRuntimeError, "spool hash size can be no more than 64K");
return ST_STOP;
if (TYPE(rbval) == T_STRING) {
char *val = RSTRING_PTR(rbval); uint16_t vallen = RSTRING_LEN(rbval);
if (uwsgi_buffer_append_keyval(ub, key, keylen, val, vallen)) {
rb_raise(rb_eRuntimeError, "error building the spool packet");
return ST_STOP;
}
}
else {
VALUE str = rb_any_to_s(rbval);
if (!str) {
rb_raise(rb_eRuntimeError, "error building the spool packet");
return ST_STOP;
}
char *val = RSTRING_PTR(str); uint16_t vallen = RSTRING_LEN(str);
if (uwsgi_buffer_append_keyval(ub, key, keylen, val, vallen)) {
rb_raise(rb_eRuntimeError, "error building the spool packet");
return ST_STOP;
}
}
*cur_buf++ = (uint8_t) (keylen & 0xff);
*cur_buf++ = (uint8_t) ((keylen >> 8) & 0xff);
memcpy(cur_buf, key, keylen); cur_buf += keylen;
*cur_buf++ = (uint8_t) (vallen & 0xff);
*cur_buf++ = (uint8_t) ((vallen >> 8) & 0xff);
memcpy(cur_buf, val, vallen); cur_buf += vallen;
// fix the ptr
sa[0] = cur_buf;
return ST_CONTINUE;
}
@@ -968,38 +968,12 @@ int rack_uwsgi_build_spool(VALUE rbkey, VALUE rbval, VALUE argv) {
static VALUE rack_uwsgi_send_spool(VALUE *class, VALUE args) {
char spool_filename[1024];
struct wsgi_request *wsgi_req = current_wsgi_req();
char *priority = NULL;
long numprio = 0;
time_t at = 0;
char *body = NULL;
size_t body_len= 0;
Check_Type(args, T_HASH);
// priority
#ifdef RUBY19
VALUE rbprio = rb_hash_lookup(args, rb_str_new2("priority"));
#else
VALUE rbprio = rb_hash_aref(args, rb_str_new2("priority"));
#endif
if (TYPE(rbprio) == T_FIXNUM) {
numprio = NUM2INT(rbprio);
rb_hash_delete(args, rb_str_new2("priority"));
}
// at
#ifdef RUBY19
VALUE rbat = rb_hash_lookup(args, rb_str_new2("at"));
#else
VALUE rbat = rb_hash_aref(args, rb_str_new2("at"));
#endif
if (TYPE(rbat) == T_FIXNUM) {
at = NUM2INT(rbat);
rb_hash_delete(args, rb_str_new2("at"));
}
// body
#ifdef RUBY19
VALUE rbbody = rb_hash_lookup(args, rb_str_new2("body"));
@@ -1012,31 +986,18 @@ static VALUE rack_uwsgi_send_spool(VALUE *class, VALUE args) {
rb_hash_delete(args, rb_str_new2("body"));
}
char *spool_buffer = uwsgi_malloc(UMAX16);
char *argv[2];
argv[0] = spool_buffer;
argv[1] = spool_buffer + UMAX16 ;
struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size);
rb_hash_foreach(args, rack_uwsgi_build_spool, (VALUE) argv);
rb_hash_foreach(args, rack_uwsgi_build_spool, (VALUE) ub);
if (numprio) {
priority = uwsgi_num2str((int)numprio);
}
char *filename = uwsgi_spool_request(wsgi_req, ub->buf, ub->pos, body, body_len);
int ret = spool_request(uwsgi.spoolers, spool_filename, (int)(uwsgi.workers[0].requests + 1), wsgi_req->async_id, spool_buffer, (int)(argv[0] - spool_buffer), priority, at, body, body_len);
uwsgi_buffer_destroy(ub);
if (priority) {
free(priority);
}
free(spool_buffer);
if (ret > 0) {
char *slash = uwsgi_get_last_char(spool_filename, '/');
if (slash) {
return rb_str_new2(slash+1);
}
return rb_str_new2(spool_filename);
if (filename) {
VALUE ret = rb_str_new2(filename);
free(filename);
return ret;
}
rb_raise(rb_eRuntimeError, "unable to spool job");
+7 -10
View File
@@ -1,4 +1,4 @@
#include "../../uwsgi.h"
#include <uwsgi.h>
/*
@@ -10,15 +10,11 @@ extern struct uwsgi_server uwsgi;
int uwsgi_request_spooler(struct wsgi_request *wsgi_req) {
int i;
char spool_filename[1024];
struct uwsgi_header uh;
// get the spooler from the modifier2
// TODO get the spooler from the modifier2
struct uwsgi_spooler *uspool = uwsgi.spoolers;
if (uspool == NULL) {
if (uwsgi.spoolers == NULL) {
uwsgi_log("the spooler is inactive !!!...skip\n");
uh.modifier1 = 255;
uh.pktsize = 0;
@@ -27,20 +23,21 @@ int uwsgi_request_spooler(struct wsgi_request *wsgi_req) {
return -1;
}
i = spool_request(uspool, spool_filename, uwsgi.workers[0].requests + 1, wsgi_req->async_id, wsgi_req->buffer, wsgi_req->uh->pktsize, NULL, 0, NULL, 0);
char *filename = uwsgi_spool_request(wsgi_req, wsgi_req->buffer, wsgi_req->uh->pktsize, NULL, 0);
uh.modifier1 = 255;
uh.pktsize = 0;
if (i > 0) {
if (filename) {
uh.modifier2 = 1;
if (uwsgi_response_write_body_do(wsgi_req, (char *) &uh, 4)) {
uwsgi_log("disconnected client, remove spool file.\n");
/* client disconnect, remove spool file */
if (unlink(spool_filename)) {
if (unlink(filename)) {
uwsgi_error("uwsgi_request_spooler()/unlink()");
uwsgi_log("something horrible happened !!! check your spooler ASAP !!!\n");
exit(1);
}
}
free(filename);
return 0;
}
else {
+2 -2
View File
@@ -2976,7 +2976,7 @@ void snmp_init(void);
void uwsgi_master_manage_snmp(int);
int spool_request(struct uwsgi_spooler *uspool, char *, int, int, char *, int, char *, time_t, char *, size_t);
char *uwsgi_spool_request(struct wsgi_request *, char *, size_t, char *, size_t);
void spooler(struct uwsgi_spooler *);
pid_t spooler_start(struct uwsgi_spooler *);
@@ -3615,7 +3615,7 @@ void *uwsgi_calloc_shared(size_t);
struct uwsgi_spooler *uwsgi_new_spooler(char *);
struct uwsgi_spooler *uwsgi_get_spooler_by_name(char *);
struct uwsgi_spooler *uwsgi_get_spooler_by_name(char *, size_t);
int uwsgi_zerg_attach(char *);