From 7f15b5c9abfae9904cdd2f68beaaac2256b220b3 Mon Sep 17 00:00:00 2001 From: maesi Date: Tue, 16 Sep 2025 19:08:34 +0200 Subject: [PATCH 1/3] improve config loading process --- config/dynamic/config.go | 6 +++++ config/dynamic/config_test.go | 9 ++++--- js/script_http_test.go | 10 ++++--- server/configwatcher.go | 7 +++-- server/configwatcher_test.go | 51 +++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/config/dynamic/config.go b/config/dynamic/config.go index 0e54034fd..4694c1513 100644 --- a/config/dynamic/config.go +++ b/config/dynamic/config.go @@ -74,7 +74,13 @@ func AddRef(parent, ref *Config) { if !added { return } + checksum := ref.Info.Checksum ref.Listeners.Add(parent.Info.Url.String(), func(e ConfigEvent) { + if bytes.Equal(e.Config.Info.Checksum, checksum) && e.Event == Create { + return + } + e.Config.Info.Checksum = checksum + parent.Info.Time = ref.Info.Time parent.Listeners.Invoke(ConfigEvent{Event: Update, Config: parent, Name: parent.Info.Path()}) if e.Event == Delete { diff --git a/config/dynamic/config_test.go b/config/dynamic/config_test.go index 854883be0..9e262bd0f 100644 --- a/config/dynamic/config_test.go +++ b/config/dynamic/config_test.go @@ -97,12 +97,13 @@ func TestAddRef(t *testing.T) { name: "ref updates parent time", test: func(t *testing.T) { parent := &dynamic.Config{Info: dynamictest.NewConfigInfo(dynamictest.WithUrl("file://parent.yaml"))} - ref := &dynamic.Config{Info: dynamictest.NewConfigInfo(dynamictest.WithUrl("file://ref.yaml"))} + child := &dynamic.Config{Info: dynamictest.NewConfigInfo(dynamictest.WithUrl("file://ref.yaml"))} d, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - ref.Info.Time = d + child.Info.Time = d - dynamic.AddRef(parent, ref) - ref.Listeners.Invoke(dynamic.ConfigEvent{Config: ref}) + dynamic.AddRef(parent, child) + child.Info.Checksum = []byte{1} + child.Listeners.Invoke(dynamic.ConfigEvent{Config: child}) require.Equal(t, d, parent.Info.Time) }, diff --git a/js/script_http_test.go b/js/script_http_test.go index 5f6b806f2..3480d302c 100644 --- a/js/script_http_test.go +++ b/js/script_http_test.go @@ -424,15 +424,17 @@ func TestMaxRedirects(t *testing.T) { testcases := []struct { name string code string - }{{ - name: "max redirects 0", - code: fmt.Sprintf(`import http from 'mokapi/http' + }{ + { + name: "max redirects 0", + code: fmt.Sprintf(`import http from 'mokapi/http' export default function() { const res = http.get('%s', { maxRedirects: 0 }); console.log(res.headers.Location[0]); } `, server.URL), - }} + }, + } hook := test.NewGlobal() for _, tc := range testcases { diff --git a/server/configwatcher.go b/server/configwatcher.go index c56ad15ff..cf603108c 100644 --- a/server/configwatcher.go +++ b/server/configwatcher.go @@ -73,7 +73,7 @@ func (w *ConfigWatcher) Read(u *url.URL, v any) (*dynamic.Config, error) { parse = true } else { c = e.config - parse = c.Data == nil + parse = c.Data == nil && v != nil } w.m.Unlock() @@ -87,7 +87,7 @@ func (w *ConfigWatcher) Read(u *url.URL, v any) (*dynamic.Config, error) { c.Data = v } - log.Debugf("processing %v", c.Info.Path()) + log.Debugf("read processing %v", c.Info.Path()) defer log.Debugf("processed %v", c.Info.Path()) // Currently, read does not validate config. Add Validate would break compatibility @@ -245,6 +245,7 @@ func (w *ConfigWatcher) configChanged(evt dynamic.ConfigEvent) { if c.Data == nil { e.m.Unlock() + log.Debugf("processed %v", c.Info.Path()) return } @@ -256,6 +257,8 @@ func (w *ConfigWatcher) configChanged(evt dynamic.ConfigEvent) { e.m.Unlock() + log.Debugf("processed %v", c.Info.Path()) + for _, l := range w.listener { go l(evt) } diff --git a/server/configwatcher_test.go b/server/configwatcher_test.go index c5d58a59d..053a3173e 100644 --- a/server/configwatcher_test.go +++ b/server/configwatcher_test.go @@ -389,6 +389,54 @@ func TestConfigWatcher_Read(t *testing.T) { require.Equal(t, c, mainFile.Config) }, }, + { + name: "provider triggers main file and then referenced file", + test: func(t *testing.T) { + w := NewConfigWatcher(&static.Config{}) + mainPath := mustParse("foo://file.yml") + mainConfig := &dynamic.Config{Info: dynamic.ConfigInfo{Url: mainPath, Checksum: []byte{1}}} + refPath := mustParse("foo://ref.yml") + refConfig := &dynamic.Config{Info: dynamic.ConfigInfo{Url: refPath, Checksum: []byte{1}}} + + var ch chan dynamic.ConfigEvent + w.providers["foo"] = &testprovider{ + read: func(u *url.URL) (*dynamic.Config, error) { + c := &dynamic.Config{Info: dynamic.ConfigInfo{Url: u}} + c.Info.Checksum = []byte{1} + return c, nil + }, + start: func(configs chan dynamic.ConfigEvent, pool *safe.Pool) error { + ch = configs + return nil + }, + } + pool := safe.NewPool(context.Background()) + w.Start(pool) + defer pool.Stop() + + var events []string + w.AddListener(func(e dynamic.ConfigEvent) { + events = append(events, e.Config.Info.Path()) + }) + + ch <- dynamic.ConfigEvent{Name: mainPath.String(), Event: dynamic.Create, Config: mainConfig} + c, err := w.Read(refPath, nil) + require.NoError(t, err) + require.NotNil(t, c) + dynamic.AddRef(mainConfig, c) + + ch <- dynamic.ConfigEvent{Name: refPath.String(), Event: dynamic.Create, Config: refConfig} + + time.Sleep(5 * time.Millisecond) + require.Equal(t, []string{"foo://file.yml", "foo://ref.yml"}, events) + + refConfig.Info.Checksum = []byte{2} + ch <- dynamic.ConfigEvent{Name: refPath.String(), Event: dynamic.Create, Config: refConfig} + + time.Sleep(5 * time.Millisecond) + require.Equal(t, []string{"foo://file.yml", "foo://ref.yml", "foo://file.yml", "foo://ref.yml"}, events) + }, + }, } for _, tc := range testcases { @@ -465,9 +513,12 @@ func TestConfigWatcher_Start(t *testing.T) { }, } + t.Parallel() for _, tc := range testcases { tc := tc t.Run(tc.name, func(t *testing.T) { + t.Parallel() + tc.f(t) }) } From b0e13c37fc3c34c01331d9d565373ea5762db415 Mon Sep 17 00:00:00 2001 From: maesi Date: Tue, 16 Sep 2025 22:11:40 +0200 Subject: [PATCH 2/3] fix test --- config/dynamic/config.go | 1 + server/configwatcher_test.go | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/config/dynamic/config.go b/config/dynamic/config.go index 4694c1513..29cd83296 100644 --- a/config/dynamic/config.go +++ b/config/dynamic/config.go @@ -76,6 +76,7 @@ func AddRef(parent, ref *Config) { } checksum := ref.Info.Checksum ref.Listeners.Add(parent.Info.Url.String(), func(e ConfigEvent) { + // event Create is used for reading first time if bytes.Equal(e.Config.Info.Checksum, checksum) && e.Event == Create { return } diff --git a/server/configwatcher_test.go b/server/configwatcher_test.go index 053a3173e..62e4d788d 100644 --- a/server/configwatcher_test.go +++ b/server/configwatcher_test.go @@ -513,12 +513,9 @@ func TestConfigWatcher_Start(t *testing.T) { }, } - t.Parallel() + // no parallel git provider is not thread-safe for _, tc := range testcases { - tc := tc t.Run(tc.name, func(t *testing.T) { - t.Parallel() - tc.f(t) }) } From 873542f8ff1e03b5b3caee8b613365060836f706 Mon Sep 17 00:00:00 2001 From: maesi Date: Tue, 16 Sep 2025 22:22:46 +0200 Subject: [PATCH 3/3] fix test --- server/configwatcher_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/configwatcher_test.go b/server/configwatcher_test.go index 62e4d788d..ca5357406 100644 --- a/server/configwatcher_test.go +++ b/server/configwatcher_test.go @@ -434,7 +434,7 @@ func TestConfigWatcher_Read(t *testing.T) { ch <- dynamic.ConfigEvent{Name: refPath.String(), Event: dynamic.Create, Config: refConfig} time.Sleep(5 * time.Millisecond) - require.Equal(t, []string{"foo://file.yml", "foo://ref.yml", "foo://file.yml", "foo://ref.yml"}, events) + require.ElementsMatch(t, []string{"foo://file.yml", "foo://ref.yml", "foo://file.yml", "foo://ref.yml"}, events) }, }, }