homme.io
Clean.Precise.Quick.
..
[env]
[sources]
[Makefile]
[sakura.service]
[C client]
[resty/sakura.lua]
[lua client]
Performance test
[2do list]
Моно-но аварэ
Сакура в японской культуре

[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.535ms, s]