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() + } +}