diff --git a/.github/ci.yml b/.github/workflows/ci.yml similarity index 100% rename from .github/ci.yml rename to .github/workflows/ci.yml diff --git a/go.mod b/go.mod index 53dffeb..d465d5e 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,7 @@ module github.com/thinkdata-works/gocache go 1.18 -require ( - github.com/stretchr/testify v1.8.3 - github.com/thinkdata-works/gopromise v0.1.0 -) +require github.com/stretchr/testify v1.8.3 require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index a78ef5a..57c201b 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/thinkdata-works/gopromise v0.1.0 h1:qVWnyufZfoMyeUr4DtdhQhmgXz9abFdGAHhCdipM3Pk= -github.com/thinkdata-works/gopromise v0.1.0/go.mod h1:vec0Q1/jvqEZ7UutAfzRKYIiqEcj4/pAQOxLvnzxP3c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/pkg/gocache/cache.go b/pkg/gocache/cache.go index 33a7a7a..419830c 100644 --- a/pkg/gocache/cache.go +++ b/pkg/gocache/cache.go @@ -2,17 +2,15 @@ package gocache import ( "time" - - "github.com/thinkdata-works/gopromise/pkg/promise" ) type Cache[K comparable, V any] struct { - cache *partitionedCache[K, promise.Promise[V]] + cache *partitionedCache[K, Promise[V]] } func NewCache[K comparable, V any](partitionSize, totalPartitions int, cacheExpiry time.Duration) *Cache[K, V] { return &Cache[K, V]{ - cache: newPartitionedCached[K, promise.Promise[V]]( + cache: newPartitionedCached[K, Promise[V]]( partitionSize, totalPartitions, cacheExpiry, ), } @@ -23,7 +21,7 @@ func (c *Cache[K, V]) HasKey(k K) (bool, error) { } func (c *Cache[K, V]) Get(k K, getter func() (*V, error)) (*V, error) { - valpromise, alreadyExists := c.cache.GetOrCreate(k, promise.NewPromise[V]()) + valpromise, alreadyExists := c.cache.GetOrCreate(k, NewPromise[V]()) if alreadyExists { val, err := valpromise.Wait() if err != nil { diff --git a/pkg/gocache/promise.go b/pkg/gocache/promise.go new file mode 100644 index 0000000..6ae6d05 --- /dev/null +++ b/pkg/gocache/promise.go @@ -0,0 +1,73 @@ +package gocache + +import ( + "sync" + "time" +) + +type future[V any] struct { + v *V + err error +} + +type Promise[V any] struct { + ch chan future[V] + v *V + err error + mu *sync.Mutex + count int + done bool + start time.Time +} + +func NewPromise[V any]() *Promise[V] { + return &Promise[V]{ + ch: make(chan future[V]), + start: time.Now(), + mu: &sync.Mutex{}, + done: false, + count: 0, + v: nil, + err: nil, + } +} + +func (p *Promise[V]) Wait() (*V, error) { + p.mu.Lock() + if p.done { + p.mu.Unlock() + return p.v, p.err + } + p.count++ + p.mu.Unlock() + + f := <-p.ch + return f.v, f.err +} + +func (p *Promise[V]) fulfill(v *V, err error) { + p.mu.Lock() + defer p.mu.Unlock() + + if p.done { + // already fulfilled + return + } + p.done = true + p.v = v + p.err = err + for i := 0; i < p.count; i++ { + p.ch <- future[V]{ + v: v, + err: err, + } + } +} + +func (p *Promise[V]) Resolve(v *V) { + p.fulfill(v, nil) +} + +func (p *Promise[V]) Reject(err error) { + p.fulfill(nil, err) +}