Commit db64ffcb authored by liuyuhang's avatar liuyuhang

add kvmvccMavl function

parent ed0f98f0
...@@ -147,6 +147,13 @@ enableMVCC=false ...@@ -147,6 +147,13 @@ enableMVCC=false
enableMavlPrune=false enableMavlPrune=false
pruneHeight=10000 pruneHeight=10000
[store.sub.kvmvccMavl]
enableMVCCIter=false
enableMavlPrefix=false
enableMVCC=false
enableMavlPrune=false
pruneHeight=10000
[wallet] [wallet]
minFee=100000 minFee=100000
driver="leveldb" driver="leveldb"
......
...@@ -4,4 +4,5 @@ import ( ...@@ -4,4 +4,5 @@ import (
_ "github.com/33cn/plugin/plugin/store/kvdb" //auto gen _ "github.com/33cn/plugin/plugin/store/kvdb" //auto gen
_ "github.com/33cn/plugin/plugin/store/kvmvcc" //auto gen _ "github.com/33cn/plugin/plugin/store/kvmvcc" //auto gen
_ "github.com/33cn/plugin/plugin/store/mpt" //auto gen _ "github.com/33cn/plugin/plugin/store/mpt" //auto gen
_ "github.com/33cn/plugin/plugin/store/kvmvccMavl" //auto gen
) )
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package kvmvccMavl
import (
clog "github.com/33cn/chain33/common/log"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/queue"
drivers "github.com/33cn/chain33/system/store"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/system/store/mavl"
"github.com/33cn/plugin/plugin/store/kvmvcc"
"github.com/hashicorp/golang-lru"
"encoding/json"
"errors"
)
var (
kmlog = log.New("module", "kvmvccMavl")
ErrStateHashLost = errors.New("ErrStateHashLost")
kvmvccMavlFork int64 = 200 * 10000
)
// SetLogLevel set log level
func SetLogLevel(level string) {
clog.SetLogLevel(level)
}
// DisableLog disable log output
func DisableLog() {
kmlog.SetHandler(log.DiscardHandler())
}
func init() {
drivers.Reg("kvmvccMavl", New)
}
// KVMVCCMavlStore provide kvmvcc and mavl store interface implementation
type KVMVCCMavlStore struct {
*drivers.BaseStore
*kvmvccdb.KVMVCCStore
*mavl.Store
cance *lru.Cache
}
type subKVMVCCConfig struct {
EnableMVCCIter bool `json:"enableMVCCIter"`
}
type subMavlConfig struct {
EnableMavlPrefix bool `json:"enableMavlPrefix"`
EnableMVCC bool `json:"enableMVCC"`
EnableMavlPrune bool `json:"enableMavlPrune"`
PruneHeight int32 `json:"pruneHeight"`
}
type subConfig struct {
EnableMVCCIter bool `json:"enableMVCCIter"`
EnableMavlPrefix bool `json:"enableMavlPrefix"`
EnableMVCC bool `json:"enableMVCC"`
EnableMavlPrune bool `json:"enableMavlPrune"`
PruneHeight int32 `json:"pruneHeight"`
}
// New construct KVMVCCStore module
func New(cfg *types.Store, sub []byte) queue.Module {
bs := drivers.NewBaseStore(cfg)
var kvms *KVMVCCMavlStore
var subcfg subConfig
var subKVMVCCcfg subKVMVCCConfig
var subMavlcfg subMavlConfig
if sub != nil {
types.MustDecode(sub, &subcfg)
subKVMVCCcfg.EnableMVCCIter = subcfg.EnableMVCCIter
subMavlcfg.EnableMavlPrefix = subcfg.EnableMavlPrefix
subMavlcfg.EnableMVCC = subcfg.EnableMVCC
subMavlcfg.EnableMavlPrune = subcfg.EnableMavlPrune
subMavlcfg.PruneHeight = subcfg.PruneHeight
}
mvcVal, _ := json.Marshal(&subKVMVCCcfg)
mavlVal, _ := json.Marshal(&subMavlcfg)
cance, err := lru.New(1024)
if err != nil {
panic("new KVMVCCMavlStore fail")
}
mvccCfg := &types.Store{}
mvccCfg.Name = "kvmvcc"
mvccCfg.Driver = cfg.Driver
mvccCfg.DbPath = cfg.DbPath + "/kvmvcc"
mvccCfg.DbCache = cfg.DbCache
mvccCfg.LocalDBVersion = cfg.LocalDBVersion
mavlCfg := &types.Store{}
mavlCfg.Name = "mavl"
mavlCfg.Driver = cfg.Driver
mavlCfg.DbPath = cfg.DbPath + "/mavl"
mavlCfg.DbCache = cfg.DbCache
mavlCfg.LocalDBVersion = cfg.LocalDBVersion
kvms = &KVMVCCMavlStore{bs, kvmvccdb.New(mvccCfg, mvcVal).(*kvmvccdb.KVMVCCStore),
mavl.New(mavlCfg, mavlVal).(*mavl.Store), cance}
bs.SetChild(kvms)
return kvms
}
// Close the KVMVCCStore module
func (kvmMavls *KVMVCCMavlStore) Close() {
kvmMavls.BaseStore.Close()
kvmMavls.KVMVCCStore.Close()
kvmMavls.Store.Close()
kmlog.Info("store kvdb closed")
}
// Set kvs with statehash to KVMVCCStore
func (kvmMavls *KVMVCCMavlStore) Set(datas *types.StoreSet, sync bool) ([]byte, error) {
// 这里后续需要考虑分叉回退
if datas.Height < kvmvccMavlFork {
hash, err := kvmMavls.Store.Set(datas, sync)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.Set(datas, sync)
if err != nil {
return hash, err
}
if err == nil {
kvmMavls.cance.Add(string(hash), datas.Height)
}
return hash, err
}
// 仅仅做kvmvcc
hash, err := kvmMavls.KVMVCCStore.Set(datas, sync)
if err == nil {
kvmMavls.cance.Add(string(hash), datas.Height)
}
return hash, err
}
// Get kvs with statehash from KVMVCCStore
func (kvmMavls *KVMVCCMavlStore) Get(datas *types.StoreGet) [][]byte {
if value, ok := kvmMavls.cance.Get(string(datas.StateHash)); ok {
if value.(int64) < kvmvccMavlFork {
return kvmMavls.Store.Get(datas)
}
return kvmMavls.KVMVCCStore.Get(datas)
}
return kvmMavls.KVMVCCStore.Get(datas)
}
// MemSet set kvs to the mem of KVMVCCStore module and return the StateHash
func (kvmMavls *KVMVCCMavlStore) MemSet(datas *types.StoreSet, sync bool) ([]byte, error) {
// 这里后续需要考虑分叉回退
if datas.Height < kvmvccMavlFork {
hash, err := kvmMavls.Store.MemSet(datas, sync)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.MemSet(datas, sync)
if err != nil {
return hash, err
}
if err == nil {
kvmMavls.cance.Add(string(hash), datas.Height)
}
return hash, err
}
// 仅仅做kvmvcc
hash, err := kvmMavls.KVMVCCStore.MemSet(datas, sync)
if err == nil {
kvmMavls.cance.Add(string(hash), datas.Height)
}
return hash, err
}
// Commit kvs in the mem of KVMVCCStore module to state db and return the StateHash
func (kvmMavls *KVMVCCMavlStore) Commit(req *types.ReqHash) ([]byte, error) {
if value, ok := kvmMavls.cance.Get(string(req.Hash)); ok {
if value.(int64) < kvmvccMavlFork {
hash, err := kvmMavls.Store.Commit(req)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.Commit(req)
if err != nil {
return hash, err
}
return hash, err
}
return kvmMavls.KVMVCCStore.Commit(req)
}
return kvmMavls.KVMVCCStore.Commit(req)
}
// Rollback kvs in the mem of KVMVCCStore module and return the StateHash
func (kvmMavls *KVMVCCMavlStore) Rollback(req *types.ReqHash) ([]byte, error) {
if value, ok := kvmMavls.cance.Get(string(req.Hash)); ok {
if value.(int64) < kvmvccMavlFork {
hash, err := kvmMavls.Store.Rollback(req)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.Rollback(req)
if err != nil {
return hash, err
}
return hash, err
}
return kvmMavls.KVMVCCStore.Rollback(req)
}
return kvmMavls.KVMVCCStore.Rollback(req)
}
// IterateRangeByStateHash travel with Prefix by StateHash to get the latest version kvs.
func (kvmMavls *KVMVCCMavlStore) IterateRangeByStateHash(statehash []byte, start []byte, end []byte, ascending bool, fn func(key, value []byte) bool) {
if value, ok := kvmMavls.cance.Get(string(statehash)); ok {
if value.(int64) < kvmvccMavlFork {
kvmMavls.Store.IterateRangeByStateHash(statehash, start, end, ascending, fn)
}
kvmMavls.KVMVCCStore.IterateRangeByStateHash(statehash, start, end, ascending, fn)
}
kvmMavls.KVMVCCStore.IterateRangeByStateHash(statehash, start, end, ascending, fn)
}
// ProcEvent handles supported events
func (kvmMavls *KVMVCCMavlStore) ProcEvent(msg queue.Message) {
msg.ReplyErr("KVMVCCMavlStore", types.ErrActionNotSupport)
}
// Del set kvs to nil with StateHash
func (kvmMavls *KVMVCCMavlStore) Del(req *types.StoreDel) ([]byte, error) {
// 这里后续需要考虑分叉回退
if req.Height < kvmvccMavlFork {
hash, err := kvmMavls.Store.Del(req)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.Del(req)
if err != nil {
return hash, err
}
if err == nil {
kvmMavls.cance.Remove(string(req.StateHash))
}
return hash, err
}
// 仅仅做kvmvcc
hash, err := kvmMavls.KVMVCCStore.Del(req)
if err == nil {
kvmMavls.cance.Remove(string(req.StateHash))
}
return hash, err
}
\ No newline at end of file
This diff is collapsed.
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package kvmvccMavl
import (
"github.com/33cn/chain33/common"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/queue"
drivers "github.com/33cn/chain33/system/store"
"github.com/33cn/chain33/types"
"github.com/golang/protobuf/proto"
)
var maxRollbackNum = 200
// KVMVCCStore provide kvmvcc store interface implementation
type KVMVCCStore struct {
db dbm.DB
mvcc dbm.MVCC
kvsetmap map[string][]*types.KeyValue
enableMVCCIter bool
}
// NewKVMVCC construct KVMVCCStore module
func NewKVMVCC(cfg *types.Store, sub *subKVMVCCConfig, db dbm.DB) *KVMVCCStore {
bs := drivers.NewBaseStore(cfg)
var kvs *KVMVCCStore
enable := false
if sub != nil {
enable = sub.EnableMVCCIter
}
if enable {
kvs = &KVMVCCStore{db, dbm.NewMVCCIter(bs.GetDB()), make(map[string][]*types.KeyValue), true}
} else {
kvs = &KVMVCCStore{db, dbm.NewMVCC(bs.GetDB()), make(map[string][]*types.KeyValue), false}
}
return kvs
}
// Close the KVMVCCStore module
func (mvccs *KVMVCCStore) Close() {
kmlog.Info("store kvdb closed")
}
// Set kvs with statehash to KVMVCCStore
func (mvccs *KVMVCCStore) Set(datas *types.StoreSet, sync bool) ([]byte, error) {
hash := calcHash(datas)
kvlist, err := mvccs.mvcc.AddMVCC(datas.KV, hash, datas.StateHash, datas.Height)
if err != nil {
return nil, err
}
mvccs.saveKVSets(kvlist)
return hash, nil
}
// Get kvs with statehash from KVMVCCStore
func (mvccs *KVMVCCStore) Get(datas *types.StoreGet) [][]byte {
values := make([][]byte, len(datas.Keys))
version, err := mvccs.mvcc.GetVersion(datas.StateHash)
if err != nil {
kmlog.Error("Get version by hash failed.", "hash", common.ToHex(datas.StateHash))
return values
}
for i := 0; i < len(datas.Keys); i++ {
value, err := mvccs.mvcc.GetV(datas.Keys[i], version)
if err != nil {
kmlog.Error("GetV by Keys failed.", "Key", string(datas.Keys[i]), "version", version)
} else if value != nil {
values[i] = value
}
}
return values
}
// MemSet set kvs to the mem of KVMVCCStore module and return the StateHash
func (mvccs *KVMVCCStore) MemSet(datas *types.StoreSet, sync bool) ([]byte, error) {
kvset, err := mvccs.checkVersion(datas.Height)
if err != nil {
return nil, err
}
hash := calcHash(datas)
//kmlog.Debug("KVMVCCStore MemSet AddMVCC", "prestatehash", common.ToHex(datas.StateHash), "hash", common.ToHex(hash), "height", datas.Height)
kvlist, err := mvccs.mvcc.AddMVCC(datas.KV, hash, datas.StateHash, datas.Height)
if err != nil {
return nil, err
}
if len(kvlist) > 0 {
kvset = append(kvset, kvlist...)
}
mvccs.kvsetmap[string(hash)] = kvset
return hash, nil
}
// Commit kvs in the mem of KVMVCCStore module to state db and return the StateHash
func (mvccs *KVMVCCStore) Commit(req *types.ReqHash) ([]byte, error) {
_, ok := mvccs.kvsetmap[string(req.Hash)]
if !ok {
kmlog.Error("store kvmvcc commit", "err", types.ErrHashNotFound)
return nil, types.ErrHashNotFound
}
//kmlog.Debug("KVMVCCStore Commit saveKVSets", "hash", common.ToHex(req.Hash))
mvccs.saveKVSets(mvccs.kvsetmap[string(req.Hash)])
delete(mvccs.kvsetmap, string(req.Hash))
return req.Hash, nil
}
// Rollback kvs in the mem of KVMVCCStore module and return the StateHash
func (mvccs *KVMVCCStore) Rollback(req *types.ReqHash) ([]byte, error) {
_, ok := mvccs.kvsetmap[string(req.Hash)]
if !ok {
kmlog.Error("store kvmvcc rollback", "err", types.ErrHashNotFound)
return nil, types.ErrHashNotFound
}
//kmlog.Debug("KVMVCCStore Rollback", "hash", common.ToHex(req.Hash))
delete(mvccs.kvsetmap, string(req.Hash))
return req.Hash, nil
}
// IterateRangeByStateHash travel with Prefix by StateHash to get the latest version kvs.
func (mvccs *KVMVCCStore) IterateRangeByStateHash(statehash []byte, start []byte, end []byte, ascending bool, fn func(key, value []byte) bool) {
if !mvccs.enableMVCCIter {
panic("call IterateRangeByStateHash when disable mvcc iter")
}
//按照kv最新值来进行遍历处理,要求statehash必须是最新区块的statehash,否则不支持该接口
maxVersion, err := mvccs.mvcc.GetMaxVersion()
if err != nil {
kmlog.Error("KVMVCCStore IterateRangeByStateHash can't get max version, ignore the call.", "err", err)
return
}
version, err := mvccs.mvcc.GetVersion(statehash)
if err != nil {
kmlog.Error("KVMVCCStore IterateRangeByStateHash can't get version, ignore the call.", "stateHash", common.ToHex(statehash), "err", err)
return
}
if version != maxVersion {
kmlog.Error("KVMVCCStore IterateRangeByStateHash call failed for maxVersion does not match version.", "maxVersion", maxVersion, "version", version, "stateHash", common.ToHex(statehash))
return
}
//kmlog.Info("KVMVCCStore do the IterateRangeByStateHash")
listhelper := dbm.NewListHelper(mvccs.mvcc.(*dbm.MVCCIter))
listhelper.IteratorCallback(start, end, 0, 1, fn)
}
// ProcEvent handles supported events
func (mvccs *KVMVCCStore) ProcEvent(msg queue.Message) {
msg.ReplyErr("KVStore", types.ErrActionNotSupport)
}
// Del set kvs to nil with StateHash
func (mvccs *KVMVCCStore) Del(req *types.StoreDel) ([]byte, error) {
kvset, err := mvccs.mvcc.DelMVCC(req.StateHash, req.Height, true)
if err != nil {
kmlog.Error("store kvmvcc del", "err", err)
return nil, err
}
kmlog.Info("KVMVCCStore Del", "hash", common.ToHex(req.StateHash), "height", req.Height)
mvccs.saveKVSets(kvset)
return req.StateHash, nil
}
func (mvccs *KVMVCCStore) saveKVSets(kvset []*types.KeyValue) {
if len(kvset) == 0 {
return
}
storeBatch := mvccs.db.NewBatch(true)
for i := 0; i < len(kvset); i++ {
if kvset[i].Value == nil {
storeBatch.Delete(kvset[i].Key)
} else {
storeBatch.Set(kvset[i].Key, kvset[i].Value)
}
}
storeBatch.Write()
}
func (mvccs *KVMVCCStore) checkVersion(height int64) ([]*types.KeyValue, error) {
//检查新加入区块的height和现有的version的关系,来判断是否要回滚数据
maxVersion, err := mvccs.mvcc.GetMaxVersion()
if err != nil {
if err != types.ErrNotFound {
kmlog.Error("store kvmvcc checkVersion GetMaxVersion failed", "err", err)
panic(err)
} else {
maxVersion = -1
}
}
//kmlog.Debug("store kvmvcc checkVersion ", "maxVersion", maxVersion, "currentVersion", height)
var kvset []*types.KeyValue
if maxVersion < height-1 {
kmlog.Error("store kvmvcc checkVersion found statehash lost", "maxVersion", maxVersion, "height", height)
return nil, ErrStateHashLost
} else if maxVersion == height-1 {
return nil, nil
} else {
count := 1
for i := maxVersion; i >= height; i-- {
hash, err := mvccs.mvcc.GetVersionHash(i)
if err != nil {
kmlog.Warn("store kvmvcc checkVersion GetVersionHash failed", "height", i, "maxVersion", maxVersion)
continue
}
kvlist, err := mvccs.mvcc.DelMVCC(hash, i, false)
if err != nil {
kmlog.Warn("store kvmvcc checkVersion DelMVCC failed", "height", i, "err", err)
continue
}
kvset = append(kvset, kvlist...)
kmlog.Debug("store kvmvcc checkVersion DelMVCC4Height", "height", i, "maxVersion", maxVersion)
//为避免高度差过大时出现异常,做一个保护,一次最多回滚200个区块
count++
if count >= maxRollbackNum {
break
}
}
}
return kvset, nil
}
func calcHash(datas proto.Message) []byte {
b := types.Encode(datas)
return common.Sha256(b)
}
This diff is collapsed.
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package mavl 默克尔平衡树接口
package kvmvccMavl
import (
"sync"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/system/store/mavl/db"
"github.com/33cn/chain33/types"
dbm "github.com/33cn/chain33/common/db"
)
// Store mavl store struct
type MavlStore struct {
db dbm.DB
trees *sync.Map
enableMavlPrefix bool
enableMVCC bool
enableMavlPrune bool
pruneHeight int32
}
// NewMavl new mavl store module
func NewMavl(cfg *types.Store, sub *subMavlConfig, db dbm.DB) *MavlStore {
var subcfg subMavlConfig
if sub != nil {
subcfg.EnableMavlPrefix = sub.EnableMavlPrefix
subcfg.EnableMVCC = sub.EnableMVCC
subcfg.EnableMavlPrune = sub.EnableMavlPrune
subcfg.PruneHeight = sub.PruneHeight
}
mavls := &MavlStore{db, &sync.Map{}, subcfg.EnableMavlPrefix, subcfg.EnableMVCC, subcfg.EnableMavlPrune, subcfg.PruneHeight}
mavl.EnableMavlPrefix(subcfg.EnableMavlPrefix)
mavl.EnableMVCC(subcfg.EnableMVCC)
mavl.EnablePrune(subcfg.EnableMavlPrune)
mavl.SetPruneHeight(int(subcfg.PruneHeight))
return mavls
}
// Close close mavl store
func (mavls *MavlStore) Close() {
mavl.ClosePrune()
kmlog.Info("store mavl closed")
}
// Set set k v to mavl store db; sync is true represent write sync
func (mavls *MavlStore) Set(datas *types.StoreSet, sync bool) ([]byte, error) {
return mavl.SetKVPair(mavls.db, datas, sync)
}
// Get get values by keys
func (mavls *MavlStore) Get(datas *types.StoreGet) [][]byte {
var tree *mavl.Tree
var err error
values := make([][]byte, len(datas.Keys))
search := string(datas.StateHash)
if data, ok := mavls.trees.Load(search); ok {
tree = data.(*mavl.Tree)
} else {
tree = mavl.NewTree(mavls.db, true)
//get接口也应该传入高度
//tree.SetBlockHeight(datas.Height)
err = tree.Load(datas.StateHash)
kmlog.Debug("store mavl get tree", "err", err, "StateHash", common.ToHex(datas.StateHash))
}
if err == nil {
for i := 0; i < len(datas.Keys); i++ {
_, value, exit := tree.Get(datas.Keys[i])
if exit {
values[i] = value
}
}
}
return values
}
// MemSet set keys values to memcory mavl, return root hash and error
func (mavls *MavlStore) MemSet(datas *types.StoreSet, sync bool) ([]byte, error) {
beg := types.Now()
defer func() {
kmlog.Info("MemSet", "cost", types.Since(beg))
}()
if len(datas.KV) == 0 {
kmlog.Info("store mavl memset,use preStateHash as stateHash for kvset is null")
mavls.trees.Store(string(datas.StateHash), nil)
return datas.StateHash, nil
}
tree := mavl.NewTree(mavls.db, sync)
tree.SetBlockHeight(datas.Height)
err := tree.Load(datas.StateHash)
if err != nil {
return nil, err
}
for i := 0; i < len(datas.KV); i++ {
tree.Set(datas.KV[i].Key, datas.KV[i].Value)
}
hash := tree.Hash()
mavls.trees.Store(string(hash), tree)
return hash, nil
}
// Commit convert memcory mavl to storage db
func (mavls *MavlStore) Commit(req *types.ReqHash) ([]byte, error) {
beg := types.Now()
defer func() {
kmlog.Info("Commit", "cost", types.Since(beg))
}()
tree, ok := mavls.trees.Load(string(req.Hash))
if !ok {
kmlog.Error("store mavl commit", "err", types.ErrHashNotFound)
return nil, types.ErrHashNotFound
}
if tree == nil {
kmlog.Info("store mavl commit,do nothing for kvset is null")
mavls.trees.Delete(string(req.Hash))
return req.Hash, nil
}
hash := tree.(*mavl.Tree).Save()
if hash == nil {
kmlog.Error("store mavl commit", "err", types.ErrHashNotFound)
return nil, types.ErrDataBaseDamage
}
mavls.trees.Delete(string(req.Hash))
return req.Hash, nil
}
// Rollback 回退将缓存的mavl树删除掉
func (mavls *MavlStore) Rollback(req *types.ReqHash) ([]byte, error) {
beg := types.Now()
defer func() {
kmlog.Info("Rollback", "cost", types.Since(beg))
}()
_, ok := mavls.trees.Load(string(req.Hash))
if !ok {
kmlog.Error("store mavl rollback", "err", types.ErrHashNotFound)
return nil, types.ErrHashNotFound
}
mavls.trees.Delete(string(req.Hash))
return req.Hash, nil
}
// IterateRangeByStateHash 迭代实现功能; statehash:当前状态hash, start:开始查找的key, end: 结束的key, ascending:升序,降序, fn 迭代回调函数
func (mavls *MavlStore) IterateRangeByStateHash(statehash []byte, start []byte, end []byte, ascending bool, fn func(key, value []byte) bool) {
mavl.IterateRangeByStateHash(mavls.db, statehash, start, end, ascending, fn)
}
// ProcEvent not support message
func (mavls *MavlStore) ProcEvent(msg queue.Message) {
msg.ReplyErr("Store", types.ErrActionNotSupport)
}
// Del ...
func (mavls *MavlStore) Del(req *types.StoreDel) ([]byte, error) {
//not support
return nil, nil
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment