package main import ( "fmt" "github.com/go-redis/redis/v8" "math" "os" "strings" "time" ) type eventCounts struct { All int Members int GroupSizeFull int GroupSizeHalf int } const tarpitFactor = 1000.0 func main() { // konfigurace redis klienta options, err := redis.ParseURL(os.Getenv("CFG_REDIS")) if err != nil { panic(err) } // redis klient a kontext rdb := redis.NewClient(options) ctx := rdb.Context() // ukonceni prace redis klienta defer func() { err := rdb.Close() if err != nil { panic(err) } }() lastPub := make(map[string]time.Time) // main loop ticker := time.Tick(time.Second * 1) for now := range ticker { online := make(map[string]eventCounts) iter := rdb.Scan(ctx, 0, "live:*", 0).Iterator() for iter.Next(ctx) { key := strings.Split(iter.Val(), ":") eventID := key[1] isMember := key[2] == "1" // pocitani event := online[eventID] event.All++ if isMember { event.Members++ } event.GroupSizeFull, event.GroupSizeHalf = member_group_size(event.All) online[key[1]] = event } if err := iter.Err(); err != nil { panic(err) } // publsh counts for event, count := range online { elapsed := now.Sub(lastPub[event]).Round(time.Second).Seconds() if elapsed < float64(count.All)/tarpitFactor { break } lastPub[event] = now topic := fmt.Sprintf("online:%s", event) message := fmt.Sprintf("%v", count) message = message[1 : len(message)-1] if os.Getenv("DEBUG") != "" { fmt.Println(topic, message) } err = rdb.Publish(ctx, topic, message).Err() if err != nil { panic(err) } } } } func member_group_size(count int) (full int, half int) { min := float64(count) / 100 max := float64(count) / 5 group := 2 * math.Sqrt(float64(count)) if group < min { group = min } if group > max { group = max } full = int(math.Ceil(group)) half = int(math.Ceil(group / 2)) return }