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

com.evolutiongaming.kafka.journal.eventual.ReplicatedTopicJournal.scala Maven / Gradle / Ivy

The newest version!
package com.evolutiongaming.kafka.journal.eventual

import java.time.Instant

import cats.data.{NonEmptyMap => Nem}
import cats.effect.Resource
import cats.syntax.all._
import cats.{Applicative, Defer, Monad, ~>}
import com.evolutiongaming.catshelper.CatsHelper._
import com.evolutiongaming.catshelper.{BracketThrowable, Log, MonadThrowable}
import com.evolutiongaming.kafka.journal._
import com.evolutiongaming.skafka.{Offset, Partition, Topic}
import com.evolutiongaming.smetrics._


trait ReplicatedTopicJournal[F[_]] {
  import ReplicatedTopicJournal._

  def pointers: F[TopicPointers]

  def journal(id: String): Resource[F, ReplicatedKeyJournal[F]]

  def save(pointers: Nem[Partition, Offset], timestamp: Instant): F[Changed]
}

object ReplicatedTopicJournal {

  type Changed = Boolean


  def empty[F[_] : Applicative]: ReplicatedTopicJournal[F] = new ReplicatedTopicJournal[F] {

    def pointers = TopicPointers.empty.pure[F]

    def journal(id: String) = {
      ReplicatedKeyJournal
        .empty[F]
        .pure[F]
        .toResource
    }

    def save(pointers: Nem[Partition, Offset], timestamp: Instant) = false.pure[F]
  }


  def apply[F[_] : Applicative](
    topic: Topic,
    replicatedJournal: ReplicatedJournalFlat[F]
  ): ReplicatedTopicJournal[F] = {

    new ReplicatedTopicJournal[F] {

      def pointers = replicatedJournal.pointers(topic)

      def journal(id: String) = {
        val key = Key(id = id, topic = topic)
        ReplicatedKeyJournal(key, replicatedJournal)
          .pure[F]
          .toResource
      }

      def save(pointers: Nem[Partition, Offset], timestamp: Instant) = {
        replicatedJournal.save(topic, pointers, timestamp)
      }
    }
  }


  implicit class ReplicatedTopicJournalOps[F[_]](val self: ReplicatedTopicJournal[F]) extends AnyVal {

    def mapK[G[_]](
      f: F ~> G)(implicit
      B: BracketThrowable[F],
      D: Defer[G],
      G: Applicative[G]
    ): ReplicatedTopicJournal[G] = new ReplicatedTopicJournal[G] {

      def pointers = f(self.pointers)

      def journal(id: String) = {
        self
          .journal(id)
          .map(_.mapK(f))
          .mapK(f)
      }

      def save(pointers: Nem[Partition, Offset], timestamp: Instant) = {
        f(self.save(pointers, timestamp))
      }
    }


    def withLog(
      topic: Topic,
      log: Log[F])(implicit
      F: Monad[F],
      measureDuration: MeasureDuration[F]
    ): ReplicatedTopicJournal[F] = {

      new ReplicatedTopicJournal[F] {

        def pointers = {
          for {
            d <- MeasureDuration[F].start
            r <- self.pointers
            d <- d
            _ <- log.debug(s"$topic pointers in ${ d.toMillis }ms, result: $r")
          } yield r
        }

        def journal(id: String) = {
          self
            .journal(id)
            .map { _.withLog(Key(id = id, topic = topic), log) }
        }

        def save(pointers: Nem[Partition, Offset], timestamp: Instant) = {
          for {
            d <- MeasureDuration[F].start
            r <- self.save(pointers, timestamp)
            d <- d
            _ <- log.debug(s"$topic save in ${ d.toMillis }ms, pointers: ${ pointers.mkString_(",") }, timestamp: $timestamp")
          } yield r
        }
      }
    }


    def withMetrics(
      topic: Topic,
      metrics: ReplicatedJournal.Metrics[F])(implicit
      F: Monad[F],
      measureDuration: MeasureDuration[F]
    ): ReplicatedTopicJournal[F] = {
      new ReplicatedTopicJournal[F] {

        def pointers = {
          for {
            d <- MeasureDuration[F].start
            r <- self.pointers
            d <- d
            _ <- metrics.pointers(d)
          } yield r
        }

        def journal(id: String) = {
          self
            .journal(id)
            .map { _.withMetrics(topic, metrics) }
        }

        def save(pointers: Nem[Partition, Offset], timestamp: Instant) = {
          for {
            d <- MeasureDuration[F].start
            r <- self.save(pointers, timestamp)
            d <- d
            _ <- metrics.save(topic, d)
          } yield r
        }
      }
    }


    def enhanceError(
      topic: Topic)(implicit
      F: MonadThrowable[F]
    ): ReplicatedTopicJournal[F] = {

      def journalError[A](msg: String, cause: Throwable) = {
        JournalError(s"ReplicatedTopicJournal.$msg failed with $cause", cause)
      }

      new ReplicatedTopicJournal[F] {

        def pointers = {
          self
            .pointers
            .adaptError { case a => journalError(s"pointers topic: $topic", a) }
        }

        def journal(id: String) = {
          val key = Key(id = id, topic = topic)
          self
            .journal(id)
            .map { _.enhanceError(key) }
            .adaptError { case a => journalError(s"journal key: $key", a) }
        }

        def save(pointers: Nem[Partition, Offset], timestamp: Instant) = {
          self
            .save(pointers, timestamp)
            .adaptError { case a =>
              journalError(s"save " +
                s"topic: $topic, " +
                s"pointers: $pointers, " +
                s"timestamp: $timestamp", a)
            }
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy