github.com/tsuna/gohbase@v0.0.0-20250731002811-4ffcadfba63e/admin_client.go (about)

     1  // Copyright (C) 2016  The GoHBase Authors.  All rights reserved.
     2  // This file is part of GoHBase.
     3  // Use of this source code is governed by the Apache License 2.0
     4  // that can be found in the COPYING file.
     5  
     6  package gohbase
     7  
     8  import (
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"log/slog"
    13  	"time"
    14  
    15  	"github.com/tsuna/gohbase/hrpc"
    16  	"github.com/tsuna/gohbase/pb"
    17  	"github.com/tsuna/gohbase/region"
    18  	"github.com/tsuna/gohbase/zk"
    19  )
    20  
    21  const (
    22  	// snapshotValidateInterval specifies the amount of time to wait before
    23  	// polling the hbase server about the status of a snapshot operation.
    24  	snaphotValidateInterval time.Duration = time.Second / 2
    25  )
    26  
    27  // AdminClient to perform administrative operations with HMaster
    28  type AdminClient interface {
    29  	CreateTable(t *hrpc.CreateTable) error
    30  	DeleteTable(t *hrpc.DeleteTable) error
    31  	EnableTable(t *hrpc.EnableTable) error
    32  	DisableTable(t *hrpc.DisableTable) error
    33  	CreateSnapshot(t *hrpc.Snapshot) error
    34  	DeleteSnapshot(t *hrpc.Snapshot) error
    35  	ListSnapshots(t *hrpc.ListSnapshots) ([]*pb.SnapshotDescription, error)
    36  	RestoreSnapshot(t *hrpc.Snapshot) error
    37  	ClusterStatus() (*pb.ClusterStatus, error)
    38  	ListTableNames(t *hrpc.ListTableNames) ([]*pb.TableName, error)
    39  	// SetBalancer sets balancer state and returns previous state
    40  	SetBalancer(sb *hrpc.SetBalancer) (bool, error)
    41  	// MoveRegion moves a region to a different RegionServer
    42  	MoveRegion(mr *hrpc.MoveRegion) error
    43  }
    44  
    45  // NewAdminClient creates an admin HBase client.
    46  func NewAdminClient(zkquorum string, options ...Option) AdminClient {
    47  	return newAdminClient(zkquorum, options...)
    48  }
    49  
    50  func newAdminClient(zkquorum string, options ...Option) AdminClient {
    51  	c := &client{
    52  		clientType:    region.MasterClient,
    53  		rpcQueueSize:  defaultRPCQueueSize,
    54  		flushInterval: defaultFlushInterval,
    55  		// empty region in order to be able to set client to it
    56  		adminRegionInfo:     region.NewInfo(0, nil, nil, nil, nil, nil),
    57  		zkTimeout:           defaultZkTimeout,
    58  		zkRoot:              defaultZkRoot,
    59  		effectiveUser:       defaultEffectiveUser,
    60  		regionLookupTimeout: region.DefaultLookupTimeout,
    61  		regionReadTimeout:   region.DefaultReadTimeout,
    62  		newRegionClientFn:   region.NewClient,
    63  		logger:              slog.Default(),
    64  	}
    65  	for _, option := range options {
    66  		option(c)
    67  	}
    68  	c.logger.Debug("Creating new admin client.", "Host", slog.StringValue(zkquorum))
    69  	c.zkClient = zk.NewClient(zkquorum, c.zkTimeout, c.zkDialer, c.logger)
    70  	return c
    71  }
    72  
    73  // Get the status of the cluster
    74  func (c *client) ClusterStatus() (*pb.ClusterStatus, error) {
    75  	pbmsg, err := c.SendRPC(hrpc.NewClusterStatus())
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	r, ok := pbmsg.(*pb.GetClusterStatusResponse)
    81  	if !ok {
    82  		return nil, fmt.Errorf("sendRPC returned not a ClusterStatusResponse")
    83  	}
    84  
    85  	return r.GetClusterStatus(), nil
    86  }
    87  
    88  func (c *client) CreateTable(t *hrpc.CreateTable) error {
    89  	pbmsg, err := c.SendRPC(t)
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	r, ok := pbmsg.(*pb.CreateTableResponse)
    95  	if !ok {
    96  		return fmt.Errorf("sendRPC returned not a CreateTableResponse")
    97  	}
    98  
    99  	return c.checkProcedureWithBackoff(t.Context(), r.GetProcId())
   100  }
   101  
   102  func (c *client) DeleteTable(t *hrpc.DeleteTable) error {
   103  	pbmsg, err := c.SendRPC(t)
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	r, ok := pbmsg.(*pb.DeleteTableResponse)
   109  	if !ok {
   110  		return fmt.Errorf("sendRPC returned not a DeleteTableResponse")
   111  	}
   112  
   113  	return c.checkProcedureWithBackoff(t.Context(), r.GetProcId())
   114  }
   115  
   116  func (c *client) EnableTable(t *hrpc.EnableTable) error {
   117  	pbmsg, err := c.SendRPC(t)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	r, ok := pbmsg.(*pb.EnableTableResponse)
   123  	if !ok {
   124  		return fmt.Errorf("sendRPC returned not a EnableTableResponse")
   125  	}
   126  
   127  	return c.checkProcedureWithBackoff(t.Context(), r.GetProcId())
   128  }
   129  
   130  func (c *client) DisableTable(t *hrpc.DisableTable) error {
   131  	pbmsg, err := c.SendRPC(t)
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	r, ok := pbmsg.(*pb.DisableTableResponse)
   137  	if !ok {
   138  		return fmt.Errorf("sendRPC returned not a DisableTableResponse")
   139  	}
   140  
   141  	return c.checkProcedureWithBackoff(t.Context(), r.GetProcId())
   142  }
   143  
   144  func (c *client) checkProcedureWithBackoff(ctx context.Context, procID uint64) error {
   145  	backoff := backoffStart
   146  	for {
   147  		pbmsg, err := c.SendRPC(hrpc.NewGetProcedureState(ctx, procID))
   148  		if err != nil {
   149  			return err
   150  		}
   151  
   152  		res := pbmsg.(*pb.GetProcedureResultResponse)
   153  		switch res.GetState() {
   154  		case pb.GetProcedureResultResponse_NOT_FOUND:
   155  			return fmt.Errorf("procedure not found")
   156  		case pb.GetProcedureResultResponse_FINISHED:
   157  			if fe := res.Exception; fe != nil {
   158  				ge := fe.GenericException
   159  				if ge == nil {
   160  					return errors.New("got unexpected empty exception")
   161  				}
   162  				return fmt.Errorf("procedure exception: %s: %s", ge.GetClassName(), ge.GetMessage())
   163  			}
   164  			return nil
   165  		default:
   166  			backoff, err = sleepAndIncreaseBackoff(ctx, backoff)
   167  			if err != nil {
   168  				return err
   169  			}
   170  		}
   171  	}
   172  }
   173  
   174  // CreateSnapshot creates a snapshot in HBase.
   175  //
   176  // If a context happens during creation, no cleanup is done.
   177  func (c *client) CreateSnapshot(t *hrpc.Snapshot) error {
   178  
   179  	pbmsg, err := c.SendRPC(t)
   180  	if err != nil {
   181  		return err
   182  	}
   183  
   184  	_, ok := pbmsg.(*pb.SnapshotResponse)
   185  	if !ok {
   186  		return errors.New("sendPRC returned not a SnapshotResponse")
   187  	}
   188  
   189  	ticker := time.NewTicker(snaphotValidateInterval)
   190  	defer ticker.Stop()
   191  	check := hrpc.NewSnapshotDone(t)
   192  	ctx := t.Context()
   193  
   194  	for {
   195  		select {
   196  		case <-ticker.C:
   197  			pbmsgs, err := c.SendRPC(check)
   198  			if err != nil {
   199  				return err
   200  			}
   201  
   202  			r, ok := pbmsgs.(*pb.IsSnapshotDoneResponse)
   203  			if !ok {
   204  				return errors.New("sendPRC returned not a IsSnapshotDoneResponse")
   205  			}
   206  
   207  			if r.GetDone() {
   208  				return nil
   209  			}
   210  		case <-ctx.Done():
   211  			return ctx.Err()
   212  		}
   213  	}
   214  }
   215  
   216  // DeleteSnapshot deletes a snapshot in HBase.
   217  func (c *client) DeleteSnapshot(t *hrpc.Snapshot) error {
   218  	rt := hrpc.NewDeleteSnapshot(t)
   219  	pbmsg, err := c.SendRPC(rt)
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	_, ok := pbmsg.(*pb.DeleteSnapshotResponse)
   225  	if !ok {
   226  		return errors.New("sendPRC returned not a DeleteSnapshotResponse")
   227  	}
   228  
   229  	return nil
   230  }
   231  
   232  func (c *client) ListSnapshots(t *hrpc.ListSnapshots) ([]*pb.SnapshotDescription, error) {
   233  	pbmsg, err := c.SendRPC(t)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  
   238  	r, ok := pbmsg.(*pb.GetCompletedSnapshotsResponse)
   239  	if !ok {
   240  		return nil, errors.New("sendPRC returned not a GetCompletedSnapshotsResponse")
   241  	}
   242  
   243  	return r.GetSnapshots(), nil
   244  
   245  }
   246  
   247  func (c *client) RestoreSnapshot(t *hrpc.Snapshot) error {
   248  	rt := hrpc.NewRestoreSnapshot(t)
   249  	pbmsg, err := c.SendRPC(rt)
   250  	if err != nil {
   251  		return err
   252  	}
   253  
   254  	_, ok := pbmsg.(*pb.RestoreSnapshotResponse)
   255  	if !ok {
   256  		return errors.New("sendPRC returned not a RestoreSnapshotResponse")
   257  	}
   258  	return nil
   259  }
   260  
   261  func (c *client) ListTableNames(t *hrpc.ListTableNames) ([]*pb.TableName, error) {
   262  	pbmsg, err := c.SendRPC(t)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  
   267  	res, ok := pbmsg.(*pb.GetTableNamesResponse)
   268  	if !ok {
   269  		return nil, errors.New("sendPRC returned not a GetTableNamesResponse")
   270  	}
   271  
   272  	return res.GetTableNames(), nil
   273  }
   274  
   275  func (c *client) SetBalancer(sb *hrpc.SetBalancer) (bool, error) {
   276  	pbmsg, err := c.SendRPC(sb)
   277  	if err != nil {
   278  		return false, err
   279  	}
   280  	res, ok := pbmsg.(*pb.SetBalancerRunningResponse)
   281  	if !ok {
   282  		return false, errors.New("SendPRC returned not a SetBalancerRunningResponse")
   283  	}
   284  	return res.GetPrevBalanceValue(), nil
   285  }
   286  
   287  func (c *client) MoveRegion(mr *hrpc.MoveRegion) error {
   288  	pbmsg, err := c.SendRPC(mr)
   289  	if err != nil {
   290  		return err
   291  	}
   292  	_, ok := pbmsg.(*pb.MoveRegionResponse)
   293  	if !ok {
   294  		return errors.New("SendPRC returned not a MoveRegionResponse")
   295  	}
   296  	return nil
   297  }