zio and refilling a cache
In some small web app server projects I sometimes need to cache some data to reduce latency and the impact of queries on a “weak” backend database. We can use alot of clever caching tools but sometimes I just need something quick and dirty.
If you are using ZIO-style services, you may want the cache to be independent of other services and maintain the cache internally to the service environment. There are many ways to cache remote data, for example, you could also use ZQuery or the newer ZCache.
Let’s assume that we want to do something simple:
case class PeopleCache(
byId: Map[UUID, Person] = Map(),
last: LocalDateTime = LocalDateTime.now()
)
object PeopleCache:
def createFromList(people: List[Person]) = ???
trait Service:
def search(qstring: String): IO[Exception, Option[Person]
val live = ???
class PeopleServiceImpl(
cache: RefM[PeopleCache],
db: db.Service) extends Service:
def search(qstring: String) = cache.get.map(searchWithString(qstring, _))
We need to create a live service and update the cache every 30 minutes. It is quite simple using zio since we can compose effects into the layer definition. Once the layer is created and fed to our effectful program, the updating starts.
val live =
(for
db <- ZIO.service[DB]
cache <- ZRefM.make[PeopleCache](PeopleCache())
// update cache and update ref
_ <- cache
.update(old => refill(old.last, db))
.repeat(zioScheduleEvery30Minutes)
.forkDaemon
_ <-
yield PeopleServiceImpl(cache, db): Service)
.toLayer
// this replaces the cache, but you could just update
def refill(lastFill: LocalDateTime, db: db.Service) =
val paramBasedOnLastFill = ???
for
newCache <- fetchPeople(paramBasedOnLastFill)
.map(PeopleCache.createFromList(_))
yield newCache
Now our service will use whatever is in the ZRef. We use a ZRefM so that we can run the refill effect during the update operation. The update
operation gives us the old cache so we know when it was last filled and we create and fill a new cache. We use forkDaemon
and fork the fiber as a “daemon” fiber so that the fiber attaches to the parent. This allows us the program to cancel the refresh when the upper levels of the program terminate.
That’s it!
Comments
Post a Comment