package redisstate // releaseAllByUserScript atomically clears every registered, reservation, // and pending_registration binding owned by one user. Inputs: // // KEYS[1] — user_registered set key // KEYS[2] — user_reservations set key // KEYS[3] — pending_index sorted-set key // ARGV[1] — Lobby Redis key prefix (e.g. "lobby:") // // The script returns a three-entry table `{registeredCount, // reservationsTotal, pendingCount}` so callers can emit telemetry without // a second round-trip. reservationsTotal includes both reserved and // pending_registration entries; pendingCount is the pending-only subset. const releaseAllByUserScript = ` local userRegisteredKey = KEYS[1] local userReservationsKey = KEYS[2] local pendingIndexKey = KEYS[3] local prefix = ARGV[1] local registered = redis.call('SMEMBERS', userRegisteredKey) for _, canonical in ipairs(registered) do redis.call('DEL', prefix .. 'race_names:registered:' .. canonical) redis.call('DEL', prefix .. 'race_names:canonical_lookup:' .. canonical) end local registeredCount = #registered if registeredCount > 0 then redis.call('DEL', userRegisteredKey) end local reservations = redis.call('SMEMBERS', userReservationsKey) local pendingCount = 0 for _, member in ipairs(reservations) do local sep = string.find(member, ':', 1, true) if sep then local encGame = string.sub(member, 1, sep - 1) local encCanonical = string.sub(member, sep + 1) redis.call('DEL', prefix .. 'race_names:reservations:' .. encGame .. ':' .. encCanonical) local pendingRemoved = redis.call('ZREM', pendingIndexKey, member) if pendingRemoved == 1 then pendingCount = pendingCount + 1 end redis.call('DEL', prefix .. 'race_names:canonical_lookup:' .. encCanonical) end end local reservationsTotal = #reservations if reservationsTotal > 0 then redis.call('DEL', userReservationsKey) end return {registeredCount, reservationsTotal, pendingCount} `