All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.gvolpe.fs2redis.interpreter.streams.Fs2Streaming.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2018 Fs2 Redis
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.github.gvolpe.fs2redis.interpreter.streams

import cats.effect.Concurrent
import cats.effect.concurrent.Ref
import cats.instances.list._
import cats.syntax.all._
import com.github.gvolpe.fs2redis.algebra.Streaming
import com.github.gvolpe.fs2redis.interpreter.connection.Fs2RedisMasterSlave
import com.github.gvolpe.fs2redis.model._
import com.github.gvolpe.fs2redis.util.{JRFuture, Log}
import fs2.Stream
import io.lettuce.core.{ReadFrom, RedisURI}

object Fs2Streaming {

  def mkStreamingConnection[F[_], K, V](client: Fs2RedisClient, codec: Fs2RedisCodec[K, V], uri: RedisURI)(
      implicit F: Concurrent[F],
      L: Log[F]): Stream[F, Streaming[Stream[F, ?], K, V]] = {
    val acquire = JRFuture
      .fromConnectionFuture {
        F.delay(client.underlying.connectAsync[K, V](codec.underlying, uri))
      }
      .map(c => new Fs2RawStreaming(c))

    val release: Fs2RawStreaming[F, K, V] => F[Unit] = c =>
      JRFuture.fromCompletableFuture(F.delay(c.client.closeAsync())) *>
        L.info(s"Releasing Streaming connection: $uri")

    Stream.bracket(acquire)(release).map(rs => new Fs2Streaming(rs))
  }

  def mkMasterSlaveConnection[F[_]: Concurrent: Log, K, V](codec: Fs2RedisCodec[K, V], uris: RedisURI*)(
      readFrom: Option[ReadFrom] = None): Stream[F, Streaming[Stream[F, ?], K, V]] =
    Fs2RedisMasterSlave.stream[F, K, V](codec, uris: _*)(readFrom).map { conn =>
      new Fs2Streaming(new Fs2RawStreaming(conn.underlying))
    }

}

class Fs2Streaming[F[_]: Concurrent, K, V](rawStreaming: Fs2RawStreaming[F, K, V])
    extends Streaming[Stream[F, ?], K, V] {

  private[streams] val nextOffset: K => StreamingMessageWithId[K, V] => StreamingOffset[K] =
    key => msg => StreamingOffset.Custom(key, (msg.id.value.dropRight(2).toLong + 1).toString)

  private[streams] val offsetsByKey: List[StreamingMessageWithId[K, V]] => Map[K, Option[StreamingOffset[K]]] =
    list => list.groupBy(_.key).map { case (k, values) => k -> values.lastOption.map(nextOffset(k)) }

  override def append: Stream[F, StreamingMessage[K, V]] => Stream[F, Unit] =
    _.evalMap(msg => rawStreaming.xAdd(msg.key, msg.body).void)

  override def read(keys: Set[K], initialOffset: K => StreamingOffset[K]): Stream[F, StreamingMessageWithId[K, V]] = {
    val initial = keys.map(k => k -> initialOffset(k)).toMap
    Stream.eval(Ref.of[F, Map[K, StreamingOffset[K]]](initial)).flatMap { ref =>
      (for {
        offsets    <- Stream.eval(ref.get)
        list       <- Stream.eval(rawStreaming.xRead(offsets.values.toSet))
        newOffsets = offsetsByKey(list).collect { case (key, Some(value)) => key -> value }.toList
        _          <- Stream.eval(newOffsets.map { case (k, v) => ref.update(_.updated(k, v)) }.sequence)
        result     <- Stream.fromIterator(list.iterator)
      } yield result).repeat
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy