lua handler aborted: runtime error: table overflow

630 views
Skip to first unread message

Zhaohao Liang

unread,
Jul 18, 2012, 7:33:50 AM7/18/12
to open...@googlegroups.com
使用openresty+luajit+ffi调用一个C库,请求多了后返回500,error_log显示:

2012/07/18 18:39:58 [error] 20154#0: *33286 lua handler aborted: runtime error: table overflow
stack traceback:
        [C]: in function 'cdef'
        .../libsrc/ngx_ffi.lua:13: in function <...libsrc/ngx_ffi.lua:1>, client: 127.0.0.1, server: 127.0.0.1, request: .....

ngx_ffi.lua的代码如下:

local ngx = require("ngx")
local ffi = require("ffi")
 
ffi.cdef[[
typedef struct { void* ev_ptr; void* char_ptr; } bif_result;
bif_result* lib_search(const char* indexname, const char* key, const char* start, const char* end, const char* datatype, const char* rowstart, const char* pa
gesize);
void free_bif_result(bif_result* ptr);
]]
 
local function get_char(s)
    if s == nil then
        return nil
    end
    char_s = ffi.new("char[?]", #s+1)
    ffi.copy(char_s, s)
    return char_s
end
 
local lsearch = ffi.load('lsearch')
local r =  lsearch.lib_search(get_char(ngx.var.indexname), get_char(ngx.var.key), get_char(ngx.var.start), get_char(ngx.var.d_end), get_char(ngx.var.datatype
), nil, nil)
local res = ffi.string(r.char_ptr)
ngx.say(res)
lsearch.free_bif_result(r)
 
ngx = nil
ffi = nil
 
请问这个是lua层的问题还是C库的问题呢?

ps:C库的确有一点内存泄漏,每一个请求16byte。

环境:
CentOS release 5.8 64bit
openresty 1.0.15.10
luajit为openresty自带的LuaJIT 2.0.0-beta10

agentzh

unread,
Jul 18, 2012, 3:00:47 PM7/18/12
to open...@googlegroups.com
Hello!

On Wed, Jul 18, 2012 at 4:33 AM, Zhaohao Liang <lian...@gmail.com> wrote:
> 使用openresty+luajit+ffi调用一个C库,请求多了后返回500,error_log显示:
>
>> 2012/07/18 18:39:58 [error] 20154#0: *33286 lua handler aborted: runtime
>> error: table overflow
>> stack traceback:
>> [C]: in function 'cdef'
>> .../libsrc/ngx_ffi.lua:13: in function <...libsrc/ngx_ffi.lua:1>,
>> client: 127.0.0.1, server: 127.0.0.1, request: .....
>
>
> ngx_ffi.lua的代码如下:
>
>> local ngx = require("ngx")
>> local ffi = require("ffi")
>>
>>
>>
>> ffi.cdef[[
>> typedef struct { void* ev_ptr; void* char_ptr; } bif_result;
>> bif_result* lib_search(const char* indexname, const char* key, const char*
>> start, const char* end, const char* datatype, const char* rowstart, const
>> char* pa
>> gesize);
>> void free_bif_result(bif_result* ptr);
>> ]]
>>

貌似你每请求都调用 ffi.cdef?建议把 ngx_ffi.lua 实现为一个真正的 Lua 模块,这样就可以享受只加载一次的好处,见:

http://wiki.nginx.org/HttpLuaModule#Data_Sharing_within_an_Nginx_Worker

你遇见的 table overflow 的错误很容易通过下面这个独立的 Lua 脚本复现:

-- test.lua
local ffi = require "ffi"
while true do


ffi.cdef[[
typedef struct { void* ev_ptr; void* char_ptr; } bif_result;

]]
end

命令行上的执行结果如下:

$ /usr/local/openresty/luajit/bin/luajit-2.0.0-beta10 test.lua
/usr/local/openresty/luajit/bin/luajit-2.0.0-beta10: table overflow


stack traceback:
[C]: in function 'cdef'

a.lua:6: in main chunk
[C]: ?

所以只要把 ffi.cdef 移出循环就不会溢出了。(而在你的场景中,则是把 ffi.cdef 调用移进 Lua 模块加载代码。)

可以参考一下我的 lua-resty-string 库中的 resty.md5 模块的实现:

https://github.com/agentzh/lua-resty-string/blob/master/lib/resty/md5.lua

Best regards,
-agentzh

Zhaohao Liang

unread,
Jul 19, 2012, 9:22:20 AM7/19/12
to open...@googlegroups.com
已经按照帮助解决,感谢非常详尽的解释!

在 2012年7月19日星期四UTC+8上午3时00分47秒,agentzh写道:

lan hust

unread,
Apr 26, 2025, 4:36:57 AMApr 26
to openresty
@Zhaohao Liang 请问这个是怎么解决的?
我将自定义的c函数封装成lua模块 调用仍然出现了table overflow

openresty的lua代码:
包含在nginx.conf中的cdnlog.lua:

...
attr= require("attr_api")
function check_request_k(key) 
    attr.attr_api(12345, 1)
end
check_request_k(ngx.req.get_uri_args()['k'])
...

attr_api.lua文件:

local ffi = require "ffi"
local _M = { _VERSION = "0.1" }
ffi.cdef[[
    int Attr_API(int attr, int iValue);            
]]
local myffi = ffi.load("/usr/servers/nginx/conf/lua/libAttr_API.so")
    local ok, err = pcall(myffi.Attr_API, id, value)
    if not ok then
        ngx.log(ngx.ERR, "Attr_API call failed: ", err)
    end
end

return _M

Message has been deleted

lan hust

unread,
May 1, 2025, 9:32:27 PMMay 1
to open...@googlegroups.com
 i make a simple test for more times ,it dosn't appear and works correctly , surprising

file  main.lua:
local attr= require("attr_api")
local times=1000000000
local a = 0
while a < times do
    attr.attr_api(33712153 , 1)
    a = a+1
end
print("finish:", a)



file attr_api.lua:

local ffi = require "ffi"

local _M = { _VERSION = "0.1" }

ffi.cdef[[
    int Attr_API(int attr, int iValue);            
]]

local myffi = ffi.load("/usr/servers/nginx/conf/lua/libAttr_API.so", global)

function _M.attr_api(id, value)
    if not myffi or not myffi.Attr_API then
        print("Load attr_api failed: ")
        return
    end

    local ok, err = pcall(myffi.Attr_API, id, value)
    if not ok then
        print("Attr_API call failed: ", err)
    end
end

return _M

lan hust <hustl...@gmail.com> 于2025年4月26日周六 16:36写道:
--
--
邮件来自列表“openresty”,专用于技术讨论!
订阅: 请发空白邮件到 openresty...@googlegroups.com
发言: 请发邮件到 open...@googlegroups.com
退订: 请发邮件至 openresty+...@googlegroups.com
归档: http://groups.google.com/group/openresty
官网: http://openresty.org/
仓库: https://github.com/agentzh/ngx_openresty
教程: http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html
---
您收到此邮件是因为您订阅了Google群组上“openresty”群组中的主题。
要退订此主题,请访问https://groups.google.com/d/topic/openresty/zGxwOqUN4fc/unsubscribe
要退订此群组及其所有主题,请发送电子邮件到openresty+...@googlegroups.com
如需查看此讨论,请访问 https://groups.google.com/d/msgid/openresty/24ad9c9e-9a3a-4ce4-a461-1717b1dbecc4n%40googlegroups.com

lan hust

unread,
May 7, 2025, 10:24:35 PMMay 7
to open...@googlegroups.com
非常感谢你的建议,确实,在粘贴attr_api.lua 文件内容时,漏了 function _M.attr_api(id, value)的函数定义。
建议中的1和2已经修改,3你说包装有问题,pcall(myffi.Attr_API, attr, value) 这样的包装 也是可以捕捉myffi.Attr_API函数错误的吧。

后面1-4的建议,我再尝试,确实是多个worker同时进行,非常感谢你的建议。

Zion Goog <lian...@gmail.com> 于2025年4月30日周三 15:19写道:
貌似你的attr_api.lua不完整,是否缺少一些函数包装?

4o reply:

你遇到的 table overflow 错误通常是由于在每个请求中重复调用 ffi.cdef 导致的。LuaJIT 的 FFI 模块会将每次的 cdef 声明存储在内部表中,重复声明相同的结构或函数会导致该表迅速膨胀,最终触发 table overflow 错误。

问题分析

在你的 attr_api.lua 文件中,ffi.cdef 被放置在模块的顶层,这本身是正确的做法。然而,你提到仍然出现 table overflow 错误,可能是由于以下原因之一:

  1. 模块未被正确缓存:如果在每个请求中都重新加载 attr_api.lua 模块(例如使用 dofile 或未正确使用 require),会导致 ffi.cdef 被多次执行。

  2. 模块定义不规范:如果 attr_api.lua 没有正确返回模块表,或者模块的结构不规范,可能会导致 Lua 的模块缓存机制失效,从而在每次请求时重新加载模块。

解决方案

为避免 table overflow 错误,建议按照以下方式组织你的代码:

attr_api.lua

local ffi = require "ffi"

ffi.cdef[[
int Attr_API(int attr, int iValue);
]]

local lib = ffi.load("/usr/servers/nginx/conf/lua/libAttr_API.so")

local _M = {}

function _M.attr_api(attr, value)
    local ok, err = pcall(lib.Attr_API, attr, value)
    if not ok then
        ngx.log(ngx.ERR, "Attr_API call failed: ", err)
    end
end

return _M

cdnlog.lua

local attr = require("attr_api")

local args = ngx.req.get_uri_args()
local k = args["k"]

if k then
    attr.attr_api(12345, 1)
end

请确保:

  • 使用 require 加载模块require 会缓存模块,避免重复加载。

  • 模块返回模块表attr_api.lua 应该返回一个表(如 _M),以供其他模块使用。

  • 避免在请求处理函数中调用 ffi.cdefffi.cdef 应该只在模块加载时调用一次。

其他建议

  • 避免重复声明:确保没有在多个模块中重复声明相同的 C 函数或结构。

  • 模块化管理 C 函数声明:将所有的 ffi.cdef 声明集中在一个模块中,供其他模块引用。

  • 使用 resty.core:在 init_worker_by_lua_block 中加载 resty.core,可以提高性能并减少内存使用。 (a problem with lua ffi in openresty · Issue #1562 - GitHub)

通过上述方法,可以有效避免 table overflow 错误,并提高 OpenResty 应用的稳定性和性能。


Claude-3.7-Sonnet reply:

你的模块结构存在一些问题,这可能是导致 table overflow 的原因。主要问题点和解决方案如下:

  1. attr_api.lua 文件中的函数定义不完整 - 没有定义 attr_api 函数,但在 cdnlog.lua 中却调用了它。

  2. ffi.cdef 应该只在模块加载时执行一次,但当前实现可能导致它被重复执行。

  3. pcall 的使用方式有误,没有正确包装函数调用。

以下是修正后的 attr_api.lua 文件:

local ffi = require "ffi"

-- 正确定义模块
local _M = { _VERSION = "0.1" }

-- 仅在模块加载时执行一次
ffi.cdef[[
    int Attr_API(int attr, int iValue);            
]]

-- 加载C库,只需执行一次
local myffi = ffi.load("/usr/servers/nginx/conf/lua/libAttr_API.so")

-- 正确定义模块中的函数
function _M.attr_api(id, value)
    local ok, err = pcall(function() 
        return myffi.Attr_API(id, value)
    end)
    
    if not ok then
        ngx.log(ngx.ERR, "Attr_API call failed: ", err)
    end
    
    return ok
end

return _M

此外,还有几点建议:

  1. 确保模块只被加载一次。在 Nginx 配置中,可以使用 init_by_lua_blockinit_by_lua_file 来预加载模块:
init_by_lua_block {
    require("attr_api")
}
  1. 检查是否有多个 worker 进程同时运行,每个进程都会加载一次模块,但这是正常的。

  2. 确保 libAttr_API.so 库的路径正确且文件存在。

  3. 考虑添加错误处理和日志记录,特别是在 C 函数调用失败时。

这个问题很可能是由于 FFI 的缓存机制和模块加载问题导致的。正确实现模块结构后,应该能解决 table overflow 的问题。



Thanks

Liang Zhaohao


lan hust <hustl...@gmail.com> 于2025年4月26日周六 16:36写道:
@Zhaohao Liang 请问这个是怎么解决的?
--
您收到此邮件是因为您订阅了Google群组上的“openresty”群组。
要退订此群组并停止接收此群组的电子邮件,请发送电子邮件到openresty+...@googlegroups.com
如需查看此讨论,请访问 https://groups.google.com/d/msgid/openresty/CAMs7pcc%2B%3Doib9N%2Bv67rSmmwC4zx%2B0AMm82pt5MiBf--KzEynVw%40mail.gmail.com
Reply all
Reply to author
Forward
0 new messages