-
Notifications
You must be signed in to change notification settings - Fork 681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update the item only if it exists in the cache #4117
Changes from all commits
34f99f6
368f997
a130ff6
185cf47
c58fc33
e198282
728a72e
aedaf76
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ package cache | |
import ( | ||
"context" | ||
"fmt" | ||
"sync" | ||
"time" | ||
|
||
"github.com/flyteorg/flyte/flytestdlib/contextutils" | ||
|
@@ -122,6 +123,7 @@ type autoRefresh struct { | |
syncPeriod time.Duration | ||
workqueue workqueue.RateLimitingInterface | ||
parallelizm int | ||
lock sync.RWMutex | ||
} | ||
|
||
func getEvictionFunction(counter prometheus.Counter) func(key interface{}, value interface{}) { | ||
|
@@ -173,6 +175,25 @@ func (w *autoRefresh) Start(ctx context.Context) error { | |
return nil | ||
} | ||
|
||
// Update updates the item only if it exists in the cache, return true if we updated the item. | ||
func (w *autoRefresh) Update(id ItemID, item Item) (ok bool) { | ||
w.lock.Lock() | ||
defer w.lock.Unlock() | ||
ok = w.lruMap.Contains(id) | ||
if ok { | ||
w.lruMap.Add(id, item) | ||
} | ||
return ok | ||
} | ||
|
||
// Delete deletes the item from the cache if it exists. | ||
func (w *autoRefresh) Delete(key interface{}) { | ||
w.lock.Lock() | ||
defer w.lock.Unlock() | ||
Comment on lines
+191
to
+192
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to avoid locking the entire dataset for these operations... any reason to? |
||
w.toDelete.Remove(key) | ||
w.lruMap.Remove(key) | ||
} | ||
|
||
func (w *autoRefresh) Get(id ItemID) (Item, error) { | ||
if val, ok := w.lruMap.Get(id); ok { | ||
w.metrics.CacheHit.Inc() | ||
|
@@ -212,8 +233,7 @@ func (w *autoRefresh) enqueueBatches(ctx context.Context) error { | |
snapshot := make([]ItemWrapper, 0, len(keys)) | ||
for _, k := range keys { | ||
if w.toDelete.Contains(k) { | ||
w.lruMap.Remove(k) | ||
w.toDelete.Remove(k) | ||
w.Delete(k) | ||
continue | ||
} | ||
// If not ok, it means evicted between the item was evicted between getting the keys and this update loop | ||
|
@@ -273,18 +293,37 @@ func (w *autoRefresh) sync(ctx context.Context) (err error) { | |
case <-ctx.Done(): | ||
return nil | ||
default: | ||
item, shutdown := w.workqueue.Get() | ||
batch, shutdown := w.workqueue.Get() | ||
if shutdown { | ||
logger.Debugf(ctx, "Shutting down worker") | ||
return nil | ||
} | ||
|
||
t := w.metrics.SyncLatency.Start() | ||
updatedBatch, err := w.syncCb(ctx, *item.(*Batch)) | ||
|
||
// Since we create batches every time we sync, we will just remove the item from the queue here | ||
// regardless of whether it succeeded the sync or not. | ||
w.workqueue.Forget(item) | ||
w.workqueue.Done(item) | ||
w.workqueue.Forget(batch) | ||
w.workqueue.Done(batch) | ||
|
||
newBatch := make(Batch, 0, len(*batch.(*Batch))) | ||
for _, b := range *batch.(*Batch) { | ||
itemID := b.GetID() | ||
item, ok := w.lruMap.Get(itemID) | ||
if !ok { | ||
logger.Debugf(ctx, "item with id [%v] not found in cache", itemID) | ||
continue | ||
} | ||
if item.(Item).IsTerminal() { | ||
logger.Debugf(ctx, "item with id [%v] is terminal", itemID) | ||
continue | ||
} | ||
newBatch = append(newBatch, b) | ||
} | ||
if len(newBatch) == 0 { | ||
continue | ||
} | ||
|
||
t := w.metrics.SyncLatency.Start() | ||
updatedBatch, err := w.syncCb(ctx, newBatch) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add a check for if the len(newBatch) == 0, we don't need to sync at that point... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. updated it |
||
|
||
if err != nil { | ||
w.metrics.SyncErrors.Inc() | ||
|
@@ -295,14 +334,13 @@ func (w *autoRefresh) sync(ctx context.Context) (err error) { | |
|
||
for _, item := range updatedBatch { | ||
if item.Action == Update { | ||
// Add adds the item if it has been evicted or updates an existing one. | ||
w.lruMap.Add(item.ID, item.Item) | ||
// Updates an existing item. | ||
w.Update(item.ID, item.Item) | ||
} | ||
} | ||
|
||
w.toDelete.Range(func(key interface{}) bool { | ||
w.lruMap.Remove(key) | ||
w.toDelete.Remove(key) | ||
w.Delete(key) | ||
return true | ||
}) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only update if it's already there, what if it has been evicted since?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't check if it exist in the cache, we might have this scenario.
worker1
removes item from the caches ->worker2
adds the item to cache again. (workerqueue can have duplicate item)If we don't use lock, we might have this scenario.