From e53815b9fc3a0d3b1fca3715e696efc47dfec8a2 Mon Sep 17 00:00:00 2001 From: Edoardo Spadolini Date: Sat, 14 Dec 2024 00:10:46 +0100 Subject: [PATCH] Cache the result of TeleportProcess.GetRotation (#50235) --- lib/service/connect.go | 8 ++++++-- lib/service/service.go | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/lib/service/connect.go b/lib/service/connect.go index 62bc4a5cbcde9..3a55e16176971 100644 --- a/lib/service/connect.go +++ b/lib/service/connect.go @@ -495,12 +495,14 @@ func (process *TeleportProcess) firstTimeConnect(role types.SystemRole) (*Connec process.logger.WarnContext(process.ExitContext(), "Failed to write identity to storage.", "identity", role, "error", err) } - if err := process.storage.WriteState(role, state.StateV2{ + err = process.storage.WriteState(role, state.StateV2{ Spec: state.StateSpecV2{ Rotation: ca.GetRotation(), InitialLocalVersion: teleport.Version, }, - }); err != nil { + }) + process.rotationCache.Remove(role) + if err != nil { return nil, trace.NewAggregate(err, connector.Close()) } process.logger.InfoContext(process.ExitContext(), "The process successfully wrote the credentials and state to the disk.", "identity", role) @@ -912,6 +914,7 @@ func (process *TeleportProcess) rotate(conn *Connector, localState state.StateV2 } localState.Spec.Rotation = remote err = storage.WriteState(id.Role, localState) + process.rotationCache.Remove(id.Role) if err != nil { return trace.Wrap(err) } @@ -982,6 +985,7 @@ func (process *TeleportProcess) rotate(conn *Connector, localState state.StateV2 // only update local phase, there is no need to reload localState.Spec.Rotation = remote err = storage.WriteState(id.Role, localState) + process.rotationCache.Remove(id.Role) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/service/service.go b/lib/service/service.go index ffadadebf855b..e8c344ddd9b50 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -620,6 +620,11 @@ type TeleportProcess struct { // storage is a server local storage storage *storage.ProcessStorage + // rotationCache is a TTL cache for GetRotation, since it might get called + // frequently if the agent is heartbeating multiple resources. Keys are + // [types.SystemRole], values are [*types.Rotation]. + rotationCache *utils.FnCache + // id is a process id - used to identify different processes // during in-process reloads. id string @@ -1063,6 +1068,19 @@ func NewTeleport(cfg *servicecfg.Config) (*TeleportProcess, error) { cfg.Clock = clockwork.NewRealClock() } + // full heartbeat announces are on average every 2/3 * 6/7 of the default + // announce TTL, so we pick a slightly shorter TTL here + const rotationCacheTTL = apidefaults.ServerAnnounceTTL / 2 + rotationCache, err := utils.NewFnCache(utils.FnCacheConfig{ + TTL: rotationCacheTTL, + Clock: cfg.Clock, + Context: supervisor.ExitContext(), + ReloadOnErr: true, + }) + if err != nil { + return nil, trace.Wrap(err) + } + if cfg.PluginRegistry == nil { cfg.PluginRegistry = plugin.NewRegistry() } @@ -1155,6 +1173,7 @@ func NewTeleport(cfg *servicecfg.Config) (*TeleportProcess, error) { connectors: make(map[types.SystemRole]*Connector), importedDescriptors: cfg.FileDescriptors, storage: storage, + rotationCache: rotationCache, id: processID, log: cfg.Log, logger: cfg.Logger, @@ -2756,13 +2775,22 @@ func (process *TeleportProcess) NewLocalCache(clt authclient.ClientI, setupConfi }, clt) } -// GetRotation returns the process rotation. +// GetRotation returns the process rotation. The result is internally cached for +// a few minutes, so anything that must get the latest possible version should +// use process.storage.GetState directly, instead (writes to the state that this +// process knows about will invalidate the cache, however). func (process *TeleportProcess) GetRotation(role types.SystemRole) (*types.Rotation, error) { - state, err := process.storage.GetState(context.TODO(), role) + rotation, err := utils.FnCacheGet(process.ExitContext(), process.rotationCache, role, func(ctx context.Context) (*types.Rotation, error) { + state, err := process.storage.GetState(ctx, role) + if err != nil { + return nil, trace.Wrap(err) + } + return &state.Spec.Rotation, nil + }) if err != nil { return nil, trace.Wrap(err) } - return &state.Spec.Rotation, nil + return rotation, nil } func (process *TeleportProcess) proxyPublicAddr() utils.NetAddr {