-
Notifications
You must be signed in to change notification settings - Fork 38
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
Add a method to refresh only if lookup is a success, not an error. #159
Closed
kajebiii
wants to merge
6
commits into
zio:series/2.x
from
kajebiii:feature/refresh-rollback-if-failed
Closed
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
250a3ac
Add rollbackIfFailed parameter in refresh
kajebiii 25c3933
Implement another operator `refreshValue`
kajebiii 481871e
Return exit on promise
kajebiii 3df9381
Implement test about `refreshValue`
kajebiii 5da82d1
Add case for rollback to empty cache
kajebiii bc00941
Fix typo
kajebiii File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,6 +78,12 @@ abstract class Cache[-Key, +Error, +Value] { | |
*/ | ||
def refresh(key: Key): IO[Error, Unit] | ||
|
||
/** | ||
* Computes the value associated with the specified key, with the lookup | ||
* function, and puts it in the cache only if it is a value, not an error. | ||
*/ | ||
def refreshValue(key: Key): IO[Error, Unit] | ||
|
||
/** | ||
* Invalidates the value associated with the specified key. | ||
*/ | ||
|
@@ -247,7 +253,13 @@ object Cache { | |
} | ||
} | ||
|
||
override def refresh(in: In): IO[Error, Unit] = | ||
def refresh(in: In): IO[Error, Unit] = | ||
refresh(in, rollbackIfError = false) | ||
|
||
def refreshValue(in: In): IO[Error, Unit] = | ||
refresh(in, rollbackIfError = true) | ||
|
||
private def refresh(in: In, rollbackIfError: Boolean): IO[Error, Unit] = | ||
ZIO.suspendSucceedUnsafe { implicit u => | ||
val k = keyBy(in) | ||
val promise = newPromise() | ||
|
@@ -256,7 +268,8 @@ object Cache { | |
value = map.putIfAbsent(k, MapValue.Pending(new MapKey(k), promise)) | ||
} | ||
val result = if (value eq null) { | ||
lookupValueOf(in, promise) | ||
val rollbackResultIfError = if (rollbackIfError) Right(None) else Left(()) | ||
lookupValueOf(in, promise, rollbackResultIfError) | ||
} else { | ||
value match { | ||
case MapValue.Pending(_, promiseInProgress) => | ||
|
@@ -267,7 +280,8 @@ object Cache { | |
get(in) | ||
} else { | ||
// Only trigger the lookup if we're still the current value, `completedResult` | ||
lookupValueOf(in, promise).when { | ||
val rollbackResultIfError = if (rollbackIfError) Right(Some(completedResult)) else Left(()) | ||
lookupValueOf(in, promise, rollbackResultIfError).when { | ||
map.replace(k, completedResult, MapValue.Refreshing(promise, completedResult)) | ||
} | ||
} | ||
|
@@ -292,7 +306,16 @@ object Cache { | |
def size(implicit trace: Trace): UIO[Int] = | ||
ZIO.succeed(map.size) | ||
|
||
private def lookupValueOf(in: In, promise: Promise[Error, Value]): IO[Error, Value] = | ||
private def lookupValueOf( | ||
in: In, | ||
promise: Promise[Error, Value], | ||
/** | ||
* Left(()): Put the lookup result. | ||
* Right(None): Remove key if there is an error. | ||
* Right(Some(rollbackResult)): Rollback if there is an error. | ||
*/ | ||
rollbackResultIfError: Either[Unit, Option[MapValue.Complete[Key, Error, Value]]] = Left(()) | ||
): IO[Error, Value] = | ||
ZIO.suspendSucceed { | ||
val key = keyBy(in) | ||
lookup(in) | ||
|
@@ -302,7 +325,18 @@ object Cache { | |
val now = Unsafe.unsafe(implicit u => clock.unsafe.instant()) | ||
val entryStats = EntryStats(now) | ||
|
||
map.put(key, MapValue.Complete(new MapKey(key), exit, entryStats, now.plus(timeToLive(exit)))) | ||
if (exit.isSuccess) | ||
map.put(key, MapValue.Complete(new MapKey(key), exit, entryStats, now.plus(timeToLive(exit)))) | ||
else | ||
rollbackResultIfError match { | ||
case Left(()) => | ||
map.put(key, MapValue.Complete(new MapKey(key), exit, entryStats, now.plus(timeToLive(exit)))) | ||
case Right(None) => | ||
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. Similarly here this is basically a |
||
map.remove(key) | ||
case Right(Some(rollbackResult)) => | ||
map.put(key, rollbackResult) | ||
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. Here if the time to live has already expired this is really another |
||
} | ||
|
||
promise.done(exit) *> ZIO.done(exit) | ||
} | ||
.onInterrupt(promise.interrupt *> ZIO.succeed(map.remove(key))) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Not sure this really makes sense. If the value isn't in the cache at all how is this any different from a
get
which will put the value in the cache even if it is an error? I think one of the issues here is we are treatingrefresh
inconsistently withget
.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.
I see your point. To be consistent with
get
we can say that we put the error into the cache then and remove that special case.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.
Yeah I think our principle should be that the idea, as I understand it, is that if there is a valid "good" value in the cache we don't want to overwrite it with a "bad" value. One question that raises is what we should do when the original value is itself an error.
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.
I do wonder whether this is the best path to address this. Like do you want to cache error values at all?
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.