ngx.ctx inheirt
tokers opened this issue · 13 comments
We use the magic ngx.ctx for storing something, but when internel redirect is happening, ngx.ctx will be dereferenced and the gc will recycle it, so most of the time we have to use ngx.var.VARIABLE, but the cost is expensive especially when the QPS is high. My problem is, why not add a flag to control whether inherits the origin ngx.ctx when ngx.exec is invoked. 😃
@tokers The problem is that the nginx core clears all modules' ctx data upon internal redirects. We'll need special hacks to work around that.
@agentzh Thanks for your reply! Is there any other compromise way which is better than the ngx.var.VARIABLE .
@tokers I think maybe we can abuse a special custom nginx variable created by this module under the hood (on the C land) and use it to hold the pointer to ngx.ctx, for example. Mind to submit a patch?
@agentzh I hacked the limits of ngx.ctx when ngx.exec is invoking on the Lua hand.
Here is the demo.
ngx_ctx_bypassing.lua
local debug = require "debug"
local ffi = require "ffi"
local base = require "resty.core.base"
local C = ffi.C
local registry = debug.getregistry()
local tostring = tostring
local tonumber = tonumber
local _M = {}
ffi.cdef[[
int ngx_http_lua_ffi_set_ctx_ref(ngx_http_request_t *r, int ref);
]]
function _M.stash_ngx_ctx()
local ctxs = registry.ngx_lua_ctx_tables
local ctx_ref = base.ref_in_table(ctxs, ngx.ctx)
local r = getfenv(0).__ngx_req
if not r then
return error("no request found")
end
if C.ngx_http_lua_ffi_set_ctx_ref(r, ctx_ref) ~= base.FFI_OK then
ngx.log(ngx.DEBUG, "stash the old ngx.ctx failed")
end
ngx.var.ctx_ref = tostring(ctx_ref)
end
function _M.apply_ngx_ctx()
local ctx_ref = tonumber(ngx.var.ctx_ref)
if not ctx_ref then
return
end
local ctxs = registry.ngx_lua_ctx_tables
local origin_ngx_ctx = ctxs[ctx_ref]
ngx.ctx = origin_ngx_ctx
local FREE_LIST_REF = 0
ctxs[ctx_ref] = ctxs[FREE_LIST_REF]
ctxs[FREE_LIST_REF] = ctx_ref
ngx.var.ctx_ref = ""
end
return _Mlocation conf
location /t1 {
content_by_lua_block {
local bypass_ngx_ctx = require "ngx_ctx_bypassing"
ngx.ctx = {
Date = "Wed May 3 15:18:04 CST 2017",
Site = "unknown"
}
bypass_ngx_ctx.stash_ngx_ctx()
ngx.exec("/t2")
}
}
location /t2 {
content_by_lua_block {
local bypass_ngx_ctx = require "ngx_ctx_bypassing"
ngx.ctx = {
Date = "Wed May 3 15:18:04 CST 2017",
Site = "unknown"
}
bypass_ngx_ctx.apply_ngx_ctx()
ngx.say("Date: " .. ngx.ctx["Date"] .. " Site: " .. ngx.ctx["Site"])
}
}My lua-nginx-module version is v0.10.7, Nginx version is 1.11.2, lua-resty-core version is v0.1.9.
It works well on my local machine. Is there anything improper?
Would be highly interested in this option as well.
@tokers Yeah, your workaround looks good to me. I didn't realize it can be done all in Lua. This is clever.
@tokers I think the definition of $ctx_ref is missing form your example. Some global switch to turn this on without having to do this manually every time using ngx.exec and set the variable in every server {} directive would be appreciatied.
For example i am doing some logging with log_by_lua in an error_page directive (triggers internal redirect), collecting some data from ngx.ctx.
@tokers Yeah, we should not override the existing ctx table ref. We just need to anchor the ngx.ctx table somewhere (like in a custom lrucache store to avoid potential memory leaks).
Maybe we could introduce get_ctx_ref and set_ctx_ref APIs, and let the caller decide where to store the reference.
@spacewander Frankly I don't really want to expose such implementation details to the user API level. It makes future implementation changes much harder if not impossible.