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 }