import { randomId } from './crypto.js'; import { key } from './redisClient.js'; const QUEUE_KEY = key('queue:zset'); const QUEUE_LOCK_KEY = key('queue:lock'); export async function enqueue(redis, userId) { const score = Date.now(); await redis.zAdd(QUEUE_KEY, [{ score, value: String(userId) }]); return redis.zCard(QUEUE_KEY); } export async function leaveQueue(redis, userId) { return redis.zRem(QUEUE_KEY, String(userId)); } export async function getQueueSize(redis) { return redis.zCard(QUEUE_KEY); } export async function dequeuePair(redis) { // Simple lock to reduce thundering herd when multi-instance const lockVal = randomId('lock_'); const locked = await redis.set(QUEUE_LOCK_KEY, lockVal, { NX: true, PX: 200 }); if (!locked) return null; try { const ids = await redis.zRange(QUEUE_KEY, 0, -1); if (!ids || ids.length < 2) return null; const firstIndex = Math.floor(Math.random() * ids.length); let secondIndex = Math.floor(Math.random() * (ids.length - 1)); if (secondIndex >= firstIndex) secondIndex += 1; const picked = [ids[firstIndex], ids[secondIndex]]; await redis.zRem(QUEUE_KEY, picked[0], picked[1]); return [Number(picked[0]), Number(picked[1])]; } finally { // best-effort unlock const cur = await redis.get(QUEUE_LOCK_KEY); if (cur === lockVal) await redis.del(QUEUE_LOCK_KEY); } }