From f92c1b7536fa1c3bab891250d996ead02f17d7ca Mon Sep 17 00:00:00 2001 From: Andrey Butusov Date: Tue, 29 Oct 2024 17:52:58 +0300 Subject: [PATCH] morph: support reloading morph endpoints with SIGHUP Add a new function `Client.Reload` that passes the `WithEndpoints` option and closes the client if there is no endpoint in the config to which the client is connected. Add docs. Closes #1871. Signed-off-by: Andrey Butusov --- CHANGELOG.md | 1 + cmd/neofs-node/config.go | 4 ++++ docs/sighup.md | 8 ++++++-- pkg/morph/client/client.go | 3 ++- pkg/morph/client/constructor.go | 13 +++++++------ pkg/morph/client/multi.go | 3 +++ pkg/morph/client/reload.go | 31 +++++++++++++++++++++++++++++++ 7 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 pkg/morph/client/reload.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aef08c7f4..3215b51138 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ attribute, which is used for container domain name in NNS contracts (#2954) - `neofs-cli control object revive` command (#2968) - `--disable-auto-gen-tag` flag for gendoc command (#2983) - Docs files for cli commands to the `docs/cli-commands` folder (#2983) +- Reloading morph endpoints with SIGHUP (#2998) ### Fixed - Do not search for tombstones when handling their expiration, use local indexes instead (#2929) diff --git a/cmd/neofs-node/config.go b/cmd/neofs-node/config.go index 76d762ad92..56beef3bd2 100644 --- a/cmd/neofs-node/config.go +++ b/cmd/neofs-node/config.go @@ -870,6 +870,10 @@ func (c *cfg) configWatcher(ctx context.Context) { continue } + // Morph + + c.cli.Reload(client.WithEndpoints(c.morph.endpoints)) + c.log.Info("configuration has been reloaded successfully") case <-ctx.Done(): return diff --git a/docs/sighup.md b/docs/sighup.md index 0c41564159..cc5595f412 100644 --- a/docs/sighup.md +++ b/docs/sighup.md @@ -10,8 +10,6 @@ Available for reconfiguration fields: ```yml head_timeout: - cache_size: - cache_time: replication_cooldown: object_batch_size: max_workers: @@ -34,3 +32,9 @@ comparing paths from `shard.blobstor` section. After this we have 3 sets: | Changed section | Actions | |-----------------|----------------------------------------------------------------------------------------------------------------------| | `path` | If `path` is different, metabase is closed and opened with a new path. All other configuration will also be updated. | + +### Morph + +| Changed section | Actions | +|-----------------|--------------------------------------------------------------------------------------------------------------------| +| `endpoints` | If in the `endpoints` there is no endpoint that the client is connected to, try connecting to another endpoint N3. | diff --git a/pkg/morph/client/client.go b/pkg/morph/client/client.go index 793fafc641..0d8f96bfd8 100644 --- a/pkg/morph/client/client.go +++ b/pkg/morph/client/client.go @@ -56,7 +56,8 @@ type Client struct { cfg cfg - endpoints []string + endpointsLock *sync.RWMutex + endpoints []string subs subscriptions diff --git a/pkg/morph/client/constructor.go b/pkg/morph/client/constructor.go index 9586cbad34..e10f869bd4 100644 --- a/pkg/morph/client/constructor.go +++ b/pkg/morph/client/constructor.go @@ -108,12 +108,13 @@ func New(key *keys.PrivateKey, opts ...Option) (*Client, error) { var ( cli = &Client{ - cache: newClientCache(), - logger: cfg.logger, - acc: acc, - accAddr: accAddr, - cfg: *cfg, - closeChan: make(chan struct{}), + cache: newClientCache(), + logger: cfg.logger, + acc: acc, + accAddr: accAddr, + cfg: *cfg, + endpointsLock: &sync.RWMutex{}, + closeChan: make(chan struct{}), subs: subscriptions{ notifyChan: make(chan *state.ContainedNotificationEvent), blockChan: make(chan *block.Block), diff --git a/pkg/morph/client/multi.go b/pkg/morph/client/multi.go index 5b1a09543a..3648ccd5b9 100644 --- a/pkg/morph/client/multi.go +++ b/pkg/morph/client/multi.go @@ -33,6 +33,9 @@ func (c *Client) switchRPC() *connection { } func (c *Client) connEndpoints() *connection { + c.endpointsLock.RLock() + defer c.endpointsLock.RUnlock() + // Iterate endpoints. for _, e := range c.endpoints { conn, err := c.newConnection(e) diff --git a/pkg/morph/client/reload.go b/pkg/morph/client/reload.go new file mode 100644 index 0000000000..1ad58344b8 --- /dev/null +++ b/pkg/morph/client/reload.go @@ -0,0 +1,31 @@ +package client + +import "slices" + +// Reload allows runtime reconfiguration for WithEndpoints parameter. +func (c *Client) Reload(opts ...Option) { + cfg := new(cfg) + for _, o := range opts { + o(cfg) + } + + c.endpointsLock.Lock() + + c.cfg.endpoints = cfg.endpoints + c.endpoints = cfg.endpoints + + c.endpointsLock.Unlock() + + var conn = c.conn.Load() + if conn == nil { + return + } + currentEndpoint := conn.client.Endpoint() + + // RPC reconnections are costly for node, so we avoid them + // by using the current connection while it's alive + // if the current endpoint is present in the config + if !slices.Contains(cfg.endpoints, currentEndpoint) { + conn.client.Close() + } +}