proxy-pool/internal/adapters/redisactivity/scripts/worker_snapshot.lua
youfak 51fef78368
Some checks are pending
ci / proto (push) Waiting to run
ci / test (ubuntu-latest) (push) Waiting to run
ci / test (windows-latest) (push) Waiting to run
ci / race (push) Waiting to run
ci / integration (push) Waiting to run
feat: build worker proxy snapshots from ownership
2026-07-31 14:27:08 +08:00

76 lines
2.9 KiB
Lua

local records_key = KEYS[1]
local owners_key = KEYS[2]
local worker_owned_key = KEYS[3]
local worker_id = ARGV[1]
local limit = tonumber(ARGV[2])
local function reply(status, proxies)
if not proxies or #proxies == 0 then
return '{"status":' .. cjson.encode(status) .. ',"proxies":[]}'
end
return cjson.encode({status = status, proxies = proxies})
end
local function now_ms()
local value = redis.call('TIME')
return tonumber(value[1]) * 1000 + math.floor(tonumber(value[2]) / 1000)
end
local function decode_table(value)
if not value then
return nil
end
local ok, decoded = pcall(cjson.decode, value)
if not ok or type(decoded) ~= 'table' then
return nil
end
return decoded
end
local function valid_assignment(value, now)
return value and value.version == 1 and type(value.proxyId) == 'string' and value.proxyId ~= '' and
type(value.workerId) == 'string' and value.workerId == worker_id and type(value.epoch) == 'number' and
value.epoch > 0 and type(value.expiresAtMs) == 'number' and value.expiresAtMs > now and
type(value.draining) == 'boolean' and not value.draining and
type(value.workerIndexKey) == 'string' and value.workerIndexKey == worker_owned_key
end
local function valid_record(value, proxy_id, now)
return value and value.version == 1 and value.id == proxy_id and value.state == 'AVAILABLE' and
value.ownerWorkerId == worker_id and type(value.scheme) == 'string' and
(value.scheme == 'http' or value.scheme == 'https' or value.scheme == 'socks5') and
type(value.host) == 'string' and value.host ~= '' and type(value.port) == 'number' and
value.port > 0 and value.port <= 65535 and type(value.sourceUpstream) == 'string' and
value.sourceUpstream ~= '' and type(value.expiresAtMs) == 'number' and value.expiresAtMs > now and
type(value.usableUntilMs) == 'number' and value.usableUntilMs > now and
type(value.maxConcurrency) == 'number' and value.maxConcurrency > 0
end
if type(worker_id) ~= 'string' or worker_id == '' or not limit or limit <= 0 then
return reply('invalid')
end
if redis.call('ZCARD', worker_owned_key) > limit then
return reply('unavailable')
end
local now = now_ms()
local ids = redis.call('ZRANGEBYSCORE', worker_owned_key, now + 1, '+inf', 'LIMIT', 0, limit)
local proxies = cjson.decode('[]')
for _, proxy_id in ipairs(ids) do
local assignment = decode_table(redis.call('HGET', owners_key, proxy_id))
local raw_record = redis.call('HGET', records_key, proxy_id)
local record = decode_table(raw_record)
if valid_assignment(assignment, now) and valid_record(record, proxy_id, now) then
proxies[#proxies + 1] = {
record = raw_record,
ownershipEpoch = tostring(assignment.epoch),
leaseExpiresAtMs = assignment.expiresAtMs,
}
else
redis.call('ZREM', worker_owned_key, proxy_id)
end
end
return reply('ok', proxies)