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

za.co.absa.spline.common.future.EstimableFuture.scala Maven / Gradle / Ivy

/*
 * Copyright 2017 ABSA Group Limited
 *
 * 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 za.co.absa.spline.common.future

import scala.collection.concurrent.{Map, TrieMap}
import scala.concurrent.duration.{Duration, _}
import scala.concurrent.{CanAwait, ExecutionContext, Future}
import scala.language.implicitConversions
import scala.util.Try

class EstimableFuture[+T](underlying: Future[T], avgDuration: () => Long) extends Future[T] {
  override def onComplete[U](f: Try[T] => U)(implicit executor: ExecutionContext): Unit = underlying.onComplete(f)

  override def isCompleted: Boolean = underlying.isCompleted

  override def value: Option[Try[T]] = underlying.value

  override def ready(atMost: Duration)(implicit permit: CanAwait): EstimableFuture.this.type = {
    underlying.ready(atMost)
    this
  }

  override def result(atMost: Duration)(implicit permit: CanAwait): T = underlying.result(atMost)

  lazy val estimatedDuration: Long = avgDuration()

  override def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S] = underlying.transform(f)

  override def transformWith[S](f: Try[T] => Future[S])(implicit executor: ExecutionContext): Future[S] = underlying.transformWith(f)
}

object EstimableFuture {

  trait Implicits {

    implicit class FutureToDeferredResultAdapterImpl[T](val future: Future[T]) extends FutureToDeferredResultAdapter[T]

  }

  private[this] val durationMeasurersByCategory: Map[String, MovingAverageCalculator] = TrieMap.empty

  trait FutureToDeferredResultAdapter[T] {
    val future: Future[T]

    def asEstimable(category: String)(implicit executor: ExecutionContext): EstimableFuture[T] = {
      val durationMeasurer = durationMeasurersByCategory.getOrElseUpdate(category, {
        new MovingAverageCalculator(10.seconds.toMillis, 0.05)
      })
      val startTime = System.currentTimeMillis
      future.foreach { case _ => durationMeasurer.addMeasurement(System.currentTimeMillis - startTime) }
      new EstimableFuture(future, durationMeasurer.currentAverage _)
    }
  }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy