64 lines
1.3 KiB
Go
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
|
|
}
|
|
}
|