[Bug] go test -race exposes 10 data races (8 packages)
- Dominant language
- Go
- Stars
- 5k
- Forks
- 1k
- Avg merge
- 2d 8h
- Merged PRs (30d)
- 31
Description
### Summary
Running go test -race ./... fails 10 tests across 8 packages due to Data Race.
This issue categorizes the failures by reproducibility and lists the failing test locations, related source locations, and initial analysis, all for evaluating fixes or marking them as known issues.
### Affected Locations
#### 1. Stable data races (reproducible both in a full-suite run and in per-package runs)
| Package | Test | Test Location | Related Source Location |
| -------------------------------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| 1. `cluster/cluster/failback` | `TestFailbackRetryFailed` | [cluster\_test.go L136-L180](https://github.com/apache/dubbo-go/blob/master/cluster/cluster/failback/cluster_test.go#L136-L180) | Read [cluster\_invoker.go L109](https://github.com/apache/dubbo-go/blob/master/cluster/cluster/failback/cluster_invoker.go#L109) (`process` reads `lastT`) vs Write [cluster\_invoker.go L198-L212](https://github.com/apache/dubbo-go/blob/master/cluster/cluster/failback/cluster_invoker.go#L198-L212) (`checkRetry` writes `lastT`/`nextBackoff`) |
| 2. `cluster/cluster/failback` | `TestFailbackOutOfLimit` | [cluster\_test.go L231-L263](https://github.com/apache/dubbo-go/blob/master/cluster/cluster/failback/cluster_test.go#L231-L263) | Write [cluster\_invoker.go L95](https://github.com/apache/dubbo-go/blob/master/cluster/cluster/failback/cluster_invoker.go#L95) (`process` creates `ticker`) vs Read [cluster\_invoker.go L173](https://github.com/apache/dubbo-go/blob/master/cluster/cluster/failback/cluster_invoker.go#L173) (`Destroy` calls `ticker.Stop`) |
| 3. `cluster/router/chain` | `TestRouteCacheGenerationRace` | [chain\_test.go L423-L474](https://github.com/apache/dubbo-go/blob/master/cluster/router/chain/chain_test.go#L423-L474) | Write [chain\_test.go L386](https://github.com/apache/dubbo-go/blob/master/cluster/router/chain/chain_test.go#L386) (`SetCache` writes `r.cache`) vs Read [chain\_test.go L401](https://github.com/apache/dubbo-go/blob/master/cluster/router/chain/chain_test.go#L401) (`Route` reads `r.cache`) |
| 4. `config_center/apollo` | `TestListener` | [impl\_test.go L257-L288](https://github.com/apache/dubbo-go/blob/master/config_center/apollo/impl_test.go#L257-L288) | Map iteration [listener.go L56](https://github.com/apache/dubbo-go/blob/master/config_center/apollo/listener.go#L56) vs Write [listener.go L68](https://github.com/apache/dubbo-go/blob/master/config_center/apollo/listener.go#L68)/[L74](https://github.com/apache/dubbo-go/blob/master/config_center/apollo/listener.go#L74); also the test's own global variable [impl\_test.go L98](https://github.com/apache/dubbo-go/blob/master/config_center/apollo/impl_test.go#L98) (reads `mockConfigRes`) vs [L261](https://github.com/apache/dubbo-go/blob/master/config_center/apollo/impl_test.go#L261) (writes `mockConfigRes`), and [L300-L302](https://github.com/apache/dubbo-go/blob/master/config_center/apollo/impl_test.go#L300-L302) (`Process` writes `event`/`count`) vs [L273-L274](https://github.com/apache/dubbo-go/blob/master/config_center/apollo/impl_test.go#L273-L274) (the test reads) |
| 5. `protocol/dubbo` | `TestDubboProtocol_Refer` | [dubbo\_protocol\_test.go L129-L152](https://github.com/apache/dubbo-go/blob/master/protocol/dubbo/dubbo_protocol_test.go#L129-L152) | Third-party `dubbo-getty v1.4.10`: [session.go L857](https://github.com/apache/dubbo-getty/blob/v1.4.10/session.go#L857) (`s.ReadTimeout()`) vs [connection.go L277-L278](https://github.com/apache/dubbo-getty/blob/v1.4.10/connection.go#L277-L278) (`SetReadDeadline`/`Store`) |
| 6. `protocol/grpc` | `TestGrpcHealthWatchEmitsClosingEvent` | [active\_notify\_test.go L88-L139](https://github.com/apache/dubbo-go/blob/master/protocol/grpc/active_notify_test.go#L88-L139) | Write [active\_notify\_test.go L51](https://github.com/apache/dubbo-go/blob/master/protocol/grpc/active_notify_test.go#L51) (callback goroutine `append events`) vs Read [active\_notify\_test.go L133](https://github.com/apache/dubbo-go/blob/master/protocol/grpc/active_notify_test.go#L133) (test goroutine `len(events)`) |
| `registry/servicediscovery` (unnumbered) | `TestServiceDiscoveryRegistryUnRegister_Concurrent` | [service\_discovery\_registry\_test.go L1307-L1371](https://github.com/apache/dubbo-go/blob/master/registry/servicediscovery/service_discovery_registry_test.go#L1307-L1371) | [service\_discovery\_registry\_test.go L1368](https://github.com/apache/dubbo-go/blob/master/registry/servicediscovery/service_discovery_registry_test.go#L1368) lock-free `append(sdReg.instances, ...)`. Note: the test intentionally omits locking to simulate a real concurrent scenario, and the race is the expected outcome of the test. |
| 7. `server` | `TestCfgAPI_Export` | [inst\_test.go L85-L90](https://github.com/apache/dubbo-go/blob/master/server/inst_test.go#L85-L90) | Write [server.go L200](https://github.com/apache/dubbo-go/blob/master/protocol/triple/triple_protocol/server.go#L200) (`startHttp2` assigns `s.httpSrv`) vs Read [server.go L326](https://github.com/apache/dubbo-go/blob/master/protocol/triple/triple_protocol/server.go#L326) (`GracefulStop` reads `s.httpSrv`) |
#### 2. Non-deterministic data races (unstable reproduction: intermittent on a single run, always reproducible with `-count=N`)
| Package | Test | Test Location | Related Source Location |
| ------------------ | --------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| 8. `server` | `TestCfgAPI_Call` | https://github.com/apache/dubbo-go/blob/master/server/inst_test.go#L94-L100 | Same as `TestCfgAPI_Export`: https://github.com/apache/dubbo-go/blob/master/protocol/triple/triple_protocol/server.go#L200 vs https://github.com/apache/dubbo-go/blob/master/protocol/triple/triple_protocol/server.go#L326 (passes on a single `-run`, race reproduces with `-count=3`) |
| 9. `remoting/getty` | `TestTCPPackageHandle` | https://github.com/apache/dubbo-go/blob/master/remoting/getty/readwriter_test.go#L45-L50 | Read https://github.com/apache/dubbo-go/blob/master/remoting/getty/readwriter.go#L128 (`Write` reads global `srvConf`) vs Write https://github.com/apache/dubbo-go/blob/master/remoting/getty/getty_server.go#L116-L117 (`SetServerConfig` writes `srvConf`). Always reported in full-suite runs, intermittent when run alone |
| 10. `common/config` | `TestNewRegistryDirectoryConsumerListenerUsesResolvedApplicationName` | https://github.com/apache/dubbo-go/blob/master/registry/directory/directory_test.go#L191-L198 | Write https://github.com/apache/dubbo-go/blob/master/common/config/environment.go#L100 (`SetDynamicConfiguration`) vs Read https://github.com/apache/dubbo-go/blob/master/common/config/environment.go#L105 (`GetDynamicConfiguration`). Cross-test race: the reader goroutine is a `MockRegistry.
### Suggested Fixes
Detailed solutions for each issue will be submitted in separate issues; this issue only lists the scope of the fixes.
### Verification
These tests are skipped via -skip in CI. To reproduce locally, remove the corresponding tests from -skip and run:
```bash
GOTOOLCHAIN=go1.25.0 go test -race -count=1 ./...
```
Category 1 (stable) appears on a single run;
for Category 2 (unstable), use `-count=N` to increase the hit rate.
Contributor guide
Assessment
This issue has not been assessed yet.