Python: WSGI environment copying moved out of request processing.

The WSGI environment dictionary contains a number of static items, that are
pre-initialized on application start.  Then it's copied for each request to be
filled with request-related data.

Now this dictionary copy operation will be done between processing of requests,
which should save some CPU cycles during request processing and thus reduce
response latency for non-peak load periods.
This commit is contained in:
Valentin Bartenev
2020-12-14 17:15:49 +03:00
parent 1e9def50c8
commit 8d65a3303b

View File

@@ -59,6 +59,7 @@ static void nxt_python_wsgi_done(void);
static void nxt_python_request_handler(nxt_unit_request_info_t *req); static void nxt_python_request_handler(nxt_unit_request_info_t *req);
static PyObject *nxt_python_create_environ(nxt_python_app_conf_t *c); static PyObject *nxt_python_create_environ(nxt_python_app_conf_t *c);
static PyObject *nxt_python_copy_environ(nxt_unit_request_info_t *req);
static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx); static PyObject *nxt_python_get_environ(nxt_python_ctx_t *pctx);
static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name, static int nxt_python_add_sptr(nxt_python_ctx_t *pctx, PyObject *name,
nxt_unit_sptr_t *sptr, uint32_t size); nxt_unit_sptr_t *sptr, uint32_t size);
@@ -221,6 +222,7 @@ nxt_python_wsgi_ctx_data_alloc(void **pdata)
} }
pctx->write = NULL; pctx->write = NULL;
pctx->environ = NULL;
pctx->start_resp = PyCFunction_New(nxt_py_start_resp_method, pctx->start_resp = PyCFunction_New(nxt_py_start_resp_method,
(PyObject *) pctx); (PyObject *) pctx);
@@ -237,6 +239,11 @@ nxt_python_wsgi_ctx_data_alloc(void **pdata)
goto fail; goto fail;
} }
pctx->environ = nxt_python_copy_environ(NULL);
if (nxt_slow_path(pctx->environ == NULL)) {
goto fail;
}
*pdata = pctx; *pdata = pctx;
return NXT_UNIT_OK; return NXT_UNIT_OK;
@@ -258,6 +265,7 @@ nxt_python_wsgi_ctx_data_free(void *data)
Py_XDECREF(pctx->start_resp); Py_XDECREF(pctx->start_resp);
Py_XDECREF(pctx->write); Py_XDECREF(pctx->write);
Py_XDECREF(pctx->environ);
Py_XDECREF(pctx); Py_XDECREF(pctx);
} }
@@ -295,6 +303,7 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
int rc; int rc;
PyObject *environ, *args, *response, *iterator, *item; PyObject *environ, *args, *response, *iterator, *item;
PyObject *close, *result; PyObject *close, *result;
nxt_bool_t prepare_environ;
nxt_python_ctx_t *pctx; nxt_python_ctx_t *pctx;
pctx = req->ctx->data; pctx = req->ctx->data;
@@ -305,6 +314,19 @@ nxt_python_request_handler(nxt_unit_request_info_t *req)
PyEval_RestoreThread(pctx->thread_state); PyEval_RestoreThread(pctx->thread_state);
if (nxt_slow_path(pctx->environ == NULL)) {
pctx->environ = nxt_python_copy_environ(req);
if (pctx->environ == NULL) {
prepare_environ = 0;
rc = NXT_UNIT_ERROR;
goto done;
}
}
prepare_environ = 1;
environ = nxt_python_get_environ(pctx); environ = nxt_python_get_environ(pctx);
if (nxt_slow_path(environ == NULL)) { if (nxt_slow_path(environ == NULL)) {
rc = NXT_UNIT_ERROR; rc = NXT_UNIT_ERROR;
@@ -418,6 +440,14 @@ done:
pctx->req = NULL; pctx->req = NULL;
nxt_unit_request_done(req, rc); nxt_unit_request_done(req, rc);
if (nxt_fast_path(prepare_environ)) {
PyEval_RestoreThread(pctx->thread_state);
pctx->environ = nxt_python_copy_environ(NULL);
pctx->thread_state = PyEval_SaveThread();
}
} }
@@ -531,6 +561,23 @@ fail:
} }
static PyObject *
nxt_python_copy_environ(nxt_unit_request_info_t *req)
{
PyObject *environ;
environ = PyDict_Copy(nxt_py_environ_ptyp);
if (nxt_slow_path(environ == NULL)) {
nxt_unit_req_alert(req,
"Python failed to copy the \"environ\" dictionary");
nxt_python_print_exception();
}
return environ;
}
static PyObject * static PyObject *
nxt_python_get_environ(nxt_python_ctx_t *pctx) nxt_python_get_environ(nxt_python_ctx_t *pctx)
{ {
@@ -540,16 +587,6 @@ nxt_python_get_environ(nxt_python_ctx_t *pctx)
nxt_unit_field_t *f, *f2; nxt_unit_field_t *f, *f2;
nxt_unit_request_t *r; nxt_unit_request_t *r;
environ = PyDict_Copy(nxt_py_environ_ptyp);
if (nxt_slow_path(environ == NULL)) {
nxt_unit_req_error(pctx->req,
"Python failed to copy the \"environ\" dictionary");
return NULL;
}
pctx->environ = environ;
r = pctx->req->request; r = pctx->req->request;
#define RC(S) \ #define RC(S) \
@@ -628,7 +665,7 @@ nxt_python_get_environ(nxt_python_ctx_t *pctx)
#undef RC #undef RC
if (nxt_slow_path(PyDict_SetItem(environ, nxt_py_wsgi_input_str, if (nxt_slow_path(PyDict_SetItem(pctx->environ, nxt_py_wsgi_input_str,
(PyObject *) pctx) != 0)) (PyObject *) pctx) != 0))
{ {
nxt_unit_req_error(pctx->req, nxt_unit_req_error(pctx->req,
@@ -636,11 +673,15 @@ nxt_python_get_environ(nxt_python_ctx_t *pctx)
goto fail; goto fail;
} }
environ = pctx->environ;
pctx->environ = NULL;
return environ; return environ;
fail: fail:
Py_DECREF(environ); Py_DECREF(pctx->environ);
pctx->environ = NULL;
return NULL; return NULL;
} }