Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a good way to implement reloading of a Typesafe config

In a Scala application that is using Typesafe Config, I want to add the possibility to reload a Config at runtime. A Config instance is immutable. Here is what I have so far:

package config

trait Settings {
  private[config] var config: Config = ConfigFactory.empty()
  def engine: EngineSettings
}

trait EngineSettings {
  def weight: Int
  def offset: Int
}

class AppSettings {
  override def engine = new EngineSettings {
    override def weight = config.getInt("engine.weight")
    override def offset = config.getInt("engine.offset")
  }
}

object Settings {
  private val namedSettings = new TrieMap[String, AppSettings]
  def load(configName: String = "local"): Settings = {
    // load config
    // create or update AppSettings
    // add to map and return
  }
}

Initially a Settings instance is created using Settings.load. That instance reference is handed to other classes. Then a second thread can reload the underlying Config by calling Settings.load again. Here is how you access it:

class Engine(settings: Settings) {
  def calculate() = {
    val weight = settings.engine.weight
    // do some stuff
    val offset = settings.engine.offset
  }
}

There are two problems:

  1. someone might reload the underlying Config while calculate() is at line: // do some stuff (consistency)
  2. don't like using a var in the Settings trait

How can I improve this design :)

like image 767
reikje Avatar asked Aug 13 '14 07:08

reikje


People also ask

What is ConfigFactory?

Configurations from a fileBy default, the ConfigFactory looks for a configuration file called application. conf. If willing to use a different configuration file (e.g.: another. conf), we just need to indicate a different file name and path to load (e.g.: ConfigFactory. load("another")).

What is ConfigFactory Java?

public final class ConfigFactory extends java.lang.Object. Contains static methods for creating Config instances. See also ConfigValueFactory which contains static methods for converting Java values into a ConfigObject . You can then convert a ConfigObject into a Config with ConfigObject.


1 Answers

You could turn config into a method with support for config cache invalidation (and with sensible defaults), so you can choose between dynamic (default in the following sample) and performance.

In general I suggest you use a good Scala typesafe wrapper of TypeSafe's Config such as Ficus (e.g. Gradle-stype artifact dependency net.ceedubs:ficus_2.11:1.1.1)

package config

import scala.collection.concurrent.TrieMap

import com.typesafe.config.{Config, ConfigFactory}
import net.ceedubs.ficus.Ficus._

trait Settings {
  protected[config] def config (
    name: String = "local",
    invalidateCache: Boolean = false
  ): Config = {
    if (invalidateCache) { ConfigFactory invalidateCaches }
    ConfigFactory load name
  }
  def engine: EngineSettings
}

trait EngineSettings {
  def weight: Int
  def offset: Int
}

class AppSettings(val name: String = "local") extends Settings {
  val c = config()

  override def engine = new EngineSettings {
    override def weight = c.as[Int]("engine.weight")
    override def offset = c.as[Int]("engine.offset")
  }
}

object Settings {
  private val namedSettings = new TrieMap[String, AppSettings]
  def load(configName: String = "local"): Settings = {
    // e.g.
    val loadedUpToDate = new AppSettings
    namedSettings +=
      ((configName + "." + System.currentTimeMillis, loadedUpToDate))
    new Settings {
      override def engine = loadedUpToDate.engine
    }
  }
}

I think this solves your issues because:

  1. Configuration retrieval is dynamic by default through reload
  2. By using a method you don't resort to mutable state
like image 169
circlespainter Avatar answered Sep 24 '22 23:09

circlespainter