package conf import ( "sync/atomic" "google.golang.org/protobuf/proto" ) // Runtime owns the active immutable configuration snapshot. Callers always // receive clones so a config file reload cannot race with request handling. type Runtime struct { snapshot atomic.Pointer[runtimeSnapshot] } type runtimeSnapshot struct { data *Data admin *AdminBackend } func NewRuntime(data *Data, admin *AdminBackend) *Runtime { runtime := &Runtime{} runtime.Replace(data, admin) return runtime } func cloneData(value *Data) *Data { if value == nil { return nil } return proto.Clone(value).(*Data) } func cloneAdmin(value *AdminBackend) *AdminBackend { if value == nil { return nil } return proto.Clone(value).(*AdminBackend) } func (r *Runtime) Replace(data *Data, admin *AdminBackend) { r.snapshot.Store(&runtimeSnapshot{data: cloneData(data), admin: cloneAdmin(admin)}) } func (r *Runtime) Data() *Data { snapshot := r.snapshot.Load() if snapshot == nil { return nil } return cloneData(snapshot.data) } func (r *Runtime) Admin() *AdminBackend { snapshot := r.snapshot.Load() if snapshot == nil { return nil } return cloneAdmin(snapshot.admin) } func (r *Runtime) Values() (*Data, *AdminBackend) { snapshot := r.snapshot.Load() if snapshot == nil { return nil, nil } return cloneData(snapshot.data), cloneAdmin(snapshot.admin) } func (r *Runtime) ConfigPath() string { snapshot := r.snapshot.Load() if snapshot == nil || snapshot.admin == nil { return "" } return snapshot.admin.ConfigPath } func (r *Runtime) UpdateDatabase(database *Data_Database) { for { current := r.snapshot.Load() var data *Data var admin *AdminBackend if current != nil { data = cloneData(current.data) admin = cloneAdmin(current.admin) } if data == nil { data = &Data{} } data.Database = proto.Clone(database).(*Data_Database) next := &runtimeSnapshot{data: data, admin: admin} if r.snapshot.CompareAndSwap(current, next) { return } } }