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

     1  package themis
     2  
     3  import (
     4  	"fmt"
     5  	"runtime/debug"
     6  
     7  	pb "github.com/insionng/yougam/libraries/golang/protobuf/proto"
     8  	"github.com/insionng/yougam/libraries/juju/errors"
     9  	"github.com/insionng/yougam/libraries/ngaut/log"
    10  	"github.com/insionng/yougam/libraries/pingcap/go-hbase"
    11  	"github.com/insionng/yougam/libraries/pingcap/go-hbase/proto"
    12  	"github.com/insionng/yougam/libraries/pingcap/go-themis/oracle"
    13  )
    14  
    15  func newThemisRPC(client hbase.HBaseClient, oracle oracle.Oracle, conf TxnConfig) *themisRPC {
    16  	return &themisRPC{
    17  		client: client,
    18  		conf:   conf,
    19  		oracle: oracle,
    20  	}
    21  }
    22  
    23  type themisRPC struct {
    24  	client hbase.HBaseClient
    25  	conf   TxnConfig
    26  	oracle oracle.Oracle
    27  }
    28  
    29  func (rpc *themisRPC) call(methodName string, tbl, row []byte, req pb.Message, resp pb.Message) error {
    30  	param, _ := pb.Marshal(req)
    31  
    32  	call := &hbase.CoprocessorServiceCall{
    33  		Row:          row,
    34  		ServiceName:  ThemisServiceName,
    35  		MethodName:   methodName,
    36  		RequestParam: param,
    37  	}
    38  	r, err := rpc.client.ServiceCall(string(tbl), call)
    39  	if err != nil {
    40  		return errors.Trace(err)
    41  	}
    42  	err = pb.Unmarshal(r.GetValue().GetValue(), resp)
    43  	if err != nil {
    44  		return errors.Trace(err)
    45  	}
    46  	return nil
    47  }
    48  
    49  func (rpc *themisRPC) checkAndSetLockIsExpired(lock Lock) (bool, error) {
    50  	expired := rpc.oracle.IsExpired(lock.Timestamp(), rpc.conf.TTLInMs)
    51  	lock.SetExpired(expired)
    52  	return expired, nil
    53  }
    54  
    55  func (rpc *themisRPC) themisGet(tbl []byte, g *hbase.Get, startTs uint64, ignoreLock bool) (*hbase.ResultRow, error) {
    56  	req := &ThemisGetRequest{
    57  		Get:        g.ToProto().(*proto.Get),
    58  		StartTs:    pb.Uint64(startTs),
    59  		IgnoreLock: pb.Bool(ignoreLock),
    60  	}
    61  	var resp proto.Result
    62  	err := rpc.call("themisGet", tbl, g.Row, req, &resp)
    63  	if err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  	return hbase.NewResultRow(&resp), nil
    67  }
    68  
    69  func (rpc *themisRPC) themisBatchGet(tbl []byte, gets []*hbase.Get, startTs uint64, ignoreLock bool) ([]*hbase.ResultRow, error) {
    70  	var protoGets []*proto.Get
    71  	for _, g := range gets {
    72  		protoGets = append(protoGets, g.ToProto().(*proto.Get))
    73  	}
    74  	req := &ThemisBatchGetRequest{
    75  		Gets:       protoGets,
    76  		StartTs:    pb.Uint64(startTs),
    77  		IgnoreLock: pb.Bool(ignoreLock),
    78  	}
    79  	var resp ThemisBatchGetResponse
    80  	err := rpc.call("themisBatchGet", tbl, gets[0].Row, req, &resp)
    81  	if err != nil {
    82  		return nil, errors.Trace(err)
    83  	}
    84  	var results []*hbase.ResultRow
    85  	for _, rs := range resp.GetRs() {
    86  		results = append(results, hbase.NewResultRow(rs))
    87  	}
    88  	return results, nil
    89  }
    90  
    91  func (rpc *themisRPC) prewriteRow(tbl []byte, row []byte, mutations []*columnMutation, prewriteTs uint64, primaryLockBytes []byte, secondaryLockBytes []byte, primaryOffset int) (Lock, error) {
    92  	var cells []*proto.Cell
    93  	request := &ThemisPrewriteRequest{
    94  		PrewriteTs:    pb.Uint64(prewriteTs),
    95  		PrimaryLock:   primaryLockBytes,
    96  		SecondaryLock: secondaryLockBytes,
    97  		PrimaryIndex:  pb.Int(primaryOffset),
    98  	}
    99  	request.ThemisPrewrite = &ThemisPrewrite{
   100  		Row: row,
   101  	}
   102  	if primaryLockBytes == nil {
   103  		request.PrimaryLock = []byte("")
   104  	}
   105  	if secondaryLockBytes == nil {
   106  		request.SecondaryLock = []byte("")
   107  	}
   108  	for _, m := range mutations {
   109  		cells = append(cells, m.toCell())
   110  	}
   111  	request.ThemisPrewrite.Mutations = cells
   112  
   113  	var res ThemisPrewriteResponse
   114  	err := rpc.call("prewriteRow", tbl, row, request, &res)
   115  	if err != nil {
   116  		return nil, errors.Trace(err)
   117  	}
   118  	b := res.ThemisPrewriteResult
   119  	if b == nil {
   120  		// if lock is empty, means we got the lock, otherwise some one else had
   121  		// locked this row, and the lock should return in rpc result
   122  		return nil, nil
   123  	}
   124  	// Oops, someone else have already locked this row.
   125  
   126  	commitTs := b.GetNewerWriteTs()
   127  	if commitTs != 0 {
   128  		log.Errorf("write conflict, encounter write with larger timestamp than prewriteTs=%d, commitTs=%d, row=%s", prewriteTs, commitTs, string(row))
   129  		return nil, ErrRetryable
   130  	}
   131  
   132  	l, err := parseLockFromBytes(b.ExistLock)
   133  	if err != nil {
   134  		return nil, errors.Trace(err)
   135  	}
   136  
   137  	col := &hbase.ColumnCoordinate{
   138  		Table: tbl,
   139  		Row:   row,
   140  		Column: hbase.Column{
   141  			Family: b.Family,
   142  			Qual:   b.Qualifier,
   143  		},
   144  	}
   145  	l.SetCoordinate(col)
   146  	return l, nil
   147  }
   148  
   149  func (rpc *themisRPC) isLockExpired(tbl, row []byte, ts uint64) (bool, error) {
   150  	req := &LockExpiredRequest{
   151  		Timestamp: pb.Uint64(ts),
   152  	}
   153  	var res LockExpiredResponse
   154  	if row == nil {
   155  		debug.PrintStack()
   156  	}
   157  	err := rpc.call("isLockExpired", tbl, row, req, &res)
   158  	if err != nil {
   159  		return false, errors.Trace(err)
   160  	}
   161  	return res.GetExpired(), nil
   162  }
   163  
   164  func (rpc *themisRPC) getLockAndErase(cc *hbase.ColumnCoordinate, prewriteTs uint64) (Lock, error) {
   165  	req := &EraseLockRequest{
   166  		Row:        cc.Row,
   167  		Family:     cc.Column.Family,
   168  		Qualifier:  cc.Column.Qual,
   169  		PrewriteTs: pb.Uint64(prewriteTs),
   170  	}
   171  	var res EraseLockResponse
   172  	err := rpc.call("getLockAndErase", cc.Table, cc.Row, req, &res)
   173  	if err != nil {
   174  		return nil, errors.Trace(err)
   175  	}
   176  	b := res.GetLock()
   177  	if len(b) == 0 {
   178  		return nil, nil
   179  	}
   180  	return parseLockFromBytes(b)
   181  }
   182  
   183  func (rpc *themisRPC) commitRow(tbl, row []byte, mutations []*columnMutation,
   184  	prewriteTs, commitTs uint64, primaryOffset int) error {
   185  	req := &ThemisCommitRequest{}
   186  	req.ThemisCommit = &ThemisCommit{
   187  		Row:          row,
   188  		PrewriteTs:   pb.Uint64(prewriteTs),
   189  		CommitTs:     pb.Uint64(commitTs),
   190  		PrimaryIndex: pb.Int(primaryOffset),
   191  	}
   192  
   193  	for _, m := range mutations {
   194  		req.ThemisCommit.Mutations = append(req.ThemisCommit.Mutations, m.toCell())
   195  	}
   196  	var res ThemisCommitResponse
   197  	err := rpc.call("commitRow", tbl, row, req, &res)
   198  	if err != nil {
   199  		return errors.Trace(err)
   200  	}
   201  	ok := res.GetResult()
   202  	if !ok {
   203  		if primaryOffset == -1 {
   204  			return errors.Errorf("commit secondary failed, tbl: %s row: %q ts: %d", tbl, row, commitTs)
   205  		}
   206  		return errors.Errorf("commit primary failed, tbl: %s row: %q ts: %d", tbl, row, commitTs)
   207  	}
   208  	return nil
   209  }
   210  
   211  func (rpc *themisRPC) batchCommitSecondaryRows(tbl []byte, rowMs map[string]*rowMutation, prewriteTs, commitTs uint64) error {
   212  	req := &ThemisBatchCommitSecondaryRequest{}
   213  
   214  	i := 0
   215  	var lastRow []byte
   216  	req.ThemisCommit = make([]*ThemisCommit, len(rowMs))
   217  	for row, rowM := range rowMs {
   218  		var cells []*proto.Cell
   219  		for col, m := range rowM.mutations {
   220  			cells = append(cells, toCellFromRowM(col, m))
   221  		}
   222  
   223  		req.ThemisCommit[i] = &ThemisCommit{
   224  			Row:          []byte(row),
   225  			Mutations:    cells,
   226  			PrewriteTs:   pb.Uint64(prewriteTs),
   227  			CommitTs:     pb.Uint64(commitTs),
   228  			PrimaryIndex: pb.Int(-1),
   229  		}
   230  		i++
   231  		lastRow = []byte(row)
   232  	}
   233  
   234  	var res ThemisBatchCommitSecondaryResponse
   235  	err := rpc.call("batchCommitSecondaryRows", tbl, lastRow, req, &res)
   236  	if err != nil {
   237  		return errors.Trace(err)
   238  	}
   239  	log.Info("call batch commit secondary rows", len(req.ThemisCommit))
   240  
   241  	cResult := res.BatchCommitSecondaryResult
   242  	if cResult != nil && len(cResult) > 0 {
   243  		errorInfo := "commit failed, tbl:" + string(tbl)
   244  		for _, r := range cResult {
   245  			errorInfo += (" row:" + string(r.Row))
   246  		}
   247  		return errors.New(fmt.Sprintf("%s, commitTs:%d", errorInfo, commitTs))
   248  	}
   249  	return nil
   250  }
   251  
   252  func (rpc *themisRPC) commitSecondaryRow(tbl, row []byte, mutations []*columnMutation,
   253  	prewriteTs, commitTs uint64) error {
   254  	return rpc.commitRow(tbl, row, mutations, prewriteTs, commitTs, -1)
   255  }
   256  
   257  func (rpc *themisRPC) prewriteSecondaryRow(tbl, row []byte,
   258  	mutations []*columnMutation, prewriteTs uint64,
   259  	secondaryLockBytes []byte) (Lock, error) {
   260  	return rpc.prewriteRow(tbl, row, mutations, prewriteTs, nil, secondaryLockBytes, -1)
   261  }
   262  
   263  func (rpc *themisRPC) batchPrewriteSecondaryRows(tbl []byte, rowMs map[string]*rowMutation, prewriteTs uint64, secondaryLockBytes []byte) (map[string]Lock, error) {
   264  	request := &ThemisBatchPrewriteSecondaryRequest{
   265  		PrewriteTs:    pb.Uint64(prewriteTs),
   266  		SecondaryLock: secondaryLockBytes,
   267  	}
   268  	request.ThemisPrewrite = make([]*ThemisPrewrite, len(rowMs))
   269  
   270  	if secondaryLockBytes == nil {
   271  		secondaryLockBytes = []byte("")
   272  	}
   273  	i := 0
   274  	var lastRow []byte
   275  	for row, rowM := range rowMs {
   276  		var cells []*proto.Cell
   277  		for col, m := range rowM.mutations {
   278  			cells = append(cells, toCellFromRowM(col, m))
   279  		}
   280  		request.ThemisPrewrite[i] = &ThemisPrewrite{
   281  			Row:       []byte(row),
   282  			Mutations: cells,
   283  		}
   284  		i++
   285  		lastRow = []byte(row)
   286  	}
   287  
   288  	var res ThemisBatchPrewriteSecondaryResponse
   289  	err := rpc.call("batchPrewriteSecondaryRows", tbl, lastRow, request, &res)
   290  	if err != nil {
   291  		return nil, errors.Trace(err)
   292  	}
   293  
   294  	//Perhaps, part row has not in a region, sample : when region split, then need try
   295  	lockMap := make(map[string]Lock)
   296  	if res.RowsNotInRegion != nil && len(res.RowsNotInRegion) > 0 {
   297  		for _, r := range res.RowsNotInRegion {
   298  			tl, err := rpc.prewriteSecondaryRow(tbl, r, rowMs[string(r)].mutationList(true), prewriteTs, secondaryLockBytes)
   299  			if err != nil {
   300  				return nil, errors.Trace(err)
   301  			}
   302  
   303  			if tl != nil {
   304  				lockMap[string(r)] = tl
   305  			}
   306  		}
   307  	}
   308  
   309  	b := res.ThemisPrewriteResult
   310  	if b != nil && len(b) > 0 {
   311  		for _, pResult := range b {
   312  			lock, err := judgePerwriteResultRow(pResult, tbl, prewriteTs, pResult.Row)
   313  			if err != nil {
   314  				return nil, errors.Trace(err)
   315  			}
   316  
   317  			if lock != nil {
   318  				lockMap[string(pResult.Row)] = lock
   319  			}
   320  		}
   321  	}
   322  
   323  	return lockMap, nil
   324  }
   325  
   326  func judgePerwriteResultRow(pResult *ThemisPrewriteResult, tbl []byte, prewriteTs uint64, row []byte) (Lock, error) {
   327  	// Oops, someone else have already locked this row.
   328  	newerTs := pResult.GetNewerWriteTs()
   329  	if newerTs != 0 {
   330  		return nil, ErrRetryable
   331  	}
   332  
   333  	l, err := parseLockFromBytes(pResult.ExistLock)
   334  	if err != nil {
   335  		return nil, errors.Trace(err)
   336  	}
   337  	col := &hbase.ColumnCoordinate{
   338  		Table: tbl,
   339  		Row:   row,
   340  		Column: hbase.Column{
   341  			Family: pResult.Family,
   342  			Qual:   pResult.Qualifier,
   343  		},
   344  	}
   345  	l.SetCoordinate(col)
   346  	return l, nil
   347  }
   348  
   349  func toCellFromRowM(col string, cvPair *mutationValuePair) *proto.Cell {
   350  	c := &hbase.Column{}
   351  	// TODO: handle error, now just log
   352  	if err := c.ParseFromString(col); err != nil {
   353  		log.Warnf("parse from string error, column: %s, col: %s, error: %v", c, col, err)
   354  	}
   355  	ret := &proto.Cell{
   356  		Family:    c.Family,
   357  		Qualifier: c.Qual,
   358  		Value:     cvPair.value,
   359  	}
   360  	if cvPair.typ == hbase.TypePut { // put
   361  		ret.CellType = proto.CellType_PUT.Enum()
   362  	} else if cvPair.typ == hbase.TypeMinimum { // onlyLock
   363  		ret.CellType = proto.CellType_MINIMUM.Enum()
   364  	} else { // delete, themis delete API only support delete column
   365  		ret.CellType = proto.CellType_DELETE_COLUMN.Enum()
   366  	}
   367  	return ret
   368  }