lightbend / config

configuration library for JVM languages using HOCON files
https://lightbend.github.io/config/
6.17k stars 966 forks source link

Note about order of `reference.conf` resources loading during initialization of Config. #342

Open bespalovdn opened 9 years ago

bespalovdn commented 9 years ago

I think it worth to mention in documentation, about how to control the order of reference.conf files merging, when you initialize your configuration. For instance, commonly used library akka-actor have own configuration in reference.conf. I've created a library, that hardly uses akka-actor and redefines some of theirs configuration in own reference.conf. Then I try to get them both merged on ConfigFactory.load(). But there is no guarantee in which order those reference.confs will be read from resources. I did some investigation before came up with solution. Here is corresponding question on stackoverflow: http://stackoverflow.com/questions/31698605/hocon-multiple-reference-conf-resolution-issue/31727841 Please, consider to mention in your documentation about how to control the order of reference.confs merging.

Thanks!

manuzhang commented 7 years ago

Any updates here ? I'm running into an issue where my src/main/resources/reference.conf doesn't take effect and have no clue till I see this thread. I wonder why users' reference.conf don't take precedence

havocp commented 7 years ago

I don't know of a real answer here. The design intent is to have application.conf separate from reference.conf specifically so it can be ordered on top. For resources with the same name, they are kept in the order the class loader returns them, but afaik there are no strong guarantees about that. The same key really isn't supposed to be in multiple reference.conf.

manuzhang commented 7 years ago

"users" here actually means the library built on Akka. A typical configuration is akka.actor.provider=akka.remote.RemoteActorRefProvider for akka remoting to work. If we put configurations in application.conf, then the end users' application.conf may not take effect.

ktoso commented 7 years ago

There's no way to determine "user's reference.conf" they're all on classpath, it depends on order of loading stuff.

If you build middleware introduce your own config and load it like application.conf withFallback reference-overrides.conf withFallback reference.conf, just like Play does.

reference.conf is just that, reference, and any aproach to make it layered built-in will cause the same kind of issue on the layer you've just added. Thus the solution is to document how to load configs.

I propose this should be closed.

havocp commented 7 years ago

I can imagine some useful docs to add in this area (describing a solution like Play's?). Putting an application.conf in the library may also work in practice, since the application's own application.conf would probably be first on classpath in most cases, but I'm not 100% confident about that. I would put most library settings in reference.conf still and only the "overrides" in application.conf if trying that.

manuzhang commented 7 years ago

@ktoso Are you referring to https://github.com/playframework/playframework/blob/master/framework/src/play/src/main/scala/play/api/Configuration.scala#L35 ?

ktoso commented 7 years ago

I can imagine some useful docs to add in this area (describing a solution like Play's?).

Yeah, sounds like this could be a docs ticket.

Putting an application.conf in the library may also work in practice, since the application's own application.conf would probably be first on classpath in most cases, but I'm not 100% confident about that. I would put most library settings in reference.conf still and only the "overrides" in application.conf if trying that.

It would work for cases where no other application.conf wants to override given setting. But then you locked out people from changing that setting basically, since the ordering of "end user application.conf" with rehards to "library application.conf" is undefined again - so I would not recommend that to anyone..

Technically a "middle-man.conf" could override reference and be allowed to be overriden by application.conf - which would basically be codifying what play has been doing... We could I guess introduce a blessed name for the "middle-man"..?

@ktoso Are you referring to https://github.com/playframework/playframework/blob/master/framework/src/play/src/main/scala/play/api/Configuration.scala#L35 ?

Yes, as I said, they do:

      val combinedConfig: Config = Seq(
        systemPropertyConfig,
        directConfig,
        applicationConfig,
        playOverridesConfig,
        referenceConfig
      ).reduceLeft(_ withFallback _)
TimMoore commented 6 years ago

I think it would be helpful to detect and warn or fail on an overwrite within the same "layer".

Right now, conflicts can sometimes work by coincidence depending on the classpath order, and then fail in production in a surprising way because of an incidental change in classpath order.

havocp commented 6 years ago

could warn in one of the -Dconfig.trace modes which could be useful. we have a rule against printing anything by default (because if we do that with println people can’t control it, and we don’t depend on a log framework), so we only print warnings when the trace flags are turned on.

wsargent commented 6 years ago

Right now, conflicts can sometimes work by coincidence depending on the classpath order, and then fail in production in a surprising way because of an incidental change in classpath order.

This just happened to me and it is super annoying.

Currently, working plan is to install a java agent such that classloader.getResources returns the enumeration in the order I want, using https://stackoverflow.com/a/31727841/5266 as a guide.

wsargent commented 6 years ago

Numbered ordering scheme would work better...

TimMoore commented 6 years ago

Having a consistent ordering would be helpful to eliminate production-only surprises, but I think there's still a fundamental underlying issue. Having multiple reference.conf files that define the same property is not really a valid configuration, and even if the order were deterministic, it might not be what a particular application wants. The ambiguity needs to be detected and resolved in application.conf.

ktoso commented 6 years ago

I guess we could order on content hashing? This does not mean to make the problem to go away, but at least it would not be non-reproducable on the same set of dependencies.

huntc commented 5 years ago

Some code to reference from our library:

  val referenceOverrides =
    ConfigFactory.parseResources("your-reference-overrides.conf")
  val config =
    ConfigFactory.load(ConfigFactory.defaultApplication().withFallback(referenceOverrides))
wsargent commented 5 years ago

FYI, I fixed this by adding a byte-buddy agent and rewriting the calls to ConfigFactory.load.