-- PostgreSQL connection pool using pgmoon local pgmoon = require("pgmoon") local DB_HOST = os.getenv("DB_HOST") or "localhost" local DB_PORT = tonumber(os.getenv("DB_PORT")) or 5432 local DB_NAME = os.getenv("DB_NAME") or "handheld_devices" local DB_USER = os.getenv("DB_USER") or "devices_user" local DB_PASSWORD = os.getenv("DB_PASSWORD") or "devices_password" local DB_POOL_SIZE = tonumber(os.getenv("DB_POOL_SIZE")) or 10 local DB_CONNECT_TIMEOUT_MS = tonumber(os.getenv("DB_CONNECT_TIMEOUT_MS")) or 5000 local DB_QUERY_TIMEOUT_MS = tonumber(os.getenv("DB_QUERY_TIMEOUT_MS")) or 10000 local config = { host = DB_HOST, port = tostring(DB_PORT), database = DB_NAME, user = DB_USER, password = DB_PASSWORD, socket_type = "luasocket", } local pool = { available = {}, in_use = {}, max_size = DB_POOL_SIZE, } local function create_connection() local pg = pgmoon.new(config) pg:settimeout(DB_CONNECT_TIMEOUT_MS) local ok, err = pg:connect() if not ok then return nil, err end pg:settimeout(DB_QUERY_TIMEOUT_MS) return pg end local function get_connection() local conn = table.remove(pool.available) if conn then table.insert(pool.in_use, conn) return conn end if #pool.in_use >= pool.max_size then return nil, "connection pool exhausted" end local pg, err = create_connection() if not pg then return nil, err end table.insert(pool.in_use, pg) return pg end local function release_connection(conn) for i, c in ipairs(pool.in_use) do if c == conn then table.remove(pool.in_use, i) if #pool.available < pool.max_size then table.insert(pool.available, conn) else pcall(function() conn:disconnect() end) end return end end end -- Execute with connection from pool; auto-release on return local function with_connection(fn) local conn, err = get_connection() if not conn then return nil, err end local ok, result, result_err = pcall(function() return fn(conn) end) release_connection(conn) if not ok then return nil, result end return result, result_err end -- Retry with exponential backoff local function with_retry(fn, max_attempts) max_attempts = max_attempts or 3 local attempt = 0 local last_err while attempt < max_attempts do attempt = attempt + 1 local result, err = fn() if result ~= nil or (err and not (err:match("connection") or err:match("timeout"))) then return result, err end last_err = err if attempt < max_attempts then local delay = math.min(2 ^ attempt * 100, 5000) require("socket").sleep(delay / 1000) end end return nil, last_err end local function ping() return with_connection(function(conn) local res, err = conn:query("SELECT 1") if res and type(res) == "table" and (res[1] or #res >= 1) then return true end return nil, err or "ping failed" end) end return { config = config, get_connection = get_connection, release_connection = release_connection, with_connection = with_connection, with_retry = with_retry, ping = ping, }