perf: Remove expensive reflection from raft/mesh hot path (#16552)

* perf: Remove expensive reflection from raft/mesh hot path

Replaces a reflection-based copy of a struct in the mesh topology with a
deep-copy generated implementation.

This is in the hot-path of raft FSM updates, and the reflection overhead was a
substantial part of mesh registration times (~90%). This could manifest as raft
thread saturation, and resulting instability.

Co-authored-by: Joel Brandhorst <joel.brandhorst@gmail.com>

* add changelog

---------

Co-authored-by: Joel Brandhorst <joel.brandhorst@gmail.com>
Co-authored-by: John Murret <john.murret@hashicorp.com>
This commit is contained in:
Lincoln Stoll 2023-05-26 19:42:05 +02:00 committed by GitHub
parent 5b02d7ce83
commit 5c5e1266e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 32 additions and 17 deletions

3
.changelog/16552.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
raft: Remove expensive reflection from raft/mesh hot path
```

View File

@ -384,6 +384,7 @@ codegen-tools:
deep-copy: codegen-tools deep-copy: codegen-tools
@$(SHELL) $(CURDIR)/agent/structs/deep-copy.sh @$(SHELL) $(CURDIR)/agent/structs/deep-copy.sh
@$(SHELL) $(CURDIR)/agent/proxycfg/deep-copy.sh @$(SHELL) $(CURDIR)/agent/proxycfg/deep-copy.sh
@$(SHELL) $(CURDIR)/agent/consul/state/deep-copy.sh
version: version:
@echo -n "Version: " @echo -n "Version: "

View File

@ -11,7 +11,6 @@ import (
"strings" "strings"
"github.com/hashicorp/go-memdb" "github.com/hashicorp/go-memdb"
"github.com/mitchellh/copystructure"
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/configentry" "github.com/hashicorp/consul/agent/configentry"
@ -4711,14 +4710,7 @@ func updateMeshTopology(tx WriteTxn, idx uint64, node string, svc *structs.NodeS
var mapping *upstreamDownstream var mapping *upstreamDownstream
if existing, ok := obj.(*upstreamDownstream); ok { if existing, ok := obj.(*upstreamDownstream); ok {
rawCopy, err := copystructure.Copy(existing) mapping := existing.DeepCopy()
if err != nil {
return fmt.Errorf("failed to copy existing topology mapping: %v", err)
}
mapping, ok = rawCopy.(*upstreamDownstream)
if !ok {
return fmt.Errorf("unexpected topology type %T", rawCopy)
}
mapping.Refs[uid] = struct{}{} mapping.Refs[uid] = struct{}{}
mapping.ModifyIndex = idx mapping.ModifyIndex = idx
@ -4784,14 +4776,7 @@ func cleanupMeshTopology(tx WriteTxn, idx uint64, service *structs.ServiceNode)
// Do the updates in a separate loop so we don't trash the iterator. // Do the updates in a separate loop so we don't trash the iterator.
for _, m := range mappings { for _, m := range mappings {
rawCopy, err := copystructure.Copy(m) copy := m.DeepCopy()
if err != nil {
return fmt.Errorf("failed to copy existing topology mapping: %v", err)
}
copy, ok := rawCopy.(*upstreamDownstream)
if !ok {
return fmt.Errorf("unexpected topology type %T", rawCopy)
}
// Bail early if there's no reference to the proxy ID we're deleting // Bail early if there's no reference to the proxy ID we're deleting
if _, ok := copy.Refs[uid]; !ok { if _, ok := copy.Refs[uid]; !ok {

View File

@ -0,0 +1,15 @@
// generated by deep-copy -pointer-receiver -o ./catalog_schema.deepcopy.go -type upstreamDownstream ./; DO NOT EDIT.
package state
// DeepCopy generates a deep copy of *upstreamDownstream
func (o *upstreamDownstream) DeepCopy() *upstreamDownstream {
var cp upstreamDownstream = *o
if o.Refs != nil {
cp.Refs = make(map[string]struct{}, len(o.Refs))
for k2, v2 := range o.Refs {
cp.Refs[k2] = v2
}
}
return &cp
}

11
agent/consul/state/deep-copy.sh Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
readonly PACKAGE_DIR="$(dirname "${BASH_SOURCE[0]}")"
cd $PACKAGE_DIR
# Uses: https://github.com/globusdigital/deep-copy
deep-copy \
-pointer-receiver \
-o ./catalog_schema.deepcopy.go \
-type upstreamDownstream \
./