diff --git a/actor.go b/actor.go index 4427d86..a313082 100644 --- a/actor.go +++ b/actor.go @@ -127,3 +127,10 @@ type RunShutdownable interface { // should be shut down. Shutdown(ctx context.Context) error } + +// Cleanupable is any object that must be cleaned up after it has stopped for +// good. Actors which implement this interface will be cleaned up after they +// are deleted from the environment. +type Cleanupable interface { + Cleanup(ctx context.Context) error +} diff --git a/environment.go b/environment.go index bd138de..afb4cf5 100644 --- a/environment.go +++ b/environment.go @@ -19,6 +19,7 @@ const defaultRestartInitialInterval = 8 * time.Second const defaultRestartInitialIncrement = 8 * time.Second const defaultRestartInitialMaximum = 1 * time.Hour const defaultResetTimeout = 8 * time.Minute +const defaultCleanupTimeout = 1 * time.Minute const defaultTrimInterval = 1 * time.Minute const defaultTrimTimeout = 1 * time.Minute const defaultShutdownTimeout = 8 * time.Minute @@ -62,6 +63,7 @@ type environment struct { restartIntervalIncrement atomicDuration restartIntervalMaximum atomicDuration resetTimeout atomicDuration + cleanupTimeout atomicDuration trimTimeout atomicDuration shutdownTimeout atomicDuration } @@ -238,11 +240,30 @@ func (this *environment) start(actor Actor) { // counter will be decremented. note that this function will never increment the // wait group counter, so start should usually be used instead. func (this *environment) run(actor Actor) { + typ := actor.Type() + // clean up when done - defer this.group.Done() + defer func() { + this.group.Done() + this.delFromSets(actor) + if actor, ok := actor.(Cleanupable); ok { + ctx, done := context.WithTimeout( + context.Background(), + defaul( + this.timing.cleanupTimeout.Load(), + defaultCleanupTimeout)) + defer done() + err := actor.Cleanup(ctx) + if err != nil { + log.Printf("XXX [%s] failed to cleanup: %v", typ, err) + if this.flags.crashOnError { + panic(fmt.Sprint(err)) + } + } + } + }() // logging - typ := actor.Type() if this.Verb() { log.Printf("(i) [%s] running", typ) } var stopErr error var exited bool @@ -434,6 +455,8 @@ func (this *environment) applyConfig() error { if err != nil { return err } err = parseDuration("reset-timeout", &this.timing.resetTimeout) if err != nil { return err } + err = parseDuration("cleanup-timeout", &this.timing.cleanupTimeout) + if err != nil { return err } err = parseDuration("trim-timeout", &this.timing.trimTimeout) if err != nil { return err } err = parseDuration("shutdown-timeout", &this.timing.shutdownTimeout) diff --git a/phases.go b/phases.go index 7e3dd2a..4ad3aba 100644 --- a/phases.go +++ b/phases.go @@ -279,6 +279,7 @@ func (this *environment) phase70_5Trimming() bool { } func (this *environment) phase80Shutdown() bool { + logActors(All()) ctx, done := context.WithTimeout( context.Background(), defaul(this.timing.shutdownTimeout.Load(), defaultShutdownTimeout))