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 }