github.com/igggame/nebulas-go@v2.1.0+incompatible/common/mvccdb/staging_table.go (about)

     1  // Copyright (C) 2018 go-nebulas authors
     2  //
     3  // This file is part of the go-nebulas library.
     4  //
     5  // the go-nebulas library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // the go-nebulas library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU General Public License
    16  // along with the go-nebulas library.  If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  
    19  package mvccdb
    20  
    21  import (
    22  	"bytes"
    23  	"errors"
    24  	"sync"
    25  
    26  	"github.com/nebulasio/go-nebulas/storage"
    27  	"github.com/nebulasio/go-nebulas/util/logging"
    28  	"github.com/sirupsen/logrus"
    29  
    30  	"github.com/nebulasio/go-nebulas/util/byteutils"
    31  )
    32  
    33  // Error
    34  var (
    35  	ErrStagingTableKeyConfliction = errors.New("staging table key confliction")
    36  	ErrParentStagingTableIsNil    = errors.New("parent Staging Table is nil")
    37  )
    38  
    39  type stagingValuesMap map[string]*VersionizedValueItem
    40  type stagingValuesMapMap map[interface{}]stagingValuesMap
    41  
    42  // VersionizedValueItem a struct for key/value pair, with version, dirty, deleted flags.
    43  type VersionizedValueItem struct {
    44  	tid           interface{}
    45  	key           []byte
    46  	val           []byte
    47  	version       int
    48  	deleted       bool
    49  	dirty         bool
    50  	globalVersion int64
    51  }
    52  
    53  // StagingTable a struct to store all staging changed key/value pairs.
    54  // There are two map to store the key/value pairs. One are stored associated with tid,
    55  // the other is `finalVersionizedValue`, record the `ready to commit` key/value pairs.
    56  type StagingTable struct {
    57  	storage                         storage.Storage
    58  	globalVersion                   int64
    59  	parentStagingTable              *StagingTable
    60  	versionizedValues               stagingValuesMap
    61  	tid                             interface{}
    62  	mutex                           sync.Mutex
    63  	prepareingGlobalVersion         int64
    64  	preparedStagingTables           map[interface{}]*StagingTable
    65  	isTrieSameKeyCompatibility      bool // The `isTrieSameKeyCompatibility` is used to prevent conflict in continuous changes with same key/value.
    66  	disableStrictGlobalVersionCheck bool // default `true`
    67  }
    68  
    69  // NewStagingTable return new instance of StagingTable.
    70  func NewStagingTable(storage storage.Storage, tid interface{}, trieSameKeyCompatibility bool) *StagingTable {
    71  	tbl := &StagingTable{
    72  		storage:            storage,
    73  		globalVersion:      0,
    74  		parentStagingTable: nil,
    75  		versionizedValues:  make(stagingValuesMap),
    76  		tid:                tid,
    77  		prepareingGlobalVersion:         0,
    78  		preparedStagingTables:           make(map[interface{}]*StagingTable),
    79  		isTrieSameKeyCompatibility:      trieSameKeyCompatibility,
    80  		disableStrictGlobalVersionCheck: true,
    81  	}
    82  	return tbl
    83  }
    84  
    85  // SetStrictGlobalVersionCheck set global version check
    86  func (tbl *StagingTable) SetStrictGlobalVersionCheck(flag bool) {
    87  	tbl.disableStrictGlobalVersionCheck = !flag
    88  }
    89  
    90  // Prepare a independent staging table
    91  func (tbl *StagingTable) Prepare(tid interface{}) (*StagingTable, error) {
    92  	tbl.mutex.Lock()
    93  	defer tbl.mutex.Unlock()
    94  
    95  	if tbl.preparedStagingTables[tid] != nil {
    96  		return nil, ErrTidIsExist
    97  	}
    98  
    99  	preparedTbl := &StagingTable{
   100  		storage:                 tbl.storage,
   101  		globalVersion:           0,
   102  		parentStagingTable:      tbl,
   103  		prepareingGlobalVersion: tbl.globalVersion,
   104  		versionizedValues:       make(stagingValuesMap),
   105  		tid:                     tid,
   106  		preparedStagingTables:           make(map[interface{}]*StagingTable),
   107  		isTrieSameKeyCompatibility:      tbl.isTrieSameKeyCompatibility,
   108  		disableStrictGlobalVersionCheck: tbl.disableStrictGlobalVersionCheck,
   109  	}
   110  
   111  	tbl.preparedStagingTables[tid] = preparedTbl
   112  	return preparedTbl, nil
   113  }
   114  
   115  // Get return value by key. If key does not exist, copy and incr version from `parentStagingTable` to record previous version.
   116  func (tbl *StagingTable) Get(key []byte) (*VersionizedValueItem, error) {
   117  	return tbl.GetByKey(key, true)
   118  }
   119  
   120  // GetByKey return value by key. If key does not exist, copy and incr version from `parentStagingTable` to record previous version.
   121  func (tbl *StagingTable) GetByKey(key []byte, loadFromStorage bool) (*VersionizedValueItem, error) {
   122  	// double check lock to prevent dead lock while call MergeToParent().
   123  	tbl.mutex.Lock()
   124  	keyStr := byteutils.Hex(key)
   125  	value := tbl.versionizedValues[keyStr]
   126  	tbl.mutex.Unlock()
   127  
   128  	if value == nil {
   129  		var err error
   130  		if tbl.parentStagingTable != nil {
   131  			value, err = tbl.parentStagingTable.GetByKey(key, loadFromStorage)
   132  			if err != nil {
   133  				return nil, err
   134  			}
   135  
   136  			// global version of keys are not the same, error.
   137  			if !tbl.disableStrictGlobalVersionCheck && value.globalVersion > tbl.prepareingGlobalVersion {
   138  				return nil, ErrStagingTableKeyConfliction
   139  			}
   140  
   141  			value = CloneVersionizedValueItem(tbl.tid, value)
   142  
   143  		} else {
   144  			if loadFromStorage {
   145  				// load from storage.
   146  				value, err = tbl.loadFromStorage(key)
   147  				if err != nil && err != storage.ErrKeyNotFound {
   148  					return nil, err
   149  				}
   150  			} else {
   151  				value = NewDefaultVersionizedValueItem(key, nil, tbl.tid, 0)
   152  				return value, nil
   153  			}
   154  		}
   155  
   156  		// lock and check again.
   157  		tbl.mutex.Lock()
   158  		regetValue := tbl.versionizedValues[keyStr]
   159  		if regetValue == nil {
   160  			tbl.versionizedValues[keyStr] = value
   161  		}
   162  		tbl.mutex.Unlock()
   163  	}
   164  
   165  	return value, nil
   166  }
   167  
   168  // Put put the key/val pair. If key does not exist, copy and incr version from `parentStagingTable` to record previous version.
   169  func (tbl *StagingTable) Put(key []byte, val []byte) (*VersionizedValueItem, error) {
   170  	value, err := tbl.GetByKey(key, false)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	value.dirty = true
   176  	value.val = val
   177  	tbl.mutex.Lock()
   178  	keyStr := byteutils.Hex(key)
   179  	regetValue := tbl.versionizedValues[keyStr]
   180  	if regetValue == nil {
   181  		tbl.versionizedValues[keyStr] = value
   182  	}
   183  	tbl.mutex.Unlock()
   184  
   185  	return value, nil
   186  }
   187  
   188  // Del del the tid/key pair. If tid+key does not exist, copy and incr version from `finalVersionizedValues` to record previous version.
   189  func (tbl *StagingTable) Del(key []byte) (*VersionizedValueItem, error) {
   190  	value, err := tbl.GetByKey(key, false)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	value.deleted = true
   196  	value.dirty = true
   197  	tbl.mutex.Lock()
   198  	keyStr := byteutils.Hex(key)
   199  	regetValue := tbl.versionizedValues[keyStr]
   200  	if regetValue == nil {
   201  		tbl.versionizedValues[keyStr] = value
   202  	}
   203  	tbl.mutex.Unlock()
   204  	return value, nil
   205  }
   206  
   207  // Purge purge key/value pairs of tid.
   208  func (tbl *StagingTable) Purge() {
   209  	tbl.mutex.Lock()
   210  	defer tbl.mutex.Unlock()
   211  
   212  	for _, p := range tbl.preparedStagingTables {
   213  		p.Purge()
   214  	}
   215  	tbl.preparedStagingTables = make(map[interface{}]*StagingTable)
   216  
   217  	// purge all content.
   218  	tbl.versionizedValues = make(stagingValuesMap)
   219  }
   220  
   221  // MergeToParent merge key/value pair of tid to `finalVersionizedValues` which the version of value are the same.
   222  func (tbl *StagingTable) MergeToParent() ([]interface{}, error) {
   223  	if tbl.parentStagingTable == nil {
   224  		return nil, ErrParentStagingTableIsNil
   225  	}
   226  
   227  	tbl.parentStagingTable.mutex.Lock()
   228  	defer tbl.parentStagingTable.mutex.Unlock()
   229  
   230  	tbl.mutex.Lock()
   231  	defer tbl.mutex.Unlock()
   232  
   233  	dependentTids := make(map[interface{}]bool)
   234  	conflictKeys := make(map[string]interface{})
   235  
   236  	// 1. check version.
   237  	targetValues := tbl.parentStagingTable.versionizedValues
   238  
   239  	for keyStr, fromValueItem := range tbl.versionizedValues {
   240  		targetValueItem := targetValues[keyStr]
   241  
   242  		if targetValueItem == nil {
   243  			continue
   244  		}
   245  
   246  		// 1. record conflict.
   247  		if fromValueItem.isConflict(targetValueItem, tbl.isTrieSameKeyCompatibility) {
   248  			conflictKeys[keyStr] = targetValueItem.tid
   249  			continue
   250  		}
   251  
   252  		// 2. record dependentTids.
   253  
   254  		// skip default value loaded from storage.
   255  		if targetValueItem.isDefault() {
   256  			continue
   257  		}
   258  
   259  		// ignore same parent tid for dependentTids.
   260  		if targetValueItem.tid == tbl.parentStagingTable.tid {
   261  			continue
   262  		}
   263  
   264  		// ignore version check when TrieSameKeyCompatibility is enabled.
   265  		if tbl.isTrieSameKeyCompatibility {
   266  			continue
   267  		}
   268  
   269  		dependentTids[targetValueItem.tid] = true
   270  	}
   271  
   272  	if len(conflictKeys) > 0 {
   273  		logging.VLog().WithFields(logrus.Fields{
   274  			"tid":          tbl.tid,
   275  			"parentTid":    tbl.parentStagingTable.tid,
   276  			"conflictKeys": conflictKeys,
   277  		}).Debug("Failed to be merged into parent.")
   278  		return nil, ErrStagingTableKeyConfliction
   279  	}
   280  
   281  	// 2. merge to final.
   282  
   283  	// incr parentStagingTable.globalVersion.
   284  	tbl.parentStagingTable.globalVersion++
   285  
   286  	for keyStr, fromValueItem := range tbl.versionizedValues {
   287  		// ignore default value item.
   288  		if fromValueItem.isDefault() {
   289  			continue
   290  		}
   291  
   292  		// ignore non-dirty.
   293  		if !fromValueItem.dirty {
   294  			continue
   295  		}
   296  
   297  		// merge.
   298  		value := fromValueItem.CloneForMerge(tbl.parentStagingTable.globalVersion)
   299  		targetValues[keyStr] = value
   300  	}
   301  
   302  	tids := make([]interface{}, 0, len(dependentTids))
   303  	for key := range dependentTids {
   304  		tids = append(tids, key)
   305  	}
   306  
   307  	return tids, nil
   308  }
   309  
   310  // Detach the staging table
   311  func (tbl *StagingTable) Detach() error {
   312  	tbl.mutex.Lock()
   313  	if tbl.parentStagingTable == nil {
   314  		tbl.mutex.Unlock()
   315  		return ErrDisallowedCallingInNoPreparedDB
   316  	}
   317  	parentStagingTableMu := &tbl.parentStagingTable.mutex
   318  	tbl.mutex.Unlock()
   319  
   320  	parentStagingTableMu.Lock()
   321  	defer parentStagingTableMu.Unlock()
   322  
   323  	delete(tbl.parentStagingTable.preparedStagingTables, tbl.tid)
   324  	tbl.parentStagingTable = nil
   325  	return nil
   326  }
   327  
   328  func (tbl *StagingTable) getVersionizedValues() stagingValuesMap {
   329  	return tbl.versionizedValues
   330  }
   331  
   332  // Lock staging table
   333  func (tbl *StagingTable) Lock() {
   334  	tbl.mutex.Lock()
   335  }
   336  
   337  // Unlock staging table
   338  func (tbl *StagingTable) Unlock() {
   339  	tbl.mutex.Unlock()
   340  }
   341  
   342  func (tbl *StagingTable) loadFromStorage(key []byte) (*VersionizedValueItem, error) {
   343  	// get from storage.
   344  	val, err := tbl.storage.Get(key)
   345  	if err != nil && err != storage.ErrKeyNotFound {
   346  		return nil, err
   347  	}
   348  
   349  	value := NewDefaultVersionizedValueItem(key, val, tbl.tid, 0)
   350  	return value, nil
   351  }
   352  
   353  func (value *VersionizedValueItem) isDefault() bool {
   354  	return value.version == 0 && value.dirty == false
   355  }
   356  
   357  func (value *VersionizedValueItem) isConflict(b *VersionizedValueItem, trieSameKeyCompatibility bool) bool {
   358  	if b == nil {
   359  		return true
   360  	}
   361  
   362  	// version same, no conflict.
   363  	if value.version == b.version {
   364  		return false
   365  	}
   366  
   367  	if trieSameKeyCompatibility == true {
   368  		// ignore version check when TrieSameKeyCompatibility is enabled.
   369  
   370  		if value.deleted != b.deleted {
   371  			// deleted flag are not the same, conflict.
   372  			return true
   373  		}
   374  
   375  		if !value.deleted && !bytes.Equal(value.val, b.val) {
   376  			// both not delete, and val are not the same, conflict.
   377  			return true
   378  		}
   379  
   380  		// otherwise, no conflict.
   381  		return false
   382  	}
   383  
   384  	// otherwise, conflict.
   385  	return true
   386  }
   387  
   388  // NewDefaultVersionizedValueItem return new instance of VersionizedValueItem, old/new version are 0, dirty is false.
   389  func NewDefaultVersionizedValueItem(key []byte, val []byte, tid interface{}, globalVersion int64) *VersionizedValueItem {
   390  	return &VersionizedValueItem{
   391  		tid:           tid,
   392  		key:           key,
   393  		val:           val,
   394  		version:       0,
   395  		deleted:       false,
   396  		dirty:         false,
   397  		globalVersion: globalVersion,
   398  	}
   399  }
   400  
   401  // CloneVersionizedValueItem copy and return the version increased VersionizedValueItem.
   402  func CloneVersionizedValueItem(tid interface{}, oldValue *VersionizedValueItem) *VersionizedValueItem {
   403  	return &VersionizedValueItem{
   404  		tid:           tid,
   405  		key:           oldValue.key,
   406  		val:           oldValue.val,
   407  		version:       oldValue.version,
   408  		deleted:       oldValue.deleted,
   409  		dirty:         false,
   410  		globalVersion: oldValue.globalVersion,
   411  	}
   412  }
   413  
   414  // CloneForMerge shadow copy of `VersionizedValueItem` with dirty is true.
   415  func (value *VersionizedValueItem) CloneForMerge(globalVersion int64) *VersionizedValueItem {
   416  	return &VersionizedValueItem{
   417  		tid:           value.tid,
   418  		key:           value.key,
   419  		val:           value.val,
   420  		version:       value.version + 1,
   421  		deleted:       value.deleted,
   422  		dirty:         true,
   423  		globalVersion: globalVersion,
   424  	}
   425  }