From 34b581d8b5f5eb2f9d4213fe1b41c782e16c3591 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..a33f7cf357 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 in the `endpoints` there is no endpoint that the client is connected to, it will attempt to connect to another endpoint N3. Node service can be interrupted in this case. | diff --git a/pkg/morph/client/constructor.go b/pkg/morph/client/constructor.go index c852b0f941..dd827345e5 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 + endpoints []string + endpointsLock *sync.RWMutex 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() + } +}