[resty/sakura.lua]
local tcp = ngx.socket.tcp
local strbyte = string.byte
local bit = require "bit"
local bor = bit.bor
local lshift = bit.lshift
local band = bit.band
local rshift = bit.rshift
local strchar = string.char
local setmetatable = setmetatable
local cjson = require "cjson.safe"
local _M = {}
local SOC_FILENAME = "/var/run/soc"
local DB_NAME = "localhost:1521/orclcdb"
local STATE_CONNECTED = 1
local STATE_COMMAND_SENT = 2
local mt = { __index = _M }
local function _set_byte4(n)
return strchar(band(n, 0xff),
band(rshift(n, 8), 0xff),
band(rshift(n, 16), 0xff),
band(rshift(n, 24), 0xff))
end
local function _get_byte4(data)
local a, b, c, d = strbyte(data, 1, 4)
return bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24))
end
local function _trim(s)
return s:match'^%s*(.*%S)' or ''
end
function _M.new(self)
local soc, err = tcp()
if not soc then
return nil, "Error when open soc=>"..(err or "NULL")
end
return setmetatable({ soc = soc }, mt)
end
function _M.set_timeout(self, timeout)
local soc = self.soc
if not soc then
return false, "soc not initialized"
end
return soc:settimeout(timeout)
end
function _M.connect(self, opts)
local soc = self.soc
if not soc then
return false, "soc not initialized"
end
self.database = opts.database or DB_NAME
self.user = opts.user or ""
self.password = opts.password or ""
local pool = opts.pool
if not pool then
pool = self.user .. ":" .. self.database .. ":" .. SOC_FILENAME
end
local ok; local err
ok, err = soc:connect("unix:"..SOC_FILENAME, {pool = pool,
pool_size = opts.pool_size,
backlog = opts.backlog })
if not ok then
return false, "Error when connect to soc=>"..(err or "null")
end
local reused = soc:getreusedtimes()
if reused and reused > 0 then
self.state = STATE_CONNECTED
return true, nil
end
local con_string = self.user.."/"..self.password.."@"..self.database
ok, err = soc:send("SOC".._set_byte4(#con_string)..con_string)
if not ok then
soc:close()
return false, "Error when send into soc=>"..(err or "null")
end
--socket answer may be (or may be not) deleted due notable memory leak problem under highload by wrk (e.g. 3 cycles with -c500 -d30s) that generate soket errors
--for example - Socket errors: connect 0, read 0, write 0, timeout 165
--without answer a litle bit memory leak also takes place -:((
--this problem needs additional reserch effort
local status; local len; local len_num; local data
status, err = soc:receive(3); if not status then soc:close(); return false, "Error when recieve status from soc=>"..(err or "null") end
len, err = soc:receive(4); if not len then soc:close(); return false, "Error when recieve len from soc=>"..(err or "null") end
len_num = _get_byte4(len)
data, err = soc:receive(len_num); if not data then soc:close(); return false, "Error when recieve packet from soc=>"..(err or "null") end
if (status ~= 'SOO') then soc:close(); return false, "FAILED=>"..(data or "null") end
self.state = STATE_CONNECTED
return true, nil
end
function _M.disconnect(self)
local soc = self.soc
if not soc then
return false, "soc not initialized"
end
local ok; local err
self.state = nil
ok, err = soc:send("SOD")
if not ok then
soc:close()
return false, "Error when send into soc=>"..(err or "null")
end
local status; local len; local len_num; local data
status, err = soc:receive(3); if not status then soc:close(); return false, "Error when recieve status from soc=>"..(err or "null") end
len, err = soc:receive(4); if not len then soc:close(); return false, "Error when recieve len from soc=>"..(err or "null") end
len_num = _get_byte4(len)
data, err = soc:receive(len_num); if not data then soc:close(); return false, "Error when recieve packet from soc=>"..(err or "null") end
if (status ~= 'SOO') then soc:close(); return false, "FAILED=>"..(data or "null") end
ok, err = soc:close()
if not ok then
return false, "Error when close soc=>"..(err or "null")
end
return true, nil
end
function _M.set_keepalive(self, ...)
local soc = self.soc
if not soc then
return false, "soc not initialized"
end
if self.state ~= STATE_CONNECTED then
return false, "soc cannot be reused in the current connection state=>".. (self.state or "NULL")
end local ok; local err
self.state = nil
return soc:setkeepalive(...)
end
function _M.query(self, query_string)
local soc = self.soc
if not soc then
return false, "soc not initialized"
end
if self.state ~= STATE_CONNECTED then
return false, "soc cannot be reused in the current connection state=>".. (self.state or "NULL")
end
if not query_string then
return false, "NULL Query"
end
query_string = _trim(query_string)
local query_typ
if (string.upper(string.sub(query_string,1,6)) == "SELECT") then query_typ = 'SOS' else query_typ = 'SOQ' end
local ok; local err
ok, err = soc:send(query_typ.._set_byte4(#query_string)..query_string)
if not ok then
soc:close()
return false, "Error when send into soc=>"..(err or "null")
end
self.state = STATE_COMMAND_SENT
local status; local len; local len_num; local data
status, err = soc:receive(3); if not status then soc:close(); return false, "Error when recieve status from soc=>"..(err or "null") end
len, err = soc:receive(4); if not len then soc:close(); return false, "Error when recieve len from soc=>"..(err or "null") end
len_num = _get_byte4(len)
data, err = soc:receive(len_num); if not data then soc:close(); return false, "Error when recieve packet from soc=>"..(err or "null") end
if (status ~= 'SOO') then soc:close(); return false, "FAILED=>"..(data or "null") end
self.state = STATE_CONNECTED
if query_typ == 'SOS' then
local data_t = cjson.decode(data)
return data_t, nil
else
return true, nil
end
end
function _M.commit(self)
local soc = self.soc
if not soc then
return false, "soc not initialized"
end
local ok; local err
ok, err = soc:send("SOT")
if not ok then
soc:close()
return false, "Error when send into soc=>"..(err or "null")
end
local status; local len; local len_num; local data
status, err = soc:receive(3); if not status then soc:close(); return false, "Error when recieve status from soc=>"..(err or "null") end
len, err = soc:receive(4); if not len then soc:close(); return false, "Error when recieve len from soc=>"..(err or "null") end
len_num = _get_byte4(len)
data, err = soc:receive(len_num); if not data then soc:close(); return false, "Error when recieve packet from soc=>"..(err or "null") end
if (status ~= 'SOO') then soc:close(); return false, "FAILED=>"..(data or "null") end
return true, nil
end
function _M.rollback(self)
local soc = self.soc
if not soc then
return false, "soc not initialized"
end
local ok; local err
ok, err = soc:send("SOR")
if not ok then
soc:close()
return false, "Error when send into soc=>"..(err or "null")
end
local status; local len; local len_num; local data
status, err = soc:receive(3); if not status then soc:close(); return false, "Error when recieve status from soc=>"..(err or "null") end
len, err = soc:receive(4); if not len then soc:close(); return false, "Error when recieve len from soc=>"..(err or "null") end
len_num = _get_byte4(len)
data, err = soc:receive(len_num); if not data then soc:close(); return false, "Error when recieve packet from soc=>"..(err or "null") end
if (status ~= 'SOO') then soc:close(); return false, "FAILED=>"..(data or "null") end
return true, nil
end
return _M
sdmrnv, 2022-09-04 [0.792ms, s]
