mirror of
https://github.com/therootcompany/golib.git
synced 2026-11-04 06:55:00 +00:00
feat(dataset): add View.Close and Set.Close for final-value cleanup
Previously only the *replaced* value was closed on swap — the *currently-held* value leaked on process exit. Adds: - View.Close() — closes the held value (if it implements io.Closer) and clears the pointer; idempotent. - Set.Close() — joins Close errors across all views in the set. form2mail defers geoSet.Close() to release the geoip2 file handles on shutdown.
This commit is contained in:
parent
9e46d5ce90
commit
4ab9ffe364
@ -293,6 +293,7 @@ func main() {
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
defer func() { _ = geoSet.Close() }()
|
||||
go blocklistSet.Tick(ctx, refreshInterval, func(err error) {
|
||||
log.Printf("blocklist refresh: %v", err)
|
||||
})
|
||||
|
||||
@ -16,6 +16,7 @@ package dataset
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
@ -137,6 +138,23 @@ func (s *Set) Load(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes every view's currently-held value. Call on shutdown to
|
||||
// release any OS resources held by the final snapshots (file handles,
|
||||
// network connections). Safe to call on a Set that hasn't been loaded
|
||||
// or whose views hold pure in-memory values — non-Closer values are
|
||||
// skipped. Returns joined errors from any Close failures.
|
||||
func (s *Set) Close() error {
|
||||
var errs []error
|
||||
for _, v := range s.views {
|
||||
if c, ok := v.(io.Closer); ok {
|
||||
if err := c.Close(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// Tick calls Load every interval until ctx is done. Load errors are passed to
|
||||
// onError (if non-nil) and do not stop the loop; callers choose whether to log,
|
||||
// count, page, or ignore. Run in a goroutine: `go s.Tick(ctx, d, onError)`.
|
||||
@ -177,6 +195,19 @@ func (v *View[T]) LoadedAt() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Close clears the view and closes the currently-held value if it
|
||||
// implements io.Closer. Idempotent — subsequent calls are no-ops.
|
||||
func (v *View[T]) Close() error {
|
||||
prev := v.ptr.Swap(nil)
|
||||
if prev == nil {
|
||||
return nil
|
||||
}
|
||||
if closer, ok := any(prev).(io.Closer); ok {
|
||||
return closer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *View[T]) reload(ctx context.Context) error {
|
||||
t, err := v.loader(ctx)
|
||||
if err != nil {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user