(Arrow keys to navigate, ? for more help)
port = 12345
(Maybe you can already see this starting to suck...)
udp.prod.properties
udp.dev.properties
udp.defaults-mhellige.properties
[prod]
port=123
[dev]
port=456
udp-a.prod.properties
udp-b.prod.properties
udp.a.port=123
udp.b.port=124
(But then where can I put defaults?)
env.prod env.dev
user.mhellige user.mgodbolt
host.'sud-chidev02'
udpListener.a udpListener.b webServer
env.prod { // this is the production config
udpListener.a : port = 123
udpListener.b : port = 456
}
env.dev udpListener : port = 12345
webServer {
port = 8080 // all environments
user.mhellige : port = 8081 /* i prefer this one */
}
@context
)We can construct a context path for each query and think of CCS as a pattern matching language for context paths.
desk.foo/exchange.NYSE/product.ABC > env.prod > dataCenter.unknown
> appConfig.smq > user.produsername > host.rh-abcdef13
> cpuType.haswell > module.core > threads > thread.TimerFiber
CCS lets us match on this path using conjunction, disjunction, and descendant operations, so with rules like
hungCommandThresholdMillis = 2000
cpuType.haswell : hungCommandThresholdMillis = 1000
thread.TimerFiber cpuType.haswell : hungCommandThresholdMillis = 5000
thread.TimerFiber cpuType.westmere : hungCommandThresholdMillis = 10000
We would find that
hungCommandThresholdMillis = 5000
(Hence, an analogy with DOM trees and CSS)
CcsContext config = ... // from wherever
auto threadCfg = config.constrain("thread", {"TimerFiber"})
auto componentCfg = threadCfg.constrain("someComponent");
auto anotherCfg = threadCfg.constrain("anotherThing");
(It's pretty clear how this results in a tree...)
#include <ccs/ccs.h>
using namespace ccs;
using namespace std;
void initWeb(CcsContext config) {
config = config.constrain("web");
auto host = config.get<string>("host");
auto port = config.get<int>("port");
connect(host, port);
}
This isn't much different than declaring file- or class-specific loggers, and could be further automated when appropriate.
We use the query API by hand, but of course it could be driven by reflection or whatever, too...
We also capture context from external sources like initialization scripts, deployment information, etc...
This uniformity really pays off!
@context
@import
@constrain
@override
// ABC and DEF share an ID...
product.ABC, product.DEF : selfMatchPreventionId = 1234567
product.GHI : selfMatchPreventionId = 7654321
// in either of these environments, this host is special...
(env.dev, env.lab) host.'sul-chialg56' {
useEfVi = true
}
a b c.d : prop = true
a { c.d : prop = true }
a b {
c.d {
prop = true
}
}
@context(a b)
c.d : prop = true
@import 'module1.ccs'
@import 'module2.ccs'
// imported only into given context...
module.timers : @import 'timers.ccs'
// Pseudo-env for known overloaded machines to run them with less noise
env.lab (host.'dev-chi22', host.'dev-chi50',
host.'dev-chi65', host.'dev-chi95') {
@constrain env.overloaded
}
someTimeoutMs = 1000
env.overloaded : someTimeoutMs = 10000
someTimeoutMs = 5000
thread.this : someTimeoutMs = 1000
thread.that : someTimeoutMs = 2000
cpuType.haswell {
thread.this : someTimeoutMs = 500
thread.that : someTimeoutMs = 800
}
// forget all of that in dev...
env.dev : @override someTimeoutMs = 100000
host = "google.com"
port = 80
timeout = 1
env.lab { /* in lab, use this config */
host = "lab-web"
port = 8080
}
// bob likes to use this host
user.bob : host = "lab-proxy"
web {
host = "google.com"
port = 80
}
log {
host = "localhost"
env.lab : host = "lab-log-host"
env.prod : host = "prod-log-host"
port = 514
}
timeout = 1
// this host is really slow:
host.'dev-chi22' : timeout = 10