pomerium/internal/zero/analytics/activeusers.go
2023-12-11 13:37:01 -05:00

80 lines
2.3 KiB
Go

package analytics
import (
"time"
"github.com/pomerium/pomerium/pkg/counter"
)
const (
// activeUsersCap is the number of active users we would be able to track.
// for counter to work within the 1% error limit, actual number should be 80% of the cap.
activeUsersCap = 10_000
)
// IntervalResetFunc is a function that determines if a counter should be reset
type IntervalResetFunc func(lastReset time.Time, now time.Time) bool
// ResetMonthlyUTC resets the counter on a monthly interval
func ResetMonthlyUTC(lastReset time.Time, now time.Time) bool {
lastResetUTC := lastReset.UTC()
nowUTC := now.UTC()
return lastResetUTC.Year() != nowUTC.Year() ||
lastResetUTC.Month() != nowUTC.Month()
}
// ResetDailyUTC resets the counter on a daily interval
func ResetDailyUTC(lastReset time.Time, now time.Time) bool {
lastResetUTC := lastReset.UTC()
nowUTC := now.UTC()
return lastResetUTC.Year() != nowUTC.Year() ||
lastResetUTC.Month() != nowUTC.Month() ||
lastResetUTC.Day() != nowUTC.Day()
}
// ActiveUsersCounter is a counter that resets on a given interval
type ActiveUsersCounter struct {
*counter.Counter
lastReset time.Time
needsReset IntervalResetFunc
}
// NewActiveUsersCounter creates a new active users counter
func NewActiveUsersCounter(needsReset IntervalResetFunc, now time.Time) *ActiveUsersCounter {
return &ActiveUsersCounter{
Counter: counter.New(activeUsersCap),
lastReset: now,
needsReset: needsReset,
}
}
// LoadActiveUsersCounter loads an active users counter from a binary state
func LoadActiveUsersCounter(state []byte, lastReset time.Time, resetFn IntervalResetFunc) (*ActiveUsersCounter, error) {
c, err := counter.FromBinary(state)
if err != nil {
return nil, err
}
return &ActiveUsersCounter{
Counter: c,
lastReset: lastReset,
needsReset: resetFn,
}, nil
}
// Update updates the counter with the current users
func (c *ActiveUsersCounter) Update(users []string, now time.Time) (current uint, wasReset bool) {
if c.needsReset(c.lastReset, now) {
c.Counter.Reset()
c.lastReset = now
wasReset = true
}
for _, user := range users {
c.Mark(user)
}
return c.Count(), wasReset
}
// GetLastReset returns the last time the counter was reset
func (c *ActiveUsersCounter) GetLastReset() time.Time {
return c.lastReset
}