docker / libcompose

*Unmaintained/Deprecated* An experimental go library providing Compose-like functionality
https://godoc.org/github.com/docker/libcompose
Apache License 2.0
585 stars 191 forks source link

Load schemas in init() #455

Closed yudai closed 7 years ago

yudai commented 7 years ago

Issue Observed

When multiple goroutines use different Project instances concurrently, Go's race detector complains data races.

Issue Detail

schema_helpers.go have some global variables and they are initialized by setupSchemaLoaders() when validate() and other schema functions are called for the first time. However, setupSchemaLoaders() does not have mutex, so some times multiple gorutines proceed into this function, then a race happens.

Suggested Resolution

We can add a mutex to setupSchemaLoader() to avoid the race condition. However, the function is supposed to cache results and called not so many times. Therefore, in this patch, we simply initialize schemas in init().

(using global variables might be not a good idea. We might want to have some singleton-ish factory or something instead)

Appendix

Stack Trace by the race detector:

WARNING: DATA RACE
Write at 0x00c4201a9740 by goroutine 22:
  runtime.mapassign()
      /home/yudai/.gvm/gos/go1.8/src/runtime/hashmap.go:485 +0x0
  github.com/private/repo/vendor/github.com/docker/libcompose/config.setupSchemaLoaders()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/config/schema_helpers.go:50 +0x227
  github.com/private/repo/vendor/github.com/docker/libcompose/config.validateV2()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/config/validation.go:177 +0x8f
  github.com/private/repo/vendor/github.com/docker/libcompose/config.MergeServicesV2()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/config/merge_v2.go:15 +0xa8d
  github.com/private/repo/vendor/github.com/docker/libcompose/config.Merge()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/config/merge.go:94 +0x7ff
  github.com/private/repo/vendor/github.com/docker/libcompose/project.(*Project).load()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/project/project.go:222 +0x17c
  github.com/private/repo/vendor/github.com/docker/libcompose/project.(*Project).Parse()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/project/project.go:130 +0x338
  github.com/private/repo/vendor/github.com/docker/libcompose/docker.NewProject()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/docker/project.go:58 +0x1e5
  ...snip...

Previous write at 0x00c4201a9740 by goroutine 23:
  runtime.mapassign()
      /home/yudai/.gvm/gos/go1.8/src/runtime/hashmap.go:485 +0x0
  github.com/private/repo/vendor/github.com/docker/libcompose/config.setupSchemaLoaders()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/config/schema_helpers.go:50 +0x227
  github.com/private/repo/vendor/github.com/docker/libcompose/config.validateV2()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/config/validation.go:177 +0x8f
  github.com/private/repo/vendor/github.com/docker/libcompose/config.MergeServicesV2()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/config/merge_v2.go:15 +0xa8d
  github.com/private/repo/vendor/github.com/docker/libcompose/config.Merge()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/config/merge.go:94 +0x7ff
  github.com/private/repo/vendor/github.com/docker/libcompose/project.(*Project).load()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/project/project.go:222 +0x17c
  github.com/private/repo/vendor/github.com/docker/libcompose/project.(*Project).Parse()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/project/project.go:130 +0x338
  github.com/private/repo/vendor/github.com/docker/libcompose/docker.NewProject()
      /home/yudai/repos/private/src/github.com/private/repo/vendor/github.com/docker/libcompose/docker/project.go:58 +0x1e5
  ...snip...