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 }