From 00d74abc4a49eb66afc5a406fd136c2c3fa1205a Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" <4903+rboyer@users.noreply.github.com> Date: Mon, 3 Feb 2025 16:22:10 -0600 Subject: [PATCH] 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. --- .changelog/22113.txt | 3 +++ agent/metadata/build.go | 38 +++++++++++++++++++++++++++++++++++++- agent/metadata/server.go | 10 +++++----- 3 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 .changelog/22113.txt diff --git a/.changelog/22113.txt b/.changelog/22113.txt new file mode 100644 index 000000000000..c97fff1a4a35 --- /dev/null +++ b/.changelog/22113.txt @@ -0,0 +1,3 @@ +```release-note:improvement +metadata: memoize the parsed build versions +``` diff --git a/agent/metadata/build.go b/agent/metadata/build.go index 76a432d9a380..880132b9db03 100644 --- a/agent/metadata/build.go +++ b/agent/metadata/build.go @@ -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) } diff --git a/agent/metadata/server.go b/agent/metadata/server.go index 2e626787bdff..19dba6e2cc97 100644 --- a/agent/metadata/server.go +++ b/agent/metadata/server.go @@ -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),