metadata: memoize the parsed build versions (#22113)

There will only be a small set of consul build versions that a single consul
server will witness. Inside of metadata.IsConsulServer we use a very
expensive function in the hashicorp/go-version library to parse these into
read-only *version.Version structs all over Consul.

Memoize these in a package cache map. Likely the thing will only have like
2 keys in it ever over the life of the process.
pull/22103/head^2
R.B. Boyer 2025-02-03 16:22:10 -06:00 committed by GitHub
parent 3e0c098890
commit 00d74abc4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 45 additions and 6 deletions

3
.changelog/22113.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
metadata: memoize the parsed build versions
```

View File

@ -4,12 +4,48 @@
package metadata
import (
"sync"
"github.com/hashicorp/go-version"
"github.com/hashicorp/serf/serf"
)
type versionTuple struct {
Value *version.Version
Err error
}
var versionCache sync.Map // string->versionTuple
// Build extracts the Consul version info for a member.
func Build(m *serf.Member) (*version.Version, error) {
str := versionFormat.FindString(m.Tags["build"])
build := m.Tags["build"]
ok, v, err := getMemoizedBuildVersion(build)
if ok {
return v, err
}
v, err = parseBuildAsVersion(build)
versionCache.Store(build, versionTuple{Value: v, Err: err})
return v, err
}
func getMemoizedBuildVersion(build string) (bool, *version.Version, error) {
rawTuple, ok := versionCache.Load(build)
if !ok {
return false, nil, nil
}
tuple, ok := rawTuple.(versionTuple)
if !ok {
return false, nil, nil
}
return true, tuple.Value, tuple.Err
}
func parseBuildAsVersion(build string) (*version.Version, error) {
str := versionFormat.FindString(build)
return version.NewVersion(str)
}

View File

@ -127,11 +127,6 @@ func IsConsulServer(m serf.Member) (bool, *Server) {
}
}
buildVersion, err := Build(&m)
if err != nil {
return false, nil
}
wanJoinPort := 0
wanJoinPortStr, ok := m.Tags["wan_join_port"]
if ok {
@ -180,6 +175,11 @@ func IsConsulServer(m serf.Member) (bool, *Server) {
addr := &net.TCPAddr{IP: m.Addr, Port: port}
buildVersion, err := Build(&m)
if err != nil {
return false, nil
}
parts := &Server{
Name: m.Name,
ShortName: strings.TrimSuffix(m.Name, "."+datacenter),