forked from Shiloh/githaven
234 lines
5.2 KiB
Go
234 lines
5.2 KiB
Go
|
package themis
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sort"
|
||
|
|
||
|
"github.com/juju/errors"
|
||
|
"github.com/ngaut/log"
|
||
|
"github.com/pingcap/go-hbase"
|
||
|
"github.com/pingcap/go-hbase/proto"
|
||
|
)
|
||
|
|
||
|
type mutationValuePair struct {
|
||
|
typ hbase.Type
|
||
|
value []byte
|
||
|
}
|
||
|
|
||
|
func (mp *mutationValuePair) String() string {
|
||
|
return fmt.Sprintf("type: %d value: %s", mp.typ, mp.value)
|
||
|
}
|
||
|
|
||
|
type columnMutation struct {
|
||
|
*hbase.Column
|
||
|
*mutationValuePair
|
||
|
}
|
||
|
|
||
|
func getEntriesFromDel(p *hbase.Delete) ([]*columnMutation, error) {
|
||
|
errMsg := "must set at least one column for themis delete"
|
||
|
if len(p.FamilyQuals) == 0 {
|
||
|
return nil, errors.New(errMsg)
|
||
|
}
|
||
|
|
||
|
var ret []*columnMutation
|
||
|
for f, _ := range p.Families {
|
||
|
quilifiers := p.FamilyQuals[f]
|
||
|
if len(quilifiers) == 0 {
|
||
|
return nil, errors.New(errMsg)
|
||
|
}
|
||
|
for q, _ := range quilifiers {
|
||
|
mutation := &columnMutation{
|
||
|
Column: &hbase.Column{
|
||
|
Family: []byte(f),
|
||
|
Qual: []byte(q),
|
||
|
},
|
||
|
mutationValuePair: &mutationValuePair{
|
||
|
typ: hbase.TypeDeleteColumn,
|
||
|
},
|
||
|
}
|
||
|
ret = append(ret, mutation)
|
||
|
}
|
||
|
}
|
||
|
return ret, nil
|
||
|
}
|
||
|
|
||
|
func getEntriesFromPut(p *hbase.Put) []*columnMutation {
|
||
|
var ret []*columnMutation
|
||
|
for i, f := range p.Families {
|
||
|
qualifiers := p.Qualifiers[i]
|
||
|
for j, q := range qualifiers {
|
||
|
mutation := &columnMutation{
|
||
|
Column: &hbase.Column{
|
||
|
Family: f,
|
||
|
Qual: q,
|
||
|
},
|
||
|
mutationValuePair: &mutationValuePair{
|
||
|
typ: hbase.TypePut,
|
||
|
value: p.Values[i][j],
|
||
|
},
|
||
|
}
|
||
|
ret = append(ret, mutation)
|
||
|
}
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
func (cm *columnMutation) toCell() *proto.Cell {
|
||
|
ret := &proto.Cell{
|
||
|
Family: cm.Family,
|
||
|
Qualifier: cm.Qual,
|
||
|
Value: cm.value,
|
||
|
}
|
||
|
if cm.typ == hbase.TypePut { // put
|
||
|
ret.CellType = proto.CellType_PUT.Enum()
|
||
|
} else if cm.typ == hbase.TypeMinimum { // onlyLock
|
||
|
ret.CellType = proto.CellType_MINIMUM.Enum()
|
||
|
} else { // delete, themis delete API only support delete column
|
||
|
ret.CellType = proto.CellType_DELETE_COLUMN.Enum()
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
type rowMutation struct {
|
||
|
tbl []byte
|
||
|
row []byte
|
||
|
// mutations := { 'cf:col' => mutationValuePair }
|
||
|
mutations map[string]*mutationValuePair
|
||
|
}
|
||
|
|
||
|
func (r *rowMutation) getColumns() []hbase.Column {
|
||
|
var ret []hbase.Column
|
||
|
for k, _ := range r.mutations {
|
||
|
c := &hbase.Column{}
|
||
|
// TODO: handle error, now just ignore
|
||
|
if err := c.ParseFromString(k); err != nil {
|
||
|
log.Warnf("parse from string error, column: %s, mutation: %s, error: %v", c, k, err)
|
||
|
}
|
||
|
ret = append(ret, *c)
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
func (r *rowMutation) getSize() int {
|
||
|
return len(r.mutations)
|
||
|
}
|
||
|
|
||
|
func (r *rowMutation) getType(c hbase.Column) hbase.Type {
|
||
|
p, ok := r.mutations[c.String()]
|
||
|
if !ok {
|
||
|
return hbase.TypeMinimum
|
||
|
}
|
||
|
return p.typ
|
||
|
}
|
||
|
|
||
|
func newRowMutation(tbl, row []byte) *rowMutation {
|
||
|
return &rowMutation{
|
||
|
tbl: tbl,
|
||
|
row: row,
|
||
|
mutations: map[string]*mutationValuePair{},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *rowMutation) addMutation(c *hbase.Column, typ hbase.Type, val []byte, onlyLock bool) {
|
||
|
// 3 scene: put, delete, onlyLock
|
||
|
// if it is onlyLock scene, then has not data modify, when has contained the qualifier, can't replace exist value,
|
||
|
// becuase put or delete operation has add mutation
|
||
|
if onlyLock && r.mutations[c.String()] != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
r.mutations[c.String()] = &mutationValuePair{
|
||
|
typ: typ,
|
||
|
value: val,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *rowMutation) mutationList(withValue bool) []*columnMutation {
|
||
|
var ret []*columnMutation
|
||
|
var keys []string
|
||
|
for k, _ := range r.mutations {
|
||
|
keys = append(keys, k)
|
||
|
}
|
||
|
sort.Strings(keys)
|
||
|
for _, k := range keys {
|
||
|
v := &mutationValuePair{
|
||
|
typ: r.mutations[k].typ,
|
||
|
}
|
||
|
if withValue {
|
||
|
v.value = r.mutations[k].value
|
||
|
}
|
||
|
c := &hbase.Column{}
|
||
|
// TODO: handle error, now just ignore
|
||
|
if err := c.ParseFromString(k); err != nil {
|
||
|
log.Warnf("parse from string error, column: %s, mutation: %s, error: %v", c, k, err)
|
||
|
}
|
||
|
ret = append(ret, &columnMutation{
|
||
|
Column: c,
|
||
|
mutationValuePair: v,
|
||
|
})
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
type columnMutationCache struct {
|
||
|
// mutations => {table => { rowKey => row mutations } }
|
||
|
mutations map[string]map[string]*rowMutation
|
||
|
}
|
||
|
|
||
|
func newColumnMutationCache() *columnMutationCache {
|
||
|
return &columnMutationCache{
|
||
|
mutations: map[string]map[string]*rowMutation{},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *columnMutationCache) addMutation(tbl []byte, row []byte, col *hbase.Column, t hbase.Type, v []byte, onlyLock bool) {
|
||
|
tblRowMutations, ok := c.mutations[string(tbl)]
|
||
|
if !ok {
|
||
|
// create table mutation map
|
||
|
tblRowMutations = map[string]*rowMutation{}
|
||
|
c.mutations[string(tbl)] = tblRowMutations
|
||
|
}
|
||
|
|
||
|
rowMutations, ok := tblRowMutations[string(row)]
|
||
|
if !ok {
|
||
|
// create row mutation map
|
||
|
rowMutations = newRowMutation(tbl, row)
|
||
|
tblRowMutations[string(row)] = rowMutations
|
||
|
}
|
||
|
rowMutations.addMutation(col, t, v, onlyLock)
|
||
|
}
|
||
|
|
||
|
func (c *columnMutationCache) getMutation(cc *hbase.ColumnCoordinate) *mutationValuePair {
|
||
|
t, ok := c.mutations[string(cc.Table)]
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
rowMutation, ok := t[string(cc.Row)]
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
p, ok := rowMutation.mutations[cc.GetColumn().String()]
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
func (c *columnMutationCache) getRowCount() int {
|
||
|
ret := 0
|
||
|
for _, v := range c.mutations {
|
||
|
ret += len(v)
|
||
|
}
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
func (c *columnMutationCache) getMutationCount() int {
|
||
|
ret := 0
|
||
|
for _, v := range c.mutations {
|
||
|
for _, vv := range v {
|
||
|
ret += len(vv.mutationList(false))
|
||
|
}
|
||
|
}
|
||
|
return ret
|
||
|
}
|