Skip to content

Commit 9e80d8f

Browse files
committed
fix(config): synchronize rootConfig publication and access
- guard global rootConfig pointer with RWMutex-backed accessors - make config.Load build a local RootConfig and initialize before publishing - route config consumers through GetRootConfig instead of direct global reads - avoid in-place global mutation in SetConsumerConfig - add concurrent Set/Get regression test for root config Fixes: #3247
1 parent b73e935 commit 9e80d8f

File tree

7 files changed

+102
-47
lines changed

7 files changed

+102
-47
lines changed

config/config_loader.go

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package config
1919

2020
import (
2121
"errors"
22+
"sync"
2223
)
2324

2425
import (
@@ -34,9 +35,22 @@ import (
3435
)
3536

3637
var (
37-
rootConfig = NewRootConfigBuilder().Build()
38+
rootConfigMu sync.RWMutex
39+
rootConfig = NewRootConfigBuilder().Build()
3840
)
3941

42+
func getRootConfigInternal() *RootConfig {
43+
rootConfigMu.RLock()
44+
defer rootConfigMu.RUnlock()
45+
return rootConfig
46+
}
47+
48+
func setRootConfigInternal(rc *RootConfig) {
49+
rootConfigMu.Lock()
50+
defer rootConfigMu.Unlock()
51+
rootConfig = rc
52+
}
53+
4054
func init() {
4155
log := zap.NewDefault()
4256
logger.SetLogger(log)
@@ -45,39 +59,40 @@ func init() {
4559
func Load(opts ...LoaderConfOption) error {
4660
// conf
4761
conf := NewLoaderConf(opts...)
62+
loadConfig := conf.rc
63+
4864
if conf.rc == nil {
65+
loadConfig = NewRootConfigBuilder().Build()
4966
koan := GetConfigResolver(conf)
5067
koan = conf.MergeConfig(koan)
51-
if err := koan.UnmarshalWithConf(rootConfig.Prefix(),
52-
rootConfig, koanf.UnmarshalConf{Tag: "yaml"}); err != nil {
68+
if err := koan.UnmarshalWithConf(loadConfig.Prefix(),
69+
loadConfig, koanf.UnmarshalConf{Tag: "yaml"}); err != nil {
5370
return err
5471
}
55-
} else {
56-
rootConfig = conf.rc
5772
}
5873

59-
if err := rootConfig.Init(); err != nil {
74+
if err := loadConfig.Init(); err != nil {
6075
return err
6176
}
6277
return nil
6378
}
6479

6580
func check() error {
66-
if rootConfig == nil {
81+
if GetRootConfig() == nil {
6782
return errors.New("execute the config.Load() method first")
6883
}
6984
return nil
7085
}
7186

7287
// GetRPCService get rpc service for consumer
7388
func GetRPCService(name string) common.RPCService {
74-
return rootConfig.Consumer.References[name].GetRPCService()
89+
return GetRootConfig().Consumer.References[name].GetRPCService()
7590
}
7691

7792
// RPCService create rpc service for consumer
7893
func RPCService(service common.RPCService) {
7994
ref := common.GetReference(service)
80-
rootConfig.Consumer.References[ref].Implement(service)
95+
GetRootConfig().Consumer.References[ref].Implement(service)
8196
}
8297

8398
// GetMetricConfig find the MetricsConfig
@@ -95,17 +110,17 @@ func GetMetricConfig() *MetricsConfig {
95110
// }
96111
//}
97112
//return GetBaseConfig().Metrics
98-
return rootConfig.Metrics
113+
return GetRootConfig().Metrics
99114
}
100115

101116
func GetTracingConfig(tracingKey string) *TracingConfig {
102-
return rootConfig.Tracing[tracingKey]
117+
return GetRootConfig().Tracing[tracingKey]
103118
}
104119

105120
func GetMetadataReportConfg() *MetadataReportConfig {
106-
return rootConfig.MetadataReport
121+
return GetRootConfig().MetadataReport
107122
}
108123

109124
func IsProvider() bool {
110-
return len(rootConfig.Provider.Services) > 0
125+
return len(GetRootConfig().Provider.Services) > 0
111126
}

config/consumer_config.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func (cc *ConsumerConfig) Load() {
133133
// use interface name defined by pb
134134
refConfig.InterfaceName = triplePBService.XXX_InterfaceName()
135135
}
136-
if err := refConfig.Init(rootConfig); err != nil {
136+
if err := refConfig.Init(GetRootConfig()); err != nil {
137137
logger.Errorf(fmt.Sprintf("reference with registeredTypeName = %s init failed! err: %#v", registeredTypeName, err))
138138
continue
139139
}
@@ -185,7 +185,14 @@ func (cc *ConsumerConfig) Load() {
185185

186186
// SetConsumerConfig sets consumerConfig by @c
187187
func SetConsumerConfig(c ConsumerConfig) {
188-
rootConfig.Consumer = &c
188+
rc := GetRootConfig()
189+
if rc == nil {
190+
SetRootConfig(RootConfig{Consumer: &c})
191+
return
192+
}
193+
next := *rc
194+
next.Consumer = &c
195+
SetRootConfig(next)
189196
}
190197

191198
func newEmptyConsumerConfig() *ConsumerConfig {

config/graceful_shutdown.go

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,12 @@ func gracefulShutdownInit() {
6363
if !exist {
6464
return
6565
}
66-
if filter, ok := gracefulShutdownConsumerFilter.(Setter); ok && rootConfig.Shutdown != nil {
66+
rc := GetRootConfig()
67+
if filter, ok := gracefulShutdownConsumerFilter.(Setter); ok && rc != nil && rc.Shutdown != nil {
6768
filter.Set(constant.GracefulShutdownFilterShutdownConfig, GetShutDown())
6869
}
6970

70-
if filter, ok := gracefulShutdownProviderFilter.(Setter); ok && rootConfig.Shutdown != nil {
71+
if filter, ok := gracefulShutdownProviderFilter.(Setter); ok && rc != nil && rc.Shutdown != nil {
7172
filter.Set(constant.GracefulShutdownFilterShutdownConfig, GetShutDown())
7273
}
7374

@@ -127,21 +128,22 @@ func destroyAllRegistries() {
127128
func destroyProtocols() {
128129
logger.Info("Graceful shutdown --- Destroy protocols. ")
129130

130-
if rootConfig.Protocols == nil {
131+
rc := GetRootConfig()
132+
if rc == nil || rc.Protocols == nil {
131133
return
132134
}
133135

134-
consumerProtocols := getConsumerProtocols()
136+
consumerProtocols := getConsumerProtocols(rc)
135137

136-
destroyProviderProtocols(consumerProtocols)
138+
destroyProviderProtocols(rc, consumerProtocols)
137139
destroyConsumerProtocols(consumerProtocols)
138140
}
139141

140142
// destroyProviderProtocols destroys the provider's protocol.
141143
// if the protocol is consumer's protocol too, we will keep it
142-
func destroyProviderProtocols(consumerProtocols *gxset.HashSet) {
144+
func destroyProviderProtocols(rc *RootConfig, consumerProtocols *gxset.HashSet) {
143145
logger.Info("Graceful shutdown --- First destroy provider's protocols. ")
144-
for _, protocol := range rootConfig.Protocols {
146+
for _, protocol := range rc.Protocols {
145147
// the protocol is the consumer's protocol too, we can not destroy it.
146148
if consumerProtocols.Contains(protocol.Name) {
147149
continue
@@ -159,18 +161,19 @@ func destroyConsumerProtocols(consumerProtocols *gxset.HashSet) {
159161

160162
func waitAndAcceptNewRequests() {
161163
logger.Info("Graceful shutdown --- Keep waiting and accept new requests for a short time. ")
162-
if rootConfig.Shutdown == nil {
164+
rc := GetRootConfig()
165+
if rc == nil || rc.Shutdown == nil {
163166
return
164167
}
165168

166-
time.Sleep(rootConfig.Shutdown.GetConsumerUpdateWaitTime())
169+
time.Sleep(rc.Shutdown.GetConsumerUpdateWaitTime())
167170

168-
timeout := rootConfig.Shutdown.GetStepTimeout()
171+
timeout := rc.Shutdown.GetStepTimeout()
169172
// ignore this step
170173
if timeout < 0 {
171174
return
172175
}
173-
waitingProviderProcessedTimeout(rootConfig.Shutdown)
176+
waitingProviderProcessedTimeout(rc.Shutdown)
174177
}
175178

176179
func waitingProviderProcessedTimeout(shutdownConfig *ShutdownConfig) {
@@ -193,12 +196,13 @@ func waitingProviderProcessedTimeout(shutdownConfig *ShutdownConfig) {
193196
// for provider. It will wait for processing receiving requests
194197
func waitForSendingAndReceivingRequests() {
195198
logger.Info("Graceful shutdown --- Keep waiting until sending/accepting requests finish or timeout. ")
196-
if rootConfig == nil || rootConfig.Shutdown == nil {
199+
rc := GetRootConfig()
200+
if rc == nil || rc.Shutdown == nil {
197201
// ignore this step
198202
return
199203
}
200-
rootConfig.Shutdown.RejectRequest.Store(true)
201-
waitingConsumerProcessedTimeout(rootConfig.Shutdown)
204+
rc.Shutdown.RejectRequest.Store(true)
205+
waitingConsumerProcessedTimeout(rc.Shutdown)
202206
}
203207

204208
func waitingConsumerProcessedTimeout(shutdownConfig *ShutdownConfig) {
@@ -217,21 +221,22 @@ func waitingConsumerProcessedTimeout(shutdownConfig *ShutdownConfig) {
217221

218222
func totalTimeout() time.Duration {
219223
timeout := defaultShutDownTime
220-
if rootConfig.Shutdown != nil && rootConfig.Shutdown.GetTimeout() > timeout {
221-
timeout = rootConfig.Shutdown.GetTimeout()
224+
rc := GetRootConfig()
225+
if rc != nil && rc.Shutdown != nil && rc.Shutdown.GetTimeout() > timeout {
226+
timeout = rc.Shutdown.GetTimeout()
222227
}
223228

224229
return timeout
225230
}
226231

227232
// we can not get the protocols from consumerConfig because some protocol don't have configuration, like jsonrpc.
228-
func getConsumerProtocols() *gxset.HashSet {
233+
func getConsumerProtocols(rc *RootConfig) *gxset.HashSet {
229234
result := gxset.NewSet()
230-
if rootConfig.Consumer == nil || rootConfig.Consumer.References == nil {
235+
if rc == nil || rc.Consumer == nil || rc.Consumer.References == nil {
231236
return result
232237
}
233238

234-
for _, reference := range rootConfig.Consumer.References {
239+
for _, reference := range rc.Consumer.References {
235240
result.Add(reference.Protocol)
236241
}
237242
return result

config/metadata_config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func initMetadata(rc *RootConfig) error {
6363
func getMetadataPort(rc *RootConfig) int {
6464
port := rc.Application.MetadataServicePort
6565
if port == "" {
66-
protocolConfig, ok := rootConfig.Protocols[constant.DefaultProtocol]
66+
protocolConfig, ok := rc.Protocols[constant.DefaultProtocol]
6767
if ok {
6868
port = protocolConfig.Port
6969
} else {

config/provider_config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ func (c *ProviderConfig) Load() {
187187
serviceConfig = NewServiceConfigBuilder().Build()
188188
// use interface name defined by pb
189189
serviceConfig.Interface = supportPBPackagerNameService.XXX_InterfaceName()
190-
if err := serviceConfig.Init(rootConfig); err != nil {
190+
if err := serviceConfig.Init(GetRootConfig()); err != nil {
191191
logger.Errorf("Service with registeredTypeName = %s init failed with error = %#v", registeredTypeName, err)
192192
}
193193
serviceConfig.adaptiveService = c.AdaptiveService

config/root_config.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ type RootConfig struct {
6464
}
6565

6666
func SetRootConfig(r RootConfig) {
67-
rootConfig = &r
67+
rc := r
68+
setRootConfigInternal(&rc)
6869
}
6970

7071
// Prefix dubbo
@@ -73,37 +74,41 @@ func (rc *RootConfig) Prefix() string {
7374
}
7475

7576
func GetRootConfig() *RootConfig {
76-
return rootConfig
77+
return getRootConfigInternal()
7778
}
7879

7980
func GetProviderConfig() *ProviderConfig {
80-
if err := check(); err == nil && rootConfig.Provider != nil {
81-
return rootConfig.Provider
81+
rc := GetRootConfig()
82+
if err := check(); err == nil && rc.Provider != nil {
83+
return rc.Provider
8284
}
8385
return NewProviderConfigBuilder().Build()
8486
}
8587

8688
func GetConsumerConfig() *ConsumerConfig {
87-
if err := check(); err == nil && rootConfig.Consumer != nil {
88-
return rootConfig.Consumer
89+
rc := GetRootConfig()
90+
if err := check(); err == nil && rc.Consumer != nil {
91+
return rc.Consumer
8992
}
9093
return NewConsumerConfigBuilder().Build()
9194
}
9295

9396
func GetApplicationConfig() *ApplicationConfig {
94-
return rootConfig.Application
97+
return GetRootConfig().Application
9598
}
9699

97100
func GetShutDown() *ShutdownConfig {
98-
if err := check(); err == nil && rootConfig.Shutdown != nil {
99-
return rootConfig.Shutdown
101+
rc := GetRootConfig()
102+
if err := check(); err == nil && rc.Shutdown != nil {
103+
return rc.Shutdown
100104
}
101105
return NewShutDownConfigBuilder().Build()
102106
}
103107

104108
func GetTLSConfig() *TLSConfig {
105-
if err := check(); err == nil && rootConfig.TLSConfig != nil {
106-
return rootConfig.TLSConfig
109+
rc := GetRootConfig()
110+
if err := check(); err == nil && rc.TLSConfig != nil {
111+
return rc.TLSConfig
107112
}
108113
return NewTLSConfigBuilder().Build()
109114
}

config/root_config_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package config
1919

2020
import (
21+
"fmt"
22+
"sync"
2123
"testing"
2224
)
2325

@@ -90,3 +92,24 @@ func TestNewRootConfigBuilder(t *testing.T) {
9092
config := GetRootConfig()
9193
assert.Equal(t, rootConfig, config)
9294
}
95+
96+
func TestRootConfigConcurrentSetAndGet(t *testing.T) {
97+
SetRootConfig(*NewRootConfigBuilder().Build())
98+
99+
var wg sync.WaitGroup
100+
for i := 0; i < 200; i++ {
101+
wg.Add(2)
102+
go func(idx int) {
103+
defer wg.Done()
104+
cfg := NewRootConfigBuilder().Build()
105+
cfg.Application.Name = fmt.Sprintf("app-%d", idx)
106+
SetRootConfig(*cfg)
107+
}(i)
108+
go func() {
109+
defer wg.Done()
110+
_ = GetRootConfig()
111+
}()
112+
}
113+
wg.Wait()
114+
assert.NotNil(t, GetRootConfig())
115+
}

0 commit comments

Comments
 (0)