Automatic resolution of shared component conflitcs caused by per-role config conflicts
pshirshov opened this issue · comments
Currently we may have effective role incompatibilities caused by the need in config variations. We may have a shared component S requiring config section C and two roles, R1 and R2. If these two roles need to have some value in C set to different/conflicting values we are screwed.
Current state
Currently we can only alleviate this problem by redesigning our application in order to avoid configuration conflicts.
For example:
Instead of having
role1.conf:
postgres = { uri = "..."}
role2.conf:
postgres = { uri = "..."}
We may write
shared.conf:
postgres = {
role1 = {uri = "..."}
role2 = {uri = "..."}
}
Then we'll need a smart connection component which would reuse underlying connection for the same URIs.
Desired changes
We might automate this.
The key idea is simple:
- While we trace the object graph, we should look for configurable components
- When we find such a component, we should check which roles retain it
- We should load role configs individually and check if there are conflicts in the corresponding sections. If there are no conflicts we may configure the component straight away. If there are conflicts, we should create multiple copies of the rest of the graph, one per conflicting version.
We can definitely do the splits because we know that the graph has specific shape: generally there are NO dependencies between the roots.
I can't see a way to fit this into existing tracing pass, so probably this should be done as a separate pass which happens right after semigraph resolution.
By doing this we'd lose the guarantee that all our components are singleton and make things too complex / untraceable. As for reading from a role config instead of shared config, we could make this explicit:
makeRoleConfig[PostgresConfig](Role.id)("postgres")