diff --git a/cmd/frostfs-node/netmap.go b/cmd/frostfs-node/netmap.go index 6df947954..3027f52c1 100644 --- a/cmd/frostfs-node/netmap.go +++ b/cmd/frostfs-node/netmap.go @@ -353,12 +353,12 @@ func addNewEpochAsyncNotificationHandler(c *cfg, h event.Handler) { var errRelayBootstrap = errors.New("setting netmap status is forbidden in relay mode") -func (c *cfg) SetNetmapStatus(ctx context.Context, st control.NetmapStatus) error { +func (c *cfg) SetNetmapStatus(ctx context.Context, st control.NetmapStatus, await bool) error { switch st { default: return fmt.Errorf("unsupported status %v", st) case control.NetmapStatus_MAINTENANCE: - return c.setMaintenanceStatus(ctx, false) + return c.setMaintenanceStatus(ctx, false, await) case control.NetmapStatus_ONLINE, control.NetmapStatus_OFFLINE: } @@ -375,7 +375,7 @@ func (c *cfg) SetNetmapStatus(ctx context.Context, st control.NetmapStatus) erro c.cfgNetmap.reBoostrapTurnedOff.Store(true) - return c.updateNetMapState(ctx, func(*nmClient.UpdatePeerPrm) {}) + return c.updateNetMapState(ctx, func(*nmClient.UpdatePeerPrm) {}, await) } func (c *cfg) GetNetmapStatus() (control.NetmapStatus, uint64, error) { @@ -387,11 +387,11 @@ func (c *cfg) GetNetmapStatus() (control.NetmapStatus, uint64, error) { return st, epoch, nil } -func (c *cfg) ForceMaintenance(ctx context.Context) error { - return c.setMaintenanceStatus(ctx, true) +func (c *cfg) ForceMaintenance(ctx context.Context, await bool) error { + return c.setMaintenanceStatus(ctx, true, await) } -func (c *cfg) setMaintenanceStatus(ctx context.Context, force bool) error { +func (c *cfg) setMaintenanceStatus(ctx context.Context, force bool, await bool) error { netSettings, err := c.cfgNetmap.wrapper.ReadNetworkConfiguration() if err != nil { err = fmt.Errorf("read network settings to check maintenance allowance: %w", err) @@ -403,7 +403,7 @@ func (c *cfg) setMaintenanceStatus(ctx context.Context, force bool) error { c.startMaintenance(ctx) if err == nil { - err = c.updateNetMapState(ctx, (*nmClient.UpdatePeerPrm).SetMaintenance) + err = c.updateNetMapState(ctx, (*nmClient.UpdatePeerPrm).SetMaintenance, await) } if err != nil { @@ -416,13 +416,19 @@ func (c *cfg) setMaintenanceStatus(ctx context.Context, force bool) error { // calls UpdatePeerState operation of Netmap contract's client for the local node. // State setter is used to specify node state to switch to. -func (c *cfg) updateNetMapState(ctx context.Context, stateSetter func(*nmClient.UpdatePeerPrm)) error { +func (c *cfg) updateNetMapState(ctx context.Context, stateSetter func(*nmClient.UpdatePeerPrm), await bool) error { var prm nmClient.UpdatePeerPrm prm.SetKey(c.key.PublicKey().Bytes()) stateSetter(&prm) - _, err := c.cfgNetmap.wrapper.UpdatePeerState(ctx, prm) - return err + res, err := c.cfgNetmap.wrapper.UpdatePeerState(ctx, prm) + if err != nil { + return err + } + if await { + return c.cfgNetmap.wrapper.Morph().WaitTxHalt(ctx, res) + } + return nil } type netInfo struct { diff --git a/pkg/morph/client/netmap/peer.go b/pkg/morph/client/netmap/peer.go index 9617d018c..949e8cb63 100644 --- a/pkg/morph/client/netmap/peer.go +++ b/pkg/morph/client/netmap/peer.go @@ -58,9 +58,9 @@ func (c *Client) ForceRemovePeer(ctx context.Context, nodeInfo netmap.NodeInfo, prm.SetControlTX(true) prm.SetVUB(vub) - vub, err := c.UpdatePeerState(ctx, prm) + res, err := c.UpdatePeerState(ctx, prm) if err != nil { return 0, fmt.Errorf("updating peer state: %v", err) } - return vub, nil + return res.VUB, nil } diff --git a/pkg/morph/client/netmap/update_state.go b/pkg/morph/client/netmap/update_state.go index 971a55d33..f9f639c19 100644 --- a/pkg/morph/client/netmap/update_state.go +++ b/pkg/morph/client/netmap/update_state.go @@ -2,7 +2,6 @@ package netmap import ( "context" - "fmt" "git.frostfs.info/TrueCloudLab/frostfs-contract/netmap" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client" @@ -37,7 +36,7 @@ func (u *UpdatePeerPrm) SetMaintenance() { } // UpdatePeerState changes peer status through Netmap contract call. -func (c *Client) UpdatePeerState(ctx context.Context, p UpdatePeerPrm) (uint32, error) { +func (c *Client) UpdatePeerState(ctx context.Context, p UpdatePeerPrm) (client.InvokeRes, error) { method := updateStateMethod if c.client.WithNotary() && c.client.IsAlpha() { @@ -56,9 +55,5 @@ func (c *Client) UpdatePeerState(ctx context.Context, p UpdatePeerPrm) (uint32, prm.SetArgs(int64(p.state), p.key) prm.InvokePrmOptional = p.InvokePrmOptional - res, err := c.client.Invoke(ctx, prm) - if err != nil { - return 0, fmt.Errorf("could not invoke smart contract: %w", err) - } - return res.VUB, nil + return c.client.Invoke(ctx, prm) } diff --git a/pkg/services/control/server/server.go b/pkg/services/control/server/server.go index 94aa1ff5b..27996efa1 100644 --- a/pkg/services/control/server/server.go +++ b/pkg/services/control/server/server.go @@ -46,11 +46,11 @@ type NodeState interface { // // If status is control.NetmapStatus_MAINTENANCE and maintenance is allowed // in the network settings, the node additionally starts local maintenance. - SetNetmapStatus(ctx context.Context, st control.NetmapStatus) error + SetNetmapStatus(ctx context.Context, st control.NetmapStatus, await bool) error // ForceMaintenance works like SetNetmapStatus(control.NetmapStatus_MAINTENANCE) // but starts local maintenance regardless of the network settings. - ForceMaintenance(ctx context.Context) error + ForceMaintenance(ctx context.Context, await bool) error GetNetmapStatus() (control.NetmapStatus, uint64, error) } diff --git a/pkg/services/control/server/set_netmap_status.go b/pkg/services/control/server/set_netmap_status.go index 529041dca..2fc4a8e69 100644 --- a/pkg/services/control/server/set_netmap_status.go +++ b/pkg/services/control/server/set_netmap_status.go @@ -22,6 +22,7 @@ func (s *Server) SetNetmapStatus(ctx context.Context, req *control.SetNetmapStat bodyReq := req.GetBody() st := bodyReq.GetStatus() force := bodyReq.GetForceMaintenance() + await := bodyReq.GetAwait() if force { if st != control.NetmapStatus_MAINTENANCE { @@ -29,9 +30,9 @@ func (s *Server) SetNetmapStatus(ctx context.Context, req *control.SetNetmapStat "force_maintenance MUST be set for %s status only", control.NetmapStatus_MAINTENANCE) } - err = s.nodeState.ForceMaintenance(ctx) + err = s.nodeState.ForceMaintenance(ctx, await) } else { - err = s.nodeState.SetNetmapStatus(ctx, st) + err = s.nodeState.SetNetmapStatus(ctx, st, await) } if err != nil { diff --git a/pkg/services/control/service.proto b/pkg/services/control/service.proto index ae1939e13..58abdf214 100644 --- a/pkg/services/control/service.proto +++ b/pkg/services/control/service.proto @@ -93,7 +93,8 @@ service ControlService { rpc DetachShards(DetachShardsRequest) returns (DetachShardsResponse); // StartShardRebuild starts shard rebuild process. - rpc StartShardRebuild(StartShardRebuildRequest) returns (StartShardRebuildResponse); + rpc StartShardRebuild(StartShardRebuildRequest) + returns (StartShardRebuildResponse); } // Health check request. @@ -141,6 +142,10 @@ message SetNetmapStatusRequest { // maintenance regardless of network settings. The flag MUST NOT be // set for any other status. bool force_maintenance = 2; + + // Boolean flag indicating whether RPC should wait until status change + // request is persisted. + bool await = 3; } // Body of set netmap status request message. @@ -668,7 +673,8 @@ message SealWriteCacheRequest { // Flag indicating whether writecache will be sealed async. bool async = 3; - // If true, then writecache will be sealed, but mode will be restored to the current one. + // If true, then writecache will be sealed, but mode will be restored to the + // current one. bool restore_mode = 4; // If true, then writecache will shrink internal storage. diff --git a/pkg/services/control/service_frostfs.pb.go b/pkg/services/control/service_frostfs.pb.go index 0b4e3cf32..556b7c701 100644 Binary files a/pkg/services/control/service_frostfs.pb.go and b/pkg/services/control/service_frostfs.pb.go differ