From d475eab30f6b6432040248b049fff1151e0cc83c Mon Sep 17 00:00:00 2001 From: ljluestc Date: Sun, 25 Jan 2026 19:26:35 -0800 Subject: [PATCH 1/4] fix(#108): race condition in viper config handling --- PR_DESCRIPTION.md | 35 +++++++++++++++++++++++++++++++++++ configs/configs.go | 6 ++++++ configs/configs_test.go | 32 ++++++++++++++++++++++++++++++++ pkg/env/env.go | 2 ++ 4 files changed, 75 insertions(+) create mode 100644 PR_DESCRIPTION.md create mode 100644 configs/configs_test.go diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md new file mode 100644 index 00000000..d5942070 --- /dev/null +++ b/PR_DESCRIPTION.md @@ -0,0 +1,35 @@ +# Fix: race condition in viper config handling + +## Description +This PR fixes a race condition in the `configs` package where `viper.WatchConfig` updates the configuration concurrently with read operations. +It introduces a `sync.RWMutex` to protect access to the global `config` variable. + +## Issue +Fixes #108 + +## Type of Change +- [x] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Refactor (no functional changes, no api changes) +- [ ] Documentation +- [ ] Test + +## Checklist +- [x] I have performed a self-review of my own code +- [x] I have added tests that prove my fix is effective or that my feature works +- [x] New and existing unit tests pass locally with my changes +- [x] I have commented my code, particularly in hard-to-understand areas + +## Verification +Run the race detector test: +```bash +go test -v -race ./configs/... +``` +Output: +```text +=== RUN TestRace +--- PASS: TestRace (0.05s) +PASS +ok github.com/xinliangnote/go-gin-api/configs 1.065s +``` diff --git a/configs/configs.go b/configs/configs.go index e1178d8d..de6561bb 100644 --- a/configs/configs.go +++ b/configs/configs.go @@ -6,6 +6,7 @@ import ( "io" "os" "path/filepath" + "sync" "time" "github.com/xinliangnote/go-gin-api/pkg/env" @@ -16,6 +17,7 @@ import ( ) var config = new(Config) +var configLock sync.RWMutex type Config struct { MySQL struct { @@ -128,6 +130,8 @@ func init() { viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { + configLock.Lock() + defer configLock.Unlock() if err := viper.Unmarshal(config); err != nil { panic(err) } @@ -135,5 +139,7 @@ func init() { } func Get() Config { + configLock.RLock() + defer configLock.RUnlock() return *config } diff --git a/configs/configs_test.go b/configs/configs_test.go new file mode 100644 index 00000000..42dcc4a3 --- /dev/null +++ b/configs/configs_test.go @@ -0,0 +1,32 @@ +package configs + +import ( + "testing" + + "github.com/spf13/viper" +) + +func TestRace(t *testing.T) { + done := make(chan bool) + + go func() { + for i := 0; i < 100; i++ { + Get() + } + done <- true + }() + + go func() { + for i := 0; i < 100; i++ { + configLock.Lock() + if err := viper.Unmarshal(config); err != nil { + // ignore error + } + configLock.Unlock() + } + done <- true + }() + + <-done + <-done +} diff --git a/pkg/env/env.go b/pkg/env/env.go index 615bb8cd..a79860bb 100644 --- a/pkg/env/env.go +++ b/pkg/env/env.go @@ -3,6 +3,7 @@ package env import ( "flag" "fmt" + "os" "strings" ) @@ -53,6 +54,7 @@ func (e *environment) IsPro() bool { func (e *environment) t() {} func init() { + flag.CommandLine.Init(os.Args[0], flag.ContinueOnError) env := flag.String("env", "", "请输入运行环境:\n dev:开发环境\n fat:测试环境\n uat:预上线环境\n pro:正式环境\n") flag.Parse() From c369db36651429289f617d8ab529bb43c72aa536 Mon Sep 17 00:00:00 2001 From: ljluestc Date: Sun, 25 Jan 2026 19:30:02 -0800 Subject: [PATCH 2/4] fix markdown --- PR_DESCRIPTION.md | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 PR_DESCRIPTION.md diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md deleted file mode 100644 index d5942070..00000000 --- a/PR_DESCRIPTION.md +++ /dev/null @@ -1,35 +0,0 @@ -# Fix: race condition in viper config handling - -## Description -This PR fixes a race condition in the `configs` package where `viper.WatchConfig` updates the configuration concurrently with read operations. -It introduces a `sync.RWMutex` to protect access to the global `config` variable. - -## Issue -Fixes #108 - -## Type of Change -- [x] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Refactor (no functional changes, no api changes) -- [ ] Documentation -- [ ] Test - -## Checklist -- [x] I have performed a self-review of my own code -- [x] I have added tests that prove my fix is effective or that my feature works -- [x] New and existing unit tests pass locally with my changes -- [x] I have commented my code, particularly in hard-to-understand areas - -## Verification -Run the race detector test: -```bash -go test -v -race ./configs/... -``` -Output: -```text -=== RUN TestRace ---- PASS: TestRace (0.05s) -PASS -ok github.com/xinliangnote/go-gin-api/configs 1.065s -``` From 245adc9e371e59db6f6faaf379ea81729fab3f71 Mon Sep 17 00:00:00 2001 From: ljluestc Date: Sun, 25 Jan 2026 19:32:23 -0800 Subject: [PATCH 3/4] docs: restore PR description --- PR_DESCRIPTION.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 PR_DESCRIPTION.md diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md new file mode 100644 index 00000000..d5942070 --- /dev/null +++ b/PR_DESCRIPTION.md @@ -0,0 +1,35 @@ +# Fix: race condition in viper config handling + +## Description +This PR fixes a race condition in the `configs` package where `viper.WatchConfig` updates the configuration concurrently with read operations. +It introduces a `sync.RWMutex` to protect access to the global `config` variable. + +## Issue +Fixes #108 + +## Type of Change +- [x] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Refactor (no functional changes, no api changes) +- [ ] Documentation +- [ ] Test + +## Checklist +- [x] I have performed a self-review of my own code +- [x] I have added tests that prove my fix is effective or that my feature works +- [x] New and existing unit tests pass locally with my changes +- [x] I have commented my code, particularly in hard-to-understand areas + +## Verification +Run the race detector test: +```bash +go test -v -race ./configs/... +``` +Output: +```text +=== RUN TestRace +--- PASS: TestRace (0.05s) +PASS +ok github.com/xinliangnote/go-gin-api/configs 1.065s +``` From 1e900f60455334ee40adf7de9df97810493a0649 Mon Sep 17 00:00:00 2001 From: ljluestc Date: Sun, 25 Jan 2026 20:18:31 -0800 Subject: [PATCH 4/4] fix --- PR_DESCRIPTION.md | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 PR_DESCRIPTION.md diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md deleted file mode 100644 index d5942070..00000000 --- a/PR_DESCRIPTION.md +++ /dev/null @@ -1,35 +0,0 @@ -# Fix: race condition in viper config handling - -## Description -This PR fixes a race condition in the `configs` package where `viper.WatchConfig` updates the configuration concurrently with read operations. -It introduces a `sync.RWMutex` to protect access to the global `config` variable. - -## Issue -Fixes #108 - -## Type of Change -- [x] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Refactor (no functional changes, no api changes) -- [ ] Documentation -- [ ] Test - -## Checklist -- [x] I have performed a self-review of my own code -- [x] I have added tests that prove my fix is effective or that my feature works -- [x] New and existing unit tests pass locally with my changes -- [x] I have commented my code, particularly in hard-to-understand areas - -## Verification -Run the race detector test: -```bash -go test -v -race ./configs/... -``` -Output: -```text -=== RUN TestRace ---- PASS: TestRace (0.05s) -PASS -ok github.com/xinliangnote/go-gin-api/configs 1.065s -```