package db import ( "time" "github.com/go-gorp/gorp" "github.com/jonboulle/clockwork" "github.com/coreos/dex/pkg/log" ptime "github.com/coreos/dex/pkg/time" ) type purger interface { purge() error } type namedPurger struct { name string purger } func NewGarbageCollector(dbm *gorp.DbMap, ival time.Duration) *GarbageCollector { sRepo := NewSessionRepo(dbm) skRepo := NewSessionKeyRepo(dbm) purgers := []namedPurger{ namedPurger{ name: "session", purger: sRepo, }, namedPurger{ name: "session_key", purger: skRepo, }, } gc := GarbageCollector{ purgers: purgers, interval: ival, clock: clockwork.NewRealClock(), } return &gc } type GarbageCollector struct { purgers []namedPurger interval time.Duration clock clockwork.Clock } func (gc *GarbageCollector) Run() chan struct{} { stop := make(chan struct{}) go func() { var failing bool next := gc.interval for { select { case <-gc.clock.After(next): if anyPurgeErrors(purgeAll(gc.purgers)) { if !failing { failing = true next = time.Second } else { next = ptime.ExpBackoff(next, time.Minute) } log.Errorf("Failed garbage collection, retrying in %v", next) break } failing = false next = gc.interval log.Infof("Garbage collection complete, running again in %v", next) case <-stop: return } } }() return stop } type purgeError struct { name string err error } func anyPurgeErrors(errchan <-chan purgeError) (found bool) { for perr := range errchan { found = true log.Errorf("Failed purging %s: %v", perr.name, perr.err) } return } func purgeAll(purgers []namedPurger) <-chan purgeError { errchan := make(chan purgeError) go func() { for _, p := range purgers { if err := p.purge(); err != nil { errchan <- purgeError{name: p.name, err: err} } } close(errchan) }() return errchan }