From 51dacc9913c0c02875592df50da370d799bef285 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 updates the `Client` endpoints. Closes the current connection and attempts to reconnect if there is no endpoint in the config to which the client is connected. Node service can be interrupted in this case. Add docs. Closes #1871. Signed-off-by: Andrey Butusov --- CHANGELOG.md | 1 + cmd/neofs-node/config.go | 4 ++++ docs/sighup.md | 6 ++++++ pkg/morph/client/constructor.go | 4 +++- pkg/morph/client/multi.go | 3 +++ pkg/morph/client/reload.go | 29 +++++++++++++++++++++++++++++ 6 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 pkg/morph/client/reload.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f2510c65a..b989529dfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ attribute, which is used for container domain name in NNS contracts (#2954) - `--disable-auto-gen-tag` flag for gendoc command (#2983) - Docs files for cli commands to the `docs/cli-commands` folder (#2983) - `logger.encoding` config option (#2999) +- 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 1942548b50..958d40947c 100644 --- a/cmd/neofs-node/config.go +++ b/cmd/neofs-node/config.go @@ -872,6 +872,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 9b32a0761a..85acc924fc 100644 --- a/docs/sighup.md +++ b/docs/sighup.md @@ -32,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` | Updates N3 endpoints.
If new `endpoints` do not contain the endpoint client is connected to, it will reconnect to another endpoint from the new list. Node service can be interrupted in this case. | diff --git a/pkg/morph/client/constructor.go b/pkg/morph/client/constructor.go index c852b0f941..76fbfe2ea5 100644 --- a/pkg/morph/client/constructor.go +++ b/pkg/morph/client/constructor.go @@ -40,7 +40,8 @@ type cfg struct { autoSidechainScope bool signer *transaction.Signer - endpoints []string + endpointsLock *sync.RWMutex + endpoints []string singleCli *rpcclient.WSClient // neo-go client for single client mode @@ -63,6 +64,7 @@ func defaultConfig() *cfg { signer: &transaction.Signer{ Scopes: transaction.CalledByEntry, }, + endpointsLock: &sync.RWMutex{}, reconnectionDelay: 5 * time.Second, reconnectionRetries: 5, } diff --git a/pkg/morph/client/multi.go b/pkg/morph/client/multi.go index 160e71534c..1de456ef63 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.cfg.endpointsLock.RLock() + defer c.cfg.endpointsLock.RUnlock() + // Iterate endpoints. for _, e := range c.cfg.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..7e6df81939 --- /dev/null +++ b/pkg/morph/client/reload.go @@ -0,0 +1,29 @@ +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.cfg.endpointsLock.Lock() + + c.cfg.endpoints = cfg.endpoints + + c.cfg.endpointsLock.Unlock() + + conn := c.conn.Load() + if conn == nil { + return + } + + // Close current connection and attempt to reconnect, if there is no endpoint + // in the config to which the client is connected. + // Node service can be interrupted in this case. + if slices.Contains(cfg.endpoints, conn.client.Endpoint()) { + conn.client.Close() + } +}