Files

64 lines
1.3 KiB
Go

package api
import (
"context"
"git.misaka.ren/m1saka/dsh-web/internal/store"
"log"
"time"
)
// RunQuotaMonitor reports soft quota crossings; it never claims to constrain
// runtime writes and never deletes user data to recover capacity.
func (a *API) RunQuotaMonitor(ctx context.Context) {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
exceeded := map[string]bool{}
for {
a.checkQuotas(ctx, exceeded)
select {
case <-ctx.Done():
return
case <-ticker.C:
}
}
}
func (a *API) checkQuotas(ctx context.Context, exceeded map[string]bool) {
rows, err := a.s.DB.QueryContext(ctx, "SELECT id,quota FROM users")
if err != nil {
return
}
var users []store.User
for rows.Next() {
var u store.User
if rows.Scan(&u.ID, &u.Quota) != nil {
rows.Close()
return
}
users = append(users, u)
}
err = rows.Err()
rows.Close()
if err != nil {
return
}
for _, u := range users {
if ctx.Err() != nil {
return
}
used, err := a.userUsage(u)
if err != nil {
log.Printf("quota usage unavailable for user %s", u.ID)
continue
}
over := used > u.Quota
if over && !exceeded[u.ID] {
a.s.AddAudit(u.ID, "quota.exceeded", u.ID, "soft quota exceeded; runtime writes remain unrestricted")
}
if !over && exceeded[u.ID] {
a.s.AddAudit(u.ID, "quota.recovered", u.ID, "")
}
exceeded[u.ID] = over
}
}