-
Notifications
You must be signed in to change notification settings - Fork 120
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 cats-effect to the core module, remove modes #345
Conversation
modules/core/shared/src/main/scala/scalacache/AbstractCache.scala
Outdated
Show resolved
Hide resolved
@kubukoz this is looking great! Feels so much cleaner without the modes 😅. |
Great! The number of deleted lines is a good sign :) I’m on my phone now but I can take a proper look this weekend. |
@cb372 please do, if it looks good I'll proceed to migrate remaining modules. |
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.
Looks great so far. Please continue 😄
else | ||
Some(entry.value) | ||
def doGet(key: String): F[Option[V]] = { | ||
F.delay { |
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.
nit: let's write one-liners like this as F.delay(...)
instead of with curly braces.
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 have a personal preference of putting by-name parameters in curly braces (if the parameter list only has one parameter), especially when lifting side effects. But I can adjust :)
modules/core/shared/src/main/scala/scalacache/AbstractCache.scala
Outdated
Show resolved
Hide resolved
@@ -49,123 +55,92 @@ trait AbstractCache[V] extends Cache[V] with LoggingSupport { | |||
if (logger.isDebugEnabled) { | |||
logger.debug(s"Skipping cache PUT because cache writes are disabled. Key: $key") | |||
} | |||
mode.M.pure(()) | |||
F.pure(()) |
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.
F.unit
?
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.
That'll need .widen
- F is invariant and we need F[Any]
unless I change the interface's type. Which I want to do, but maybe as a different change.
*/ | ||
def close[F[_]]()(implicit mode: Mode[F]): F[Any] | ||
//TODO: Replace with Resource-based API? |
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.
Possibly, but let's save that for another PR
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.
sure
I know we talked in #336 about potentially removing some of the cache implementations. Could be worth drawing a box around which ones we'd want to remove (if any) prior to doing the work of actually migrating them. If so, I can open another issue so we can come to a decision about which ones to potentially remove. |
@lewisjkl I'm totally on board with that, no point migrating code that is going to disappear. If you have some thoughts on which ones we'll remove, please bootstrap an issue :) |
Hey @kubukoz thanks for waiting. We've now removed a few modules, so feel free to rebase and pick this work up again when you have time. |
Hey, I'll try to get back to this soon. Finished some other work so my bandwidth is a bit wider now... |
@cb372 @lewisjkl how do you feel about logging? Currently we have impure logging all over the place, and it should be suspended. I see the following options as best:
For convenience, I'd go with the first, but not everyone will want to use slf4j (as we do now), which might be a problem later. What do you think? |
This would be great if not for requiring that extra setup. Do you think there’d be any good way to set a default to a no-op logger that is overridable if a user desires? If so, is that something worth doing? Otherwise the sync option could be safer for now until we can think of a way to lower the friction to integrate and provide logging decoupled from a logging backend. I can definitely be persuaded here though. |
I think it's a reasonable course of action to proceed without anything smarter than Sync for now. Before the big bang release we can think about having it configurable. A no-op default certainly sounds possible in a way that makes it easy to override, as people don't create new caches all around the place (so less risk of inconsistent instances across the codebase). |
@kubukoz is there anything that I can do to help push this along? I'm not trying to push you to do anything here, just know that I am happy to help if you don't currently have the bandwidth 😄 |
I'd love to finally do this, as it's been open for a while... I'll get back to it so I'm not blocking further improvements any longer. |
Yeah it would be great to get this one over the line 😄 Let me know if I can help. |
By traversing on the entry's TTL we get the opportunity to avoid calculating the time in case there's no TTL. That might give us some extra performance in case of infinitely long-lived keys. The constraint is more powerful, but all the caching implementations will be at least monads, so it's fine. And Clock is only possible to implement in a real scenario for instances of Sync.
Apparently the redis cluster tests aren't working yet, and I haven't been able to set it up locally yet... I'll see what I can do. |
.map { | ||
case None | Some(true) => true | ||
case Some(false) => false | ||
} |
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.
What about .forall(identity)
?
Maybe that would just be harder to read, but thought I'd throw it out there.
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 had that but decided an explicit match would be more readable (in fact, I had a bug here due to having used contains
/ exists
).
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.
Cool, yeah I agree that the match is more readable. You don't have to do the mental gymnastics of remembering forall
defaults to true.
@kubukoz thanks for going through all all of that! Just looked through and didn't see any missed delays or |
I'll give it one more attempt today and let you know how it went :) |
@@ -1,23 +1,26 @@ | |||
package scalacache.logging | |||
|
|||
import org.slf4j.{Logger => Slf4jLogger, LoggerFactory} | |||
import cats.effect.Sync | |||
import cats.Applicative | |||
import cats.implicits._ |
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.
You don't appear to need this, based on what you are using I'd suggested targeted syntax:
import cats.syntax.option._
import cats.syntax.flatMap._
@@ -3,6 +3,11 @@ package scalacache | |||
import scala.concurrent.duration.Duration | |||
|
|||
import scala.language.higherKinds | |||
import cats.Monad | |||
import cats.implicits._ |
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.
targeted syntax for functor
, monadError
and flatMap
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'm not sure if there is a global standard for whether we want to use fine-grained imports or the whole thing. I can switch to either, but is there any improvement in using the targeted ones? The compilation time is roughly the same, from what I saw last time I checked.
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 try to use targeted imports as much as possible. Partially for compilation speed, although that is only anecdotal and wouldn't matter anyway on a project of this size. But more importantly for readability, as it gives readers more of a clue where instances and extension methods are coming from.
f: => F[V] | ||
)(implicit mode: Mode[F], flags: Flags): F[V] | ||
def cachingForMemoizeF(baseKey: String)(ttl: Option[Duration])( | ||
f: F[V] |
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.
Call by name might still be useful here, since I don't think there is a restriction against using Future
or Try
is there?
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.
Neither Future nor Try have cats-effect instances, which you need anyway (Sync, for some impls. Async).
I would be happy to pick this up. I’ll take a look within the next couple of days and see if I can get those tests passing. |
Started to look at the tests this morning. So far I tried to get a local redis cluster set up using docker-compose. I am able to get the tests in the |
@lewisjkl I don't think I've ever run the cluster tests locally. Somebody else contributed that makefile and I've always relied on Travis to run those test for me. In the past I installed redis via brew and fiddled around with config files to get Redis Sentinel working, but never tried for cluster. Docker sounds like a better idea. |
@cb372 sounds good, thanks for the info. Mostly just wanted to make sure I wasn't reinventing the wheel here. I will see what I can get figured out with Docker. If I come up with something noteworthy then I will add it in a PR sometime soon. |
So now all of the tests are passing and the docs are compiling. With the docs, I do think that we should go through and reformat some of the sections. Just to get them compiling, I added things like I also noticed that there was a |
@@ -3,19 +3,20 @@ package scalacache | |||
import scala.concurrent.duration.Duration | |||
import scala.language.higherKinds | |||
|
|||
trait Cache[V] extends CacheAlg[V] { | |||
//todo merge with alg? |
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.
Is this something we want to address in this PR? Seems like a good candidate for another PR to me, but happy to hear other opinions.
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.
Yes let's do this in another PR if we decide it's a good idea. I don't really remember why I separated Cache and CacheAlg in the first place.
@@ -7,74 +7,64 @@ import scala.language.higherKinds | |||
/** | |||
* Abstract algebra describing the operations a cache can perform | |||
* | |||
* @tparam F the effect of the cache. //todo elaborate |
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 don't know how much elaboration is needed here. I think going too much more into depth on what F[_]
means could be out of scope here.
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 we can just remove the @tparam
line completely. We'll assume that anyone using the library is familiar with tagless-final.
@@ -3,6 +3,7 @@ import scala.language.higherKinds | |||
|
|||
package object scalacache { | |||
|
|||
//todo |
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.
Is this todo indicating that type Id[X] = X
is able to be removed? I am guessing that it can be removed in favor of people using cats Id when appropriate. Although it won't be usable for any of the algebras requiring Sync/Async anyway..
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.
Yes I think this can be removed. I'll add a commit to do that.
Also, for anyone curious, I ended up just using this repo (I also removed the password that is set by default in this repo) to test with Redis locally. |
It finally happened! Merged! 🚀 |
No description provided.