From 169073d5bd4b7425dcfd39ffdfce733d5c78b13e Mon Sep 17 00:00:00 2001 From: Alex Sarkesian Date: Tue, 18 Oct 2022 20:29:32 -0700 Subject: [PATCH] server: add api for decommission pre-flight checks While we have an API for checking the status of an in-progress decommission, we did not previously have an API to execute sanity checks prior to requesting a node to move into the `DECOMMISSIONING` state. This adds an API to do just that, intended to be called by the CLI prior to issuing a subsequent `Decommission` RPC request. Fixes #91568. Release note: None --- docs/generated/http/full.md | 97 +++++++++++++++++++++++++++++++++ pkg/server/admin.go | 8 +++ pkg/server/admin_test.go | 27 +++++++++ pkg/server/serverpb/admin.proto | 64 ++++++++++++++++++++++ 4 files changed, 196 insertions(+) diff --git a/docs/generated/http/full.md b/docs/generated/http/full.md index a7c840fa75ae..dd97f47a9215 100644 --- a/docs/generated/http/full.md +++ b/docs/generated/http/full.md @@ -6276,6 +6276,103 @@ DrainResponse is the response to a successful DrainRequest. +## DecommissionPreCheck + + + +DecommissionPreCheck requests that the server execute preliminary checks +to evaluate the possibility of successfully decommissioning a given node. + +Support status: [reserved](#support-status) + +#### Request Parameters + + + + +DecommissionPreCheckRequest requests that preliminary checks be run to +ensure that the specified node(s) can be decommissioned successfully. + + +| Field | Type | Label | Description | Support status | +| ----- | ---- | ----- | ----------- | -------------- | +| node_ids | [int32](#cockroach.server.serverpb.DecommissionPreCheckRequest-int32) | repeated | | [reserved](#support-status) | +| num_replica_report | [int32](#cockroach.server.serverpb.DecommissionPreCheckRequest-int32) | | The maximum number of ranges for which to report errors. | [reserved](#support-status) | +| strict_readiness | [bool](#cockroach.server.serverpb.DecommissionPreCheckRequest-bool) | | If true, all ranges on the checked nodes must only need replacement or removal for decommissioning. | [reserved](#support-status) | + + + + + + + +#### Response Parameters + + + + +DecommissionPreCheckResponse returns the number of replicas that encountered +errors when running preliminary decommissioning checks, as well as the +associated error messages and traces, for each node. + + +| Field | Type | Label | Description | Support status | +| ----- | ---- | ----- | ----------- | -------------- | +| checked_nodes | [DecommissionPreCheckResponse.NodeCheckResult](#cockroach.server.serverpb.DecommissionPreCheckResponse-cockroach.server.serverpb.DecommissionPreCheckResponse.NodeCheckResult) | repeated | Status of the preliminary decommission checks across nodes. | [reserved](#support-status) | + + + + + + + +#### DecommissionPreCheckResponse.NodeCheckResult + + + +| Field | Type | Label | Description | Support status | +| ----- | ---- | ----- | ----------- | -------------- | +| node_id | [int32](#cockroach.server.serverpb.DecommissionPreCheckResponse-int32) | | | [reserved](#support-status) | +| decommission_readiness | [DecommissionPreCheckResponse.NodeReadiness](#cockroach.server.serverpb.DecommissionPreCheckResponse-cockroach.server.serverpb.DecommissionPreCheckResponse.NodeReadiness) | | | [reserved](#support-status) | +| liveness_status | [cockroach.kv.kvserver.liveness.livenesspb.NodeLivenessStatus](#cockroach.server.serverpb.DecommissionPreCheckResponse-cockroach.kv.kvserver.liveness.livenesspb.NodeLivenessStatus) | | The liveness status of the given node. | [reserved](#support-status) | +| replica_count | [int64](#cockroach.server.serverpb.DecommissionPreCheckResponse-int64) | | The number of total replicas on the node, computed by scanning range descriptors. | [reserved](#support-status) | +| checked_ranges | [DecommissionPreCheckResponse.RangeCheckResult](#cockroach.server.serverpb.DecommissionPreCheckResponse-cockroach.server.serverpb.DecommissionPreCheckResponse.RangeCheckResult) | repeated | The details and recorded traces from preprocessing each range with a replica on the checked nodes that resulted in error, up to the maximum specified in the request. | [reserved](#support-status) | + + + + + + +#### DecommissionPreCheckResponse.RangeCheckResult + + + +| Field | Type | Label | Description | Support status | +| ----- | ---- | ----- | ----------- | -------------- | +| range_id | [int32](#cockroach.server.serverpb.DecommissionPreCheckResponse-int32) | | | [reserved](#support-status) | +| allocatorAction | [string](#cockroach.server.serverpb.DecommissionPreCheckResponse-string) | | The action determined by the allocator that is needed for the range. | [reserved](#support-status) | +| events | [TraceEvent](#cockroach.server.serverpb.DecommissionPreCheckResponse-cockroach.server.serverpb.TraceEvent) | repeated | All trace events collected while processing the range in the allocator. | [reserved](#support-status) | +| error | [string](#cockroach.server.serverpb.DecommissionPreCheckResponse-string) | | The error message from the allocator's processing, if any. | [reserved](#support-status) | + + + + + + +#### TraceEvent + + + +| Field | Type | Label | Description | Support status | +| ----- | ---- | ----- | ----------- | -------------- | +| time | [google.protobuf.Timestamp](#cockroach.server.serverpb.DecommissionPreCheckResponse-google.protobuf.Timestamp) | | | [reserved](#support-status) | +| message | [string](#cockroach.server.serverpb.DecommissionPreCheckResponse-string) | | | [reserved](#support-status) | + + + + + + ## Decommission diff --git a/pkg/server/admin.go b/pkg/server/admin.go index 655933c1050d..999f0695aa27 100644 --- a/pkg/server/admin.go +++ b/pkg/server/admin.go @@ -2656,6 +2656,14 @@ func (s *adminServer) getStatementBundle(ctx context.Context, id int64, w http.R _, _ = io.Copy(w, &bundle) } +// DecommissionPreCheck runs checks and returns the DecommissionPreCheckResponse +// for the given nodes. +func (s *systemAdminServer) DecommissionPreCheck( + ctx context.Context, req *serverpb.DecommissionPreCheckRequest, +) (*serverpb.DecommissionPreCheckResponse, error) { + return nil, grpcstatus.Errorf(codes.Unimplemented, "method DecommissionPreCheck not implemented") +} + // DecommissionStatus returns the DecommissionStatus for all or the given nodes. func (s *systemAdminServer) DecommissionStatus( ctx context.Context, req *serverpb.DecommissionStatusRequest, diff --git a/pkg/server/admin_test.go b/pkg/server/admin_test.go index 2013f1dce86a..a6578399977f 100644 --- a/pkg/server/admin_test.go +++ b/pkg/server/admin_test.go @@ -2442,6 +2442,33 @@ func TestEndpointTelemetryBasic(t *testing.T) { ))) } +// TestDecommissionPreCheck tests the basic functionality of the +// DecommissionPreCheck endpoint. +func TestDecommissionPreCheck(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + skip.UnderRace(t) // can't handle 7-node clusters + + ctx := context.Background() + tc := serverutils.StartNewTestCluster(t, 7, base.TestClusterArgs{ + ReplicationMode: base.ReplicationManual, // saves time + }) + defer tc.Stopper().Stop(ctx) + + adminSrv := tc.Server(4) + conn, err := adminSrv.RPCContext().GRPCDialNode( + adminSrv.RPCAddr(), adminSrv.NodeID(), rpc.DefaultClass).Connect(ctx) + require.NoError(t, err) + adminClient := serverpb.NewAdminClient(conn) + + resp, err := adminClient.DecommissionPreCheck(ctx, &serverpb.DecommissionPreCheckRequest{ + NodeIDs: []roachpb.NodeID{tc.Server(5).NodeID()}, + }) + require.Error(t, err) + require.Equal(t, codes.Unimplemented, status.Code(err)) + require.Nil(t, resp) +} + func TestDecommissionSelf(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) diff --git a/pkg/server/serverpb/admin.proto b/pkg/server/serverpb/admin.proto index 457ebd603ff3..c3ce5bc404b7 100644 --- a/pkg/server/serverpb/admin.proto +++ b/pkg/server/serverpb/admin.proto @@ -479,6 +479,65 @@ message DrainResponse { reserved 1; } +// DecommissionPreCheckRequest requests that preliminary checks be run to +// ensure that the specified node(s) can be decommissioned successfully. +message DecommissionPreCheckRequest { + repeated int32 node_ids = 1 [(gogoproto.customname) = "NodeIDs", + (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/roachpb.NodeID"]; + + // The maximum number of ranges for which to report errors. + int32 num_replica_report = 2; + + // If true, all ranges on the checked nodes must only need replacement or + // removal for decommissioning. + bool strict_readiness = 3; +} + +// DecommissionPreCheckResponse returns the number of replicas that encountered +// errors when running preliminary decommissioning checks, as well as the +// associated error messages and traces, for each node. +message DecommissionPreCheckResponse { + enum NodeReadiness { + UNKNOWN = 0; + READY = 1; + ALREADY_DECOMMISSIONED = 2; + ALLOCATION_ERRORS = 3; + } + + message RangeCheckResult { + int32 range_id = 1 [ (gogoproto.customname) = "RangeID", + (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/roachpb.RangeID"]; + // The action determined by the allocator that is needed for the range. + string allocatorAction = 2; + // All trace events collected while processing the range in the allocator. + repeated TraceEvent events = 3; + // The error message from the allocator's processing, if any. + string error = 4; + } + + message NodeCheckResult { + int32 node_id = 1 [ (gogoproto.customname) = "NodeID", + (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/roachpb.NodeID"]; + + NodeReadiness decommission_readiness = 2; + + // The liveness status of the given node. + kv.kvserver.liveness.livenesspb.NodeLivenessStatus liveness_status = 3; + + // The number of total replicas on the node, computed by scanning range + // descriptors. + int64 replica_count = 4; + + // The details and recorded traces from preprocessing each range with a + // replica on the checked nodes that resulted in error, up to the maximum + // specified in the request. + repeated RangeCheckResult checked_ranges = 5 [(gogoproto.nullable) = false]; + } + + // Status of the preliminary decommission checks across nodes. + repeated NodeCheckResult checked_nodes = 1 [(gogoproto.nullable) = false]; +} + // DecommissionStatusRequest requests the decommissioning status for the // specified or, if none are specified, all nodes. message DecommissionStatusRequest { @@ -1072,6 +1131,11 @@ service Admin { rpc Drain(DrainRequest) returns (stream DrainResponse) { } + // DecommissionPreCheck requests that the server execute preliminary checks + // to evaluate the possibility of successfully decommissioning a given node. + rpc DecommissionPreCheck(DecommissionPreCheckRequest) returns (DecommissionPreCheckResponse) { + } + // Decommission puts the node(s) into the specified decommissioning state. // If this ever becomes exposed via HTTP, ensure that it performs // authorization. See #42567.