-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cache config value to optimize Get config API #151
Open
x-xy-y
wants to merge
6
commits into
go-chassis:master
Choose a base branch
from
x-xy-y:feature/cache-config-value
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 3 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package benchmark | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"github.com/go-chassis/go-archaius" | ||
"github.com/stretchr/testify/assert" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
func createFile(content string, name string, dir string) string { | ||
filename := filepath.Join(dir, name) | ||
f1, _ := os.Create(filename) | ||
_, _ = io.WriteString(f1, content) | ||
f1.Close() | ||
return filename | ||
} | ||
|
||
func TestMain(m *testing.M) { | ||
var buff bytes.Buffer | ||
for i:=0;i<99;i++ { | ||
buff.WriteString(fmt.Sprintf("age%d: 1\n", i)) | ||
} | ||
buff.WriteString(fmt.Sprintf("age99: 0\n")) | ||
filename := createFile(buff.String(), "f1.yaml", "/tmp") | ||
defer os.Remove(filename) | ||
|
||
err := archaius.Init(archaius.WithOptionalFiles([]string{filename})) | ||
if err != nil { | ||
os.Exit(-1) | ||
} | ||
os.Exit(m.Run()) | ||
} | ||
|
||
func BenchmarkFileSource(b *testing.B) { | ||
s := 0 | ||
b.ResetTimer() | ||
|
||
for i := 0; i < b.N; i++ { | ||
s += archaius.GetInt("age99", 1) | ||
} | ||
assert.Zero(b, s) | ||
} | ||
|
||
func BenchmarkFileSourceParallelism(b *testing.B) { | ||
s := 0 | ||
b.ResetTimer() | ||
b.SetParallelism(200) | ||
b.RunParallel(func(pb *testing.PB) { | ||
for pb.Next() { | ||
s += archaius.GetInt("age99", 1) | ||
} | ||
}) | ||
assert.Zero(b, s) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,8 +36,8 @@ import ( | |
|
||
//errors | ||
var ( | ||
ErrKeyNotExist = errors.New("key does not exist") | ||
ErrIgnoreChange = errors.New("ignore key changed") | ||
ErrKeyNotExist = errors.New("key does not exist") | ||
ErrIgnoreChange = errors.New("ignore key changed") | ||
ErrWriterInvalid = errors.New("writer is invalid") | ||
) | ||
|
||
|
@@ -55,6 +55,8 @@ type Manager struct { | |
sourceMapMux sync.RWMutex | ||
Sources map[string]ConfigSource | ||
|
||
ConfigValueCache sync.Map | ||
|
||
ConfigurationMap sync.Map | ||
|
||
dispatcher *event.Dispatcher | ||
|
@@ -79,6 +81,7 @@ func (m *Manager) Cleanup() error { | |
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
|
@@ -139,8 +142,8 @@ func (m *Manager) Marshal(w io.Writer) error { | |
} | ||
allConfig := make(map[string]map[string]interface{}) | ||
for name, source := range m.Sources { | ||
config, err := source.GetConfigurations() | ||
if err != nil { | ||
config, err := source.GetConfigurations() | ||
if err != nil { | ||
openlog.Error("get source " + name + " error " + err.Error()) | ||
continue | ||
} | ||
|
@@ -215,12 +218,11 @@ func (m *Manager) pullSourceConfigs(source string) error { | |
func (m *Manager) Configs() map[string]interface{} { | ||
config := make(map[string]interface{}, 0) | ||
|
||
m.ConfigurationMap.Range(func(key, value interface{}) bool { | ||
sValue := m.configValueBySource(key.(string), value.(string)) | ||
if sValue == nil { | ||
m.ConfigValueCache.Range(func(key, value interface{}) bool { | ||
if value == nil { | ||
return true | ||
} | ||
config[key.(string)] = sValue | ||
config[key.(string)] = value | ||
return true | ||
}) | ||
|
||
|
@@ -236,8 +238,8 @@ func (m *Manager) ConfigsWithSourceNames() map[string]interface{} { | |
config := make(map[string]interface{}, 0) | ||
|
||
m.ConfigurationMap.Range(func(key, value interface{}) bool { | ||
sValue := m.configValueBySource(key.(string), value.(string)) | ||
if sValue == nil { | ||
sValue, ok := m.ConfigValueCache.Load(key.(string)) | ||
if !ok || sValue == nil { | ||
return true | ||
} | ||
// each key stores its value and source name | ||
|
@@ -317,57 +319,35 @@ func (m *Manager) IsKeyExist(key string) bool { | |
|
||
// GetConfig returns the value for a particular key from cache | ||
func (m *Manager) GetConfig(key string) interface{} { | ||
sourceName, ok := m.ConfigurationMap.Load(key) | ||
if !ok { | ||
return nil | ||
} | ||
return m.configValueBySource(key, sourceName.(string)) | ||
val, _ := m.ConfigValueCache.Load(key) | ||
return val | ||
} | ||
|
||
func (m *Manager) updateConfigurationMap(source ConfigSource, configs map[string]interface{}) error { | ||
for key := range configs { | ||
sourceName, ok := m.ConfigurationMap.Load(key) | ||
if !ok { // if key do not exist then add source | ||
m.ConfigurationMap.Store(key, source.GetSourceName()) | ||
continue | ||
} | ||
|
||
m.sourceMapMux.RLock() | ||
currentSource, ok := m.Sources[sourceName.(string)] | ||
m.sourceMapMux.RUnlock() | ||
if !ok { | ||
m.ConfigurationMap.Store(key, source.GetSourceName()) | ||
continue | ||
} | ||
|
||
currentSrcPriority := currentSource.GetPriority() | ||
if currentSrcPriority > source.GetPriority() { // lesser value has high priority | ||
m.ConfigurationMap.Store(key, source.GetSourceName()) | ||
val, err := source.GetConfigurationByKey(key) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (m *Manager) updateConfigurationMapByDI(source ConfigSource, configs map[string]interface{}) error { | ||
for key := range configs { | ||
sourceName, ok := m.ConfigurationMap.Load(key) | ||
if !ok { // if key do not exist then add source | ||
m.ConfigurationMap.Store(key, source.GetSourceName()) | ||
m.updateCache(source.GetSourceName(), key, val) | ||
continue | ||
} | ||
|
||
m.sourceMapMux.RLock() | ||
currentSource, ok := m.Sources[sourceName.(string)] | ||
m.sourceMapMux.RUnlock() | ||
if !ok { | ||
m.ConfigurationMap.Store(key, source.GetSourceName()) | ||
m.updateCache(source.GetSourceName(), key, val) | ||
continue | ||
} | ||
|
||
currentSrcPriority := currentSource.GetPriority() | ||
if currentSrcPriority > source.GetPriority() { // lesser value has high priority | ||
m.ConfigurationMap.Store(key, source.GetSourceName()) | ||
m.updateCache(source.GetSourceName(), key, val) | ||
} | ||
} | ||
|
||
|
@@ -409,23 +389,32 @@ func (m *Manager) updateEvent(e *event.Event) error { | |
return nil | ||
} | ||
openlog.Info("config update event received") | ||
|
||
switch e.EventType { | ||
case event.Create, event.Update: | ||
|
||
sourceName, ok := m.ConfigurationMap.Load(e.Key) | ||
val := m.configValueBySource(e.Key, e.EventSource) | ||
if !ok { | ||
m.ConfigurationMap.Store(e.Key, e.EventSource) | ||
m.updateCache(e.EventSource, e.Key, val) | ||
e.EventType = event.Create | ||
} else if sourceName == e.EventSource { | ||
|
||
m.updateCache(e.EventSource, e.Key, val) | ||
|
||
e.EventType = event.Update | ||
} else if sourceName != e.EventSource { | ||
|
||
prioritySrc := m.getHighPrioritySource(sourceName.(string), e.EventSource) | ||
|
||
if prioritySrc != nil && prioritySrc.GetSourceName() == sourceName { | ||
// if event generated from less priority source then ignore | ||
openlog.Info(fmt.Sprintf("the event source %s's priority is less then %s's, ignore", | ||
e.EventSource, sourceName)) | ||
return ErrIgnoreChange | ||
} | ||
m.ConfigurationMap.Store(e.Key, e.EventSource) | ||
m.updateCache(e.EventSource, e.Key, val) | ||
|
||
e.EventType = event.Update | ||
} | ||
|
||
|
@@ -440,9 +429,16 @@ func (m *Manager) updateEvent(e *event.Event) error { | |
// find less priority source or delete key | ||
source := m.findNextBestSource(e.Key, sourceName.(string)) | ||
if source == nil { | ||
m.ConfigurationMap.Delete(e.Key) | ||
m.deleteCache(e.Key) | ||
} else { | ||
m.ConfigurationMap.Store(e.Key, source.GetSourceName()) | ||
srcName := source.GetSourceName() | ||
// fixme: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个看上去改变了程序过去的行为。可否用过archaius框架级的bool作为功能开关,控制get时是否从缓存取值 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 考虑用特性分支管理 |
||
// val may not consistent with srcName if | ||
// 1. key A have two sources | ||
// 2. both source delete the key on the same time | ||
// but it rarely happens | ||
val := m.configValueBySource(e.Key, srcName) | ||
m.updateCache(srcName, e.Key, val) | ||
} | ||
} | ||
|
||
|
@@ -452,6 +448,16 @@ func (m *Manager) updateEvent(e *event.Event) error { | |
return nil | ||
} | ||
|
||
func (m *Manager) updateCache(source, key string, val interface{}) { | ||
m.ConfigurationMap.Store(key, source) | ||
m.ConfigValueCache.Store(key, val) | ||
} | ||
|
||
func (m *Manager) deleteCache(key string) { | ||
m.ConfigurationMap.Delete(key) | ||
m.ConfigValueCache.Delete(key) | ||
} | ||
|
||
// OnEvent Triggers actions when an event is generated | ||
func (m *Manager) OnEvent(event *event.Event) { | ||
err := m.updateEvent(event) | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go的UT已经支持临时目录特性了,不需要再这么创建,可以参考下,少维护一些代码
https://pkg.go.dev/testing#T.TempDir
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
go 1.13 不支持 t.TempDir ,我们需要支持 go 1.13, 这个不改吧
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
出自 https://go.dev/doc/devel/release
go的版本策略,为了你们的安全考虑,我不建议使用低于1.5及以下的版本,没有安全保障
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
实际的业务不可能是跟着 go 官方的节奏走,总会有一些版本比较落后。就算业务有意愿,各种三方库也有大量跟不上的。而且这里的并不需要做多大的修改,就能够都支持到的,何苦一定要改呢