github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/go-hbase/client.go (about)

     1  package hbase
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/md5"
     6  	"encoding/binary"
     7  	"encoding/hex"
     8  	"fmt"
     9  	"sync"
    10  	"time"
    11  
    12  	pb "github.com/insionng/yougam/libraries/golang/protobuf/proto"
    13  	"github.com/insionng/yougam/libraries/juju/errors"
    14  	"github.com/insionng/yougam/libraries/ngaut/go-zookeeper/zk"
    15  	"github.com/insionng/yougam/libraries/ngaut/log"
    16  	"github.com/insionng/yougam/libraries/pingcap/go-hbase/proto"
    17  )
    18  
    19  const (
    20  	zkRootRegionPath = "/meta-region-server"
    21  	zkMasterAddrPath = "/master"
    22  
    23  	magicHeadByte           = 0xff
    24  	magicHeadSize           = 1
    25  	idLengthSize            = 4
    26  	md5HexSize              = 32
    27  	servernameSeparator     = ","
    28  	rpcTimeout              = 30000
    29  	pingTimeout             = 30000
    30  	callTimeout             = 5000
    31  	defaultMaxActionRetries = 3
    32  	// Some operations can take a long time such as disable of big table.
    33  	// numRetries is for 'normal' stuff... Multiply by this factor when
    34  	// want to wait a long time.
    35  	retryLongerMultiplier    = 31
    36  	socketDefaultRetryWaitMs = 200
    37  	defaultRetryWaitMs       = 100
    38  	// always >= any unix timestamp(hbase version)
    39  	beyondMaxTimestamp = "99999999999999"
    40  )
    41  
    42  var (
    43  	hbaseHeaderBytes []byte = []byte("HBas")
    44  	metaTableName    []byte = []byte("hbase:meta")
    45  	metaRegionName   []byte = []byte("hbase:meta,,1")
    46  )
    47  
    48  var retryPauseTime = []int64{1, 2, 3, 5, 10, 20, 40, 100, 100, 100, 100, 200, 200}
    49  
    50  type RegionInfo struct {
    51  	Server         string
    52  	StartKey       []byte
    53  	EndKey         []byte
    54  	Name           string
    55  	Ts             string
    56  	TableNamespace string
    57  	TableName      string
    58  	Offline        bool
    59  	Split          bool
    60  }
    61  
    62  type tableInfo struct {
    63  	tableName string
    64  	families  []string
    65  }
    66  
    67  // export client interface
    68  type HBaseClient interface {
    69  	Get(tbl string, g *Get) (*ResultRow, error)
    70  	Put(tbl string, p *Put) (bool, error)
    71  	Delete(tbl string, d *Delete) (bool, error)
    72  	TableExists(tbl string) (bool, error)
    73  	DropTable(t string) error
    74  	DisableTable(t string) error
    75  	EnableTable(t string) error
    76  	CreateTable(t *TableDescriptor, splits [][]byte) error
    77  	ServiceCall(table string, call *CoprocessorServiceCall) (*proto.CoprocessorServiceResponse, error)
    78  	LocateRegion(table, row []byte, useCache bool) (*RegionInfo, error)
    79  	GetRegions(table []byte, useCache bool) ([]*RegionInfo, error)
    80  	Split(tblOrRegion, splitPoint string) error
    81  	CleanRegionCache(table []byte)
    82  	CleanAllRegionCache()
    83  	Close() error
    84  }
    85  
    86  // hbase client implemetation
    87  var _ HBaseClient = (*client)(nil)
    88  
    89  type client struct {
    90  	mu               sync.RWMutex // for read/update region info
    91  	zkClient         *zk.Conn
    92  	zkHosts          []string
    93  	zkRoot           string
    94  	prefetched       map[string]bool
    95  	cachedConns      map[string]*connection
    96  	cachedRegionInfo map[string]map[string]*RegionInfo
    97  	maxRetries       int
    98  	rootServerName   *proto.ServerName
    99  	masterServerName *proto.ServerName
   100  }
   101  
   102  func serverNameToAddr(server *proto.ServerName) string {
   103  	return fmt.Sprintf("%s:%d", server.GetHostName(), server.GetPort())
   104  }
   105  
   106  func cachedConnKey(addr string, srvType ServiceType) string {
   107  	return fmt.Sprintf("%s|%d", addr, srvType)
   108  }
   109  
   110  func NewClient(zkHosts []string, zkRoot string) (HBaseClient, error) {
   111  	cl := &client{
   112  		zkHosts:          zkHosts,
   113  		zkRoot:           zkRoot,
   114  		cachedConns:      make(map[string]*connection),
   115  		cachedRegionInfo: make(map[string]map[string]*RegionInfo),
   116  		prefetched:       make(map[string]bool),
   117  		maxRetries:       defaultMaxActionRetries,
   118  	}
   119  
   120  	err := cl.init()
   121  	if err != nil {
   122  		return nil, errors.Trace(err)
   123  	}
   124  
   125  	return cl, nil
   126  }
   127  
   128  func (c *client) decodeMeta(data []byte) (*proto.ServerName, error) {
   129  	if data[0] != magicHeadByte {
   130  		return nil, errors.New("unknown packet")
   131  	}
   132  
   133  	var n int32
   134  	err := binary.Read(bytes.NewBuffer(data[1:]), binary.BigEndian, &n)
   135  	if err != nil {
   136  		return nil, errors.Trace(err)
   137  	}
   138  
   139  	dataOffset := magicHeadSize + idLengthSize + int(n)
   140  	data = data[(dataOffset + 4):]
   141  
   142  	var mrs proto.MetaRegionServer
   143  	err = pb.Unmarshal(data, &mrs)
   144  	if err != nil {
   145  		return nil, errors.Trace(err)
   146  	}
   147  
   148  	return mrs.GetServer(), nil
   149  }
   150  
   151  // init and get root region server addr and master addr
   152  func (c *client) init() error {
   153  	zkclient, _, err := zk.Connect(c.zkHosts, time.Second*30)
   154  	if err != nil {
   155  		return errors.Trace(err)
   156  	}
   157  	c.zkClient = zkclient
   158  
   159  	res, _, _, err := c.zkClient.GetW(c.zkRoot + zkRootRegionPath)
   160  	if err != nil {
   161  		return errors.Trace(err)
   162  	}
   163  
   164  	c.rootServerName, err = c.decodeMeta(res)
   165  	if err != nil {
   166  		return errors.Trace(err)
   167  	}
   168  
   169  	log.Debug("connect root region server...", c.rootServerName)
   170  	serverAddr := serverNameToAddr(c.rootServerName)
   171  	conn, err := newConnection(serverAddr, ClientService)
   172  	if err != nil {
   173  		return errors.Trace(err)
   174  	}
   175  
   176  	// Set buffered regionserver conn.
   177  	cachedKey := cachedConnKey(serverAddr, ClientService)
   178  	c.cachedConns[cachedKey] = conn
   179  
   180  	res, _, _, err = c.zkClient.GetW(c.zkRoot + zkMasterAddrPath)
   181  	if err != nil {
   182  		return errors.Trace(err)
   183  	}
   184  
   185  	c.masterServerName, err = c.decodeMeta(res)
   186  	if err != nil {
   187  		return errors.Trace(err)
   188  	}
   189  
   190  	return nil
   191  }
   192  
   193  // get connection
   194  func (c *client) getConn(addr string, srvType ServiceType) (*connection, error) {
   195  	connKey := cachedConnKey(addr, srvType)
   196  	c.mu.RLock()
   197  	conn, ok := c.cachedConns[connKey]
   198  	c.mu.RUnlock()
   199  
   200  	if ok {
   201  		return conn, nil
   202  	}
   203  
   204  	var err error
   205  	conn, err = newConnection(addr, srvType)
   206  	if err != nil {
   207  		return nil, errors.Errorf("create new connection failed - %v", errors.ErrorStack(err))
   208  	}
   209  	c.mu.Lock()
   210  	c.cachedConns[connKey] = conn
   211  	c.mu.Unlock()
   212  	return conn, nil
   213  }
   214  
   215  func (c *client) getAdminConn(addr string) (*connection, error) {
   216  	return c.getConn(addr, AdminService)
   217  }
   218  
   219  func (c *client) getClientConn(addr string) (*connection, error) {
   220  	return c.getConn(addr, ClientService)
   221  }
   222  
   223  func (c *client) getMasterConn() (*connection, error) {
   224  	return c.getConn(serverNameToAddr(c.masterServerName), MasterService)
   225  }
   226  
   227  func (c *client) doAction(conn *connection, req pb.Message) (chan pb.Message, error) {
   228  	cl := newCall(req)
   229  	err := conn.call(cl)
   230  	if err != nil {
   231  		return nil, errors.Trace(err)
   232  	}
   233  
   234  	return cl.responseCh, nil
   235  }
   236  
   237  func (c *client) adminAction(req pb.Message) (chan pb.Message, error) {
   238  	conn, err := c.getMasterConn()
   239  	if err != nil {
   240  		return nil, errors.Trace(err)
   241  	}
   242  	return c.doAction(conn, req)
   243  }
   244  
   245  func (c *client) regionAction(addr string, req pb.Message) (chan pb.Message, error) {
   246  	conn, err := c.getAdminConn(addr)
   247  	if err != nil {
   248  		return nil, errors.Trace(err)
   249  	}
   250  	return c.doAction(conn, req)
   251  }
   252  
   253  // http://stackoverflow.com/questions/27602013/correct-way-to-get-region-name-by-using-hbase-api
   254  func (c *client) createRegionName(table, startKey []byte, id string, newFormat bool) []byte {
   255  	if len(startKey) == 0 {
   256  		startKey = make([]byte, 1)
   257  	}
   258  
   259  	b := bytes.Join([][]byte{table, startKey, []byte(id)}, []byte{','})
   260  
   261  	if newFormat {
   262  		m := md5.Sum(b)
   263  		mhex := []byte(hex.EncodeToString(m[:]))
   264  		b = append(bytes.Join([][]byte{b, mhex}, []byte{'.'}), '.')
   265  	}
   266  
   267  	return b
   268  }
   269  
   270  func (c *client) parseRegion(rr *ResultRow) (*RegionInfo, error) {
   271  	regionInfoCol, ok := rr.Columns["info:regioninfo"]
   272  	if !ok {
   273  		return nil, errors.Errorf("Unable to parse region location (no regioninfo column): %#v", rr)
   274  	}
   275  
   276  	offset := bytes.Index(regionInfoCol.Value, []byte("PBUF")) + 4
   277  	regionInfoBytes := regionInfoCol.Value[offset:]
   278  
   279  	var info proto.RegionInfo
   280  	err := pb.Unmarshal(regionInfoBytes, &info)
   281  	if err != nil {
   282  		return nil, errors.Errorf("Unable to parse region location: %#v", err)
   283  	}
   284  
   285  	ri := &RegionInfo{
   286  		StartKey:       info.GetStartKey(),
   287  		EndKey:         info.GetEndKey(),
   288  		Name:           bytes.NewBuffer(rr.Row).String(),
   289  		TableNamespace: string(info.GetTableName().GetNamespace()),
   290  		TableName:      string(info.GetTableName().GetQualifier()),
   291  		Offline:        info.GetOffline(),
   292  		Split:          info.GetSplit(),
   293  	}
   294  
   295  	if v, ok := rr.Columns["info:server"]; ok {
   296  		ri.Server = string(v.Value)
   297  	}
   298  
   299  	return ri, nil
   300  }
   301  
   302  func (c *client) getMetaRegion() *RegionInfo {
   303  	return &RegionInfo{
   304  		StartKey: []byte{},
   305  		EndKey:   []byte{},
   306  		Name:     string(metaRegionName),
   307  		Server:   serverNameToAddr(c.rootServerName),
   308  	}
   309  }
   310  
   311  func (c *client) getCachedLocation(table, row []byte) *RegionInfo {
   312  	c.mu.RLock()
   313  	defer c.mu.RUnlock()
   314  
   315  	tableStr := string(table)
   316  	if regions, ok := c.cachedRegionInfo[tableStr]; ok {
   317  		for _, region := range regions {
   318  			if (len(region.EndKey) == 0 ||
   319  				bytes.Compare(row, region.EndKey) < 0) &&
   320  				(len(region.StartKey) == 0 ||
   321  					bytes.Compare(row, region.StartKey) >= 0) {
   322  				return region
   323  			}
   324  		}
   325  	}
   326  
   327  	return nil
   328  }
   329  
   330  func (c *client) updateRegionCache(table []byte, region *RegionInfo) {
   331  	c.mu.Lock()
   332  	defer c.mu.Unlock()
   333  
   334  	tableStr := string(table)
   335  	if _, ok := c.cachedRegionInfo[tableStr]; !ok {
   336  		c.cachedRegionInfo[tableStr] = make(map[string]*RegionInfo)
   337  	}
   338  	c.cachedRegionInfo[tableStr][region.Name] = region
   339  }
   340  
   341  func (c *client) CleanRegionCache(table []byte) {
   342  	c.mu.Lock()
   343  	defer c.mu.Unlock()
   344  	delete(c.cachedRegionInfo, string(table))
   345  }
   346  
   347  func (c *client) CleanAllRegionCache() {
   348  	c.mu.Lock()
   349  	defer c.mu.Unlock()
   350  	c.cachedRegionInfo = map[string]map[string]*RegionInfo{}
   351  }
   352  
   353  func (c *client) LocateRegion(table, row []byte, useCache bool) (*RegionInfo, error) {
   354  	// If user wants to locate metaregion, just return it.
   355  	if bytes.Equal(table, metaTableName) {
   356  		return c.getMetaRegion(), nil
   357  	}
   358  
   359  	// Try to get location from cache.
   360  	if useCache {
   361  		if r := c.getCachedLocation(table, row); r != nil {
   362  			return r, nil
   363  		}
   364  	}
   365  
   366  	// If cache missed or not using cache, try to get and update region info.
   367  	metaRegion := c.getMetaRegion()
   368  	conn, err := c.getClientConn(metaRegion.Server)
   369  	if err != nil {
   370  		return nil, errors.Trace(err)
   371  	}
   372  
   373  	regionRow := c.createRegionName(table, row, beyondMaxTimestamp, true)
   374  
   375  	call := newCall(&proto.GetRequest{
   376  		Region: &proto.RegionSpecifier{
   377  			Type:  proto.RegionSpecifier_REGION_NAME.Enum(),
   378  			Value: metaRegionName,
   379  		},
   380  		Get: &proto.Get{
   381  			Row: regionRow,
   382  			Column: []*proto.Column{&proto.Column{
   383  				Family: []byte("info"),
   384  			}},
   385  			ClosestRowBefore: pb.Bool(true),
   386  		},
   387  	})
   388  
   389  	err = conn.call(call)
   390  	if err != nil {
   391  		return nil, errors.Trace(err)
   392  	}
   393  
   394  	response := <-call.responseCh
   395  
   396  	switch r := response.(type) {
   397  	case *proto.GetResponse:
   398  		res := r.GetResult()
   399  		if res == nil {
   400  			return nil, errors.Errorf("Empty region: [table=%s] [row=%q] [region_row=%q]", table, row, regionRow)
   401  		}
   402  
   403  		rr := NewResultRow(res)
   404  		region, err := c.parseRegion(rr)
   405  		if err != nil {
   406  			return nil, errors.Trace(err)
   407  		}
   408  
   409  		c.updateRegionCache(table, region)
   410  		return region, nil
   411  	case *exception:
   412  		return nil, errors.New(r.msg)
   413  	default:
   414  		log.Warnf("Unknown response - %T - %v", r, r)
   415  	}
   416  
   417  	return nil, errors.Errorf("Couldn't find the region: [table=%s] [row=%q] [region_row=%q]", table, row, regionRow)
   418  }
   419  
   420  func (c *client) GetRegions(table []byte, useCache bool) ([]*RegionInfo, error) {
   421  	var regions []*RegionInfo
   422  	startKey := []byte("")
   423  	// Get first region.
   424  	region, err := c.LocateRegion(table, []byte(startKey), useCache)
   425  	if err != nil {
   426  		return nil, errors.Errorf("couldn't find any region: [table=%s] [useCache=%t]", table, useCache)
   427  	}
   428  	regions = append(regions, region)
   429  	startKey = region.EndKey
   430  	for len(startKey) > 0 {
   431  		region, err = c.LocateRegion(table, []byte(startKey), useCache)
   432  		if err != nil {
   433  			return nil, errors.Trace(err)
   434  		}
   435  		regions = append(regions, region)
   436  		startKey = region.EndKey
   437  	}
   438  	return regions, nil
   439  }
   440  
   441  func (c *client) Close() error {
   442  	if c.zkClient != nil {
   443  		c.zkClient.Close()
   444  	}
   445  
   446  	for _, conn := range c.cachedConns {
   447  		err := conn.close()
   448  		if err != nil {
   449  			return errors.Trace(err)
   450  		}
   451  	}
   452  
   453  	return nil
   454  }