1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-07 21:47:02 +09:00

GO-2076 Add TryRemove method to Ocache

This commit is contained in:
Mikhail Iudin 2025-01-24 22:15:25 +01:00
parent 8e0e8ae47f
commit e31b078c69
No known key found for this signature in database
GPG key ID: FAAAA8BAABDFF1C0
2 changed files with 113 additions and 0 deletions

View file

@ -89,6 +89,8 @@ type OCache interface {
Add(id string, value Object) (err error)
// Remove closes and removes object
Remove(ctx context.Context, id string) (ok bool, err error)
// TryRemove Tries to close and to remove the object
TryRemove(id string) (ok bool, err error)
// ForEach iterates over all loaded objects, breaks when callback returns false
ForEach(f func(v Object) (isContinue bool))
// GC frees not used and expired objects
@ -212,6 +214,45 @@ func (c *oCache) remove(ctx context.Context, e *entry) (ok bool, err error) {
return
}
func (c *oCache) TryRemove(id string) (ok bool, err error) {
c.mu.Lock()
if c.closed {
c.mu.Unlock()
return false, ErrClosed
}
e, contains := c.data[id]
if !contains {
c.mu.Unlock()
return false, ErrNotExists
}
c.mu.Unlock()
prevState, _ := e.setClosing(false)
if prevState == entryStateClosing || prevState == entryStateClosed {
return false, nil
}
closed, err := e.value.TryClose(c.ttl)
if err != nil {
c.log.With("object_id", e.id).Warnf("try remove err: %v", err)
return closed, err
}
if !closed {
e.setActive(true)
return false, nil
}
c.mu.Lock()
e.setClosed()
delete(c.data, e.id)
c.mu.Unlock()
return true, nil
}
func (c *oCache) DoLockedIfNotExists(id string, action func() error) error {
c.mu.Lock()
defer c.mu.Unlock()

View file

@ -317,6 +317,78 @@ func Test_OCache_Remove(t *testing.T) {
<-getCh
require.Equal(t, []string{"close", "get"}, events)
})
t.Run("tryRemove simple", func(t *testing.T) {
closeCh := make(chan struct{})
getCh := make(chan struct{})
c := New(func(ctx context.Context, id string) (value Object, err error) {
return NewTestObject(id, true, closeCh), nil
}, WithTTL(time.Millisecond*10))
val, err := c.Get(context.TODO(), "id")
require.NoError(t, err)
require.NotNil(t, val)
assert.Equal(t, 1, c.Len())
// try removing the object, so we will wait on closing
go func() {
_, err := c.TryRemove("id")
require.Equal(t, 0, c.Len())
require.NoError(t, err)
}()
time.Sleep(time.Millisecond * 20)
var events []string
go func() {
_, err := c.Get(context.TODO(), "id")
require.Equal(t, 1, c.Len())
require.NoError(t, err)
require.NotNil(t, val)
events = append(events, "get")
close(getCh)
}()
// sleeping to make sure that Get is called
time.Sleep(time.Millisecond * 20)
events = append(events, "close")
close(closeCh)
<-getCh
require.Equal(t, []string{"close", "get"}, events)
})
t.Run("tryRemove simple - can't be removed", func(t *testing.T) {
closeCh := make(chan struct{})
getCh := make(chan struct{})
c := New(func(ctx context.Context, id string) (value Object, err error) {
return NewTestObject(id, false, closeCh), nil
}, WithTTL(time.Millisecond*10))
val, err := c.Get(context.TODO(), "id")
require.NoError(t, err)
require.NotNil(t, val)
assert.Equal(t, 1, c.Len())
// try removing the object, so we will wait on closing
go func() {
_, err := c.TryRemove("id")
require.Equal(t, 1, c.Len())
require.NoError(t, err)
}()
time.Sleep(time.Millisecond * 20)
var events []string
go func() {
_, err := c.Get(context.TODO(), "id")
require.Equal(t, 1, c.Len())
require.NoError(t, err)
require.NotNil(t, val)
events = append(events, "get")
close(getCh)
}()
// sleeping to make sure that Get is called
time.Sleep(time.Millisecond * 20)
events = append(events, "close")
close(closeCh)
<-getCh
require.Equal(t, []string{"close", "get"}, events)
})
t.Run("test remove while gc, tryClose false", func(t *testing.T) {
closeCh := make(chan struct{})
removeCh := make(chan struct{})