-
Notifications
You must be signed in to change notification settings - Fork 594
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
Migrate Spray caching as akka-http-caching #213 #986
Conversation
Hi @tomrf1, Thank you for your contribution! We really value the time you've taken to put this together. Before we proceed with reviewing this pull request, please sign the Lightbend Contributors License Agreement: |
Can one of the repo owners verify this patch? |
OK TO TEST |
NICE! Looking forward to reviewing :-) |
Test FAILed. |
This looks really nice!
Note that Caffeine uses TinyLFU instead of LRU for the eviction algorithm. Your class names with The It hasn't been clear to me whether introducing an As a side note, if there is a desire for variable expiration, then that is a feature that we could collaborate on. The design that I've settled on is a hierarchical timer wheel, as shown in this slide deck (slide 10). This is an O(1) algorithm that would allow entries to have different expiration times, e.g. if caching external http resources based on their expiration header. |
|
||
def apply(key: Any, genValue: () ⇒ Future[V]): Future[V] = store.get(key, toJavaMappingFunction(genValue)).toScala | ||
|
||
def remove(key: Any): Option[Future[V]] = Option(store.synchronous().asMap().remove(key)).map(Future.successful) |
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.
When would a caller require the value on a removal? Its rare for a cache, so the penalty of waiting until the underlying future returns might be avoided.
If this is useful, we could look into supplying an asMap()
view on the AsyncLoadingCache
.
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.
The spray implementation does not use remove
, and I can't think why it would be useful to return the value. I had tried to keep the definitions consistent, but it's tempting to change this to:
def remove(key: Any): 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.
A scan of usages on Github indicate the value is often ignored.
You might argue that this is a simple cache for the common cases, but users with more advanced needs should use Caffeine (or alternatives) directly. That way feature requests are kept at a minimum, as you aren't trying to cater to every edge case.
Test FAILed. |
Test FAILed. |
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.
LGTM, however it would be nice to also have at least the scaladsl part documented.
For completeness this also needs a Java API but we could deal with that in a separate ticket as long as we mark the API with the Experimental
annotation since we cannot guarantee binary compatibility until the Java API is in place.
akka-http-caching/build.sbt
Outdated
@@ -0,0 +1,4 @@ | |||
import akka._ | |||
|
|||
libraryDependencies ++= Seq( |
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.
Please define this in the Compile
object in project/Dependencies.scala
and add to Dependencies.httpCaching
* a non-zero and finite timeToLive and/or timeToIdle is set or not. | ||
*/ | ||
def apply[V]( | ||
defaultLoader: Any ⇒ Future[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.
Was this formatted with scalariform?
import akka.http.scaladsl.model.headers.CacheDirectives._ | ||
import akka.http.scaladsl.server.RouteResult.Rejected | ||
|
||
trait CachingDirectives { |
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.
Each exposed directives should be documented in a separate page in the docs
project and listed in the alphabetically and by-trait pages. The spray cache directives docs look like a good start.
Some minimal documentation introducing caching based on
https://github.com/spray/spray/blob/master/docs/documentation/spray-caching/index.rst would also be great. This would also be a good place to make it clear that using the caching directives requires adding an additional dependency.
@@ -0,0 +1,92 @@ | |||
package akka.http.routing.directives |
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.
The package for the Scala directives should be akka.http.scaladsl.server.directives
@jonas thanks for the feedback, I've pushed some changes plus docs/examples for the caching directives. |
Test FAILed. |
Attempting to get this merged today, thanks for patiently working on this for such a long time! |
PLS BUILD |
Test FAILed. |
Awesome work @tomrf1! I believe this is ready to merge and I have some slight follow up changes I'll PR separately. I checked the comments that @jrudolph had and those are addressed as well or I'll do so in the PR I have prepped. I'll finish figuring out why the build fails with |
It could be #1371 |
I started porting the doc here: https://github.com/jonas/akka-http/tree/caching-docs |
Yeah it's the symlink that did not get created and blows up the PR now https://github.com/akka/akka-http/pull/1371/files#r147919705 |
PLS BUILD |
Test FAILed. |
PLS BUILD |
Test PASSed. |
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.
Rather, #1503 should be merged, as I cleaned up some things in it.
Guided by the discussion here (@ktoso, @ben-manes and @ianclegg) this change migrates the Spray cache and cache directive across to a separate project, akka-http-caching.
This is an almost like-for-like replacement of the spray implementation (including the tests). I've generally left the code the same where possible, but there are some changes:
ConcurrentLinkedHashMap
forLruCache
, it now uses caffeine'sAsyncLoadingCache
.ascendingKeys
from theCache
trait becauseAsyncLoadingCache
doesn't appear to have an equivalent method. I'm also not sure when you'd use it.ExecutionContext
fromapply
methods because it was only being used for completing the futures, whereas now it simply callsget
on the caffeine cache. Does anyone think this will be a problem?AsyncLoadingCache
requires a loader on creation, a 'default' loader function must be passed in when creating aLruCache
. Calling code can still pass in a separate loading function toapply
.def remove(key: Any): Option[Future[V]]
todef remove(key: Any): Unit
, as the value is unlikely to be needed