-
Notifications
You must be signed in to change notification settings - Fork 50
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
FS-37 Cache Algebra And Implementations #70
Conversation
We add the simple algebra of Cache Operations, to represent key-value stores. We provide a simple implementation, based on a java Concurrent Hash Map.
We add an implementation of the cache algebra, where the cache is backed in a Redis data-store. For the connection with the data store, we need a client library for Scala. We use Scredis.
We replace the previous ad-hoc type ScredisOps with cats.data.Kleisli, so as to reuse the functions already defined for this type.
We add a simple test example, to show the combination of applicative ( (parallel) fragments and the sequential ones. We also rename the package for the scredis subfolder.x
We replace Scredis by Rediscala as our Scala library client for Redis. Scredis does not seem to be actively maintained.
We refactor the `freestyle-cache` module. We remove from the `freestyle.cache` package any implementation details based on a Java Concurrent HashMap, and we move those to the `hashmap` subpackage. In the `freestyle.cache.implicits`, we leave a simple constructor for interpreters, based on two steps: a raw implementation, and a natural transformation.
After the previous refactoring, the Cache Module already separates between generating a _raw_ format (in the Redis it's Future) and the Natural Transformation to another carrier `F[_]`.
Codecov Report
@@ Coverage Diff @@
## master #70 +/- ##
==========================================
+ Coverage 57.23% 61.75% +4.51%
==========================================
Files 20 27 +7
Lines 159 217 +58
Branches 0 1 +1
==========================================
+ Hits 91 134 +43
- Misses 68 83 +15 Continue to review full report at Codecov.
|
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.
@diesalbla Excellent work! This is very exciting! Added a few ideas but we can add a new ticket for those improvements 👍 🎉
def has(key: Key): FreeS.Par[F, Boolean] | ||
|
||
// Returns the set of keys in the store | ||
def keys: FreeS.Par[F, Seq[Key]] |
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.
Since the default Seq
imported in Predef is mutable and pattern matching is easier over List
can this return something else that is not Seq
?
def hashCode(a: A): Int | ||
} | ||
|
||
object Hasher { |
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.
We can include here automatic instances for any product type such as case classes, HList
, TupleN
etc with shapeless generic derivation given there is also an implicit Hasher
for the contained elements.
import freestyle.implicits._ | ||
import org.scalatest._ | ||
|
||
class CacheTests extends WordSpec with Matchers with CacheTestContext { |
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.
Nice 👏
def get(key: Key): FreeS.Par[F, Option[Val]] | ||
|
||
// Sets the value of a key to a newValue. | ||
def put(key: Key, newVal: Val): FreeS.Par[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.
I would add the following methods if we can support them both on the InMemory and Redis impl.
def putAll(keyValues: Map[Key, Val]): FreeS.Par[F, Unit]
def putIfAbsent(key: Key, newVal: Val): FreeS.Par[F, Unit]
def replace(key: Key, newVal: Val): FreeS.Par[F, Unit]
def isEmpty: FreeS.Par[F, Boolean]
def containsKey(key: Key): FreeS.Par[F, Boolean]
def containsValue(v: Val): FreeS.Par[F, Boolean]
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 methods putAll
and putIfAbsent
are straight-forward. AAMOF, the latter is directly supported in redis. The command isEmpty
can be supported efficiently through the use of the scan
redis command. The containsValue
is the one that could be less efficient to implement.
In any case, this can be left to a low-hanging-fruit ticket.
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.
Sounds good, since you seem to have more insight, would you mind creating one and tagging it as such? thanks! @anamariamv Is getting started with Freestyle and she could take on that task as well.
@@ -5,6 +5,13 @@ import simulacrum.typeclass | |||
|
|||
import scala.util.Try | |||
|
|||
/* |
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.
Thanks!
The implementation of the `keys` operation in the Hashmap based was using a `2.12` API operation, unavailable in `2.11` for crossbuild.
22b6ed3
to
289d500
Compare
override def keys: Ops[M, Seq[Key]] = | ||
RediscalaCont.keys.transform(toM).map(_.map(parser).flatten) | ||
override def keys: Ops[M, List[Key]] = | ||
RediscalaCont.keys.transform(toM).map(_.map(parser).flatten.toList) |
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.
Can this be simplified to flatMap
?
This PR fulfills ticket #37. We introduce two new
sbt
modules,freestyle-cache
andfreestyle-cache-redis
, that define an algebra of cache operations, and two interpreters for it. In this ticket, we understand by cache an efficient, high-load, and potentially parallel, key-value store.Algebra
In the
sbt
projectfreestyle-cache
, in the packagefreestyle.cache
, we define an algebra named@free trait CacheM[F[_]]
, that describes a type-class of carrier typesF[_]
that support cache operations. This algebra gives the usual operations for Key-Value store:get
,put
,delete
,hasKey
,keys
(for key-set),clear
.Because we want the
CacheM
trait to be generic on the type of indexing keys and on the type of stored values, we wrap the@free
algebra inside an emptyKeyValueProvider[K,V]
class. Besides the algebra we provide animplicit
method to build an interpreter for the algebra (with the sameK
andV
types). This interpreter is built out of two pieces.F[_]
, as an object that inherits from traitKeyValueMap[F,K,V]
. The reason for this is that in some implementations the raw formatF
is fixed.F
to the desired oneG
.HashMap
In the same project, in the package
freestyle.cache.hashmap
, we provide an implementation for an interpreter of the algebra, in which we use as a store aConcurrentHashMap
from thejava.util
library.ConcurrentHashMap
is based on the use of the methodshashcode
andequals
, which unfortunately is not always properly implemented for all types. We introduce a type-classHashable
, to indicate that theKey
type must provide a hashing method.Redis
In the project
freestyle-cache-redis
, in the packagefreestyle.cache.redis
, we implement an interpreter for the algebra based on an external Redis data-store. For the implementation, we use the Redis client libraryrediscala
, which is based inakka
.In this interpreter, the notion of parallelization is based on the use of Redis transactions, which for our purposes allows us to group a sequence of data-independent operations and get them sent and executed atomically by the redis server. This parallelization is implemented using the
Applicative
instance ofKleisli
, where theKleisli
is used to defer the communications to the client.@raulraja ¿Could you have it reviewed?