diff --git a/cache/dynacache/dynacache.go b/cache/dynacache/dynacache.go index 5007e27ba..a906a0dd3 100644 --- a/cache/dynacache/dynacache.go +++ b/cache/dynacache/dynacache.go @@ -430,12 +430,25 @@ func (p *Partition[K, V]) doGetOrCreateWitTimeout(key K, duration time.Duration, errch := make(chan error, 1) go func() { - v, _, err := p.c.GetOrCreate(key, create) - if err != nil { - errch <- err - return - } - resultch <- v + var ( + v V + err error + ) + defer func() { + if r := recover(); r != nil { + if rerr, ok := r.(error); ok { + err = rerr + } else { + err = fmt.Errorf("panic: %v", r) + } + } + if err != nil { + errch <- err + } else { + resultch <- v + } + }() + v, _, err = p.c.GetOrCreate(key, create) }() select { diff --git a/cache/dynacache/dynacache_test.go b/cache/dynacache/dynacache_test.go index 9a932729b..87239479b 100644 --- a/cache/dynacache/dynacache_test.go +++ b/cache/dynacache/dynacache_test.go @@ -14,6 +14,7 @@ package dynacache import ( + "errors" "fmt" "path/filepath" "testing" @@ -174,18 +175,47 @@ func TestPanicInCreate(t *testing.T) { p1 := GetOrCreatePartition[string, testItem](cache, "/aaaa/bbbb", OptionsPartition{Weight: 30, ClearWhen: ClearOnRebuild}) + willPanic := func(i int) func() { + return func() { + p1.GetOrCreate(fmt.Sprintf("panic-%d", i), func(key string) (testItem, error) { + panic(errors.New(key)) + }) + } + } + + // GetOrCreateWitTimeout needs to recover from panics in the create func. + willErr := func(i int) error { + _, err := p1.GetOrCreateWitTimeout(fmt.Sprintf("error-%d", i), 10*time.Second, func(key string) (testItem, error) { + return testItem{}, errors.New(key) + }) + return err + } + for i := 0; i < 3; i++ { for j := 0; j < 3; j++ { - _, err := p1.GetOrCreate(fmt.Sprintf("panic1-%d", i), func(string) (testItem, error) { - panic("failed") - }) + c.Assert(willPanic(i), qt.PanicMatches, fmt.Sprintf("panic-%d", i)) + c.Assert(willErr(i), qt.ErrorMatches, fmt.Sprintf("error-%d", i)) + } + } - c.Assert(err, qt.Not(qt.IsNil)) - - _, err = p1.GetOrCreateWitTimeout(fmt.Sprintf("panic2-%d", i), 10*time.Second, func(string) (testItem, error) { - panic("failed") + // Test the same keys again without the panic. + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + v, err := p1.GetOrCreate(fmt.Sprintf("panic-%d", i), func(key string) (testItem, error) { + return testItem{ + name: key, + }, nil }) - c.Assert(err, qt.Not(qt.IsNil)) + c.Assert(err, qt.IsNil) + c.Assert(v.name, qt.Equals, fmt.Sprintf("panic-%d", i)) + + v, err = p1.GetOrCreateWitTimeout(fmt.Sprintf("error-%d", i), 10*time.Second, func(key string) (testItem, error) { + return testItem{ + name: key, + }, nil + }) + c.Assert(err, qt.IsNil) + c.Assert(v.name, qt.Equals, fmt.Sprintf("error-%d", i)) } } } diff --git a/go.mod b/go.mod index 3f5d4a707..108241dc5 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/bep/gowebp v0.3.0 github.com/bep/helpers v0.5.0 github.com/bep/imagemeta v0.8.1 - github.com/bep/lazycache v0.6.0 + github.com/bep/lazycache v0.7.0 github.com/bep/logg v0.4.0 github.com/bep/mclib v1.20400.20402 github.com/bep/overlayfs v0.9.2 diff --git a/go.sum b/go.sum index 6346fb099..cfc34880b 100644 --- a/go.sum +++ b/go.sum @@ -145,6 +145,8 @@ github.com/bep/lazycache v0.4.0 h1:X8yVyWNVupPd4e1jV7efi3zb7ZV/qcjKQgIQ5aPbkYI= github.com/bep/lazycache v0.4.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc= github.com/bep/lazycache v0.6.0 h1:0vCgFo7TBtMQpSx64jnH1sagmw0ZougIFRpsqPHTa5U= github.com/bep/lazycache v0.6.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc= +github.com/bep/lazycache v0.7.0 h1:VM257SkkjcR9z55eslXTkUIX8QMNKoqQRNKV/4xIkCY= +github.com/bep/lazycache v0.7.0/go.mod h1:NmRm7Dexh3pmR1EignYR8PjO2cWybFQ68+QgY3VMCSc= github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0= github.com/bep/mclib v1.20400.20402 h1:olpCE2WSPpOAbFE1R4hnftSEmQ34+xzy2HRzd0m69rA=