github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 SPDX-License-Identifier: Apache-2.0 4 */ 5 6 package lockbasedtxmgr 7 8 import ( 9 "fmt" 10 11 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 12 "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" 13 commonledger "github.com/hyperledger/fabric/common/ledger" 14 ledger "github.com/hyperledger/fabric/core/ledger" 15 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 16 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" 17 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/storageutil" 18 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/txmgr" 19 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version" 20 "github.com/hyperledger/fabric/core/ledger/util" 21 "github.com/pkg/errors" 22 ) 23 24 const ( 25 queryReadsHashingEnabled = true 26 maxDegreeQueryReadsHashing = uint32(50) 27 ) 28 29 type queryHelper struct { 30 txmgr *LockBasedTxMgr 31 collNameValidator *collNameValidator 32 rwsetBuilder *rwsetutil.RWSetBuilder 33 itrs []*resultsItr 34 err error 35 doneInvoked bool 36 hasher ledger.Hasher 37 } 38 39 func newQueryHelper(txmgr *LockBasedTxMgr, rwsetBuilder *rwsetutil.RWSetBuilder, performCollCheck bool, hasher ledger.Hasher) *queryHelper { 40 helper := &queryHelper{ 41 txmgr: txmgr, 42 rwsetBuilder: rwsetBuilder, 43 hasher: hasher, 44 } 45 validator := newCollNameValidator(txmgr.ledgerid, txmgr.ccInfoProvider, &lockBasedQueryExecutor{helper: helper}, !performCollCheck) 46 helper.collNameValidator = validator 47 return helper 48 } 49 50 func (h *queryHelper) getState(ns string, key string) ([]byte, []byte, error) { 51 if err := h.checkDone(); err != nil { 52 return nil, nil, err 53 } 54 versionedValue, err := h.txmgr.db.GetState(ns, key) 55 if err != nil { 56 return nil, nil, err 57 } 58 val, metadata, ver := decomposeVersionedValue(versionedValue) 59 if h.rwsetBuilder != nil { 60 h.rwsetBuilder.AddToReadSet(ns, key, ver) 61 } 62 return val, metadata, nil 63 } 64 65 func (h *queryHelper) getStateMultipleKeys(namespace string, keys []string) ([][]byte, error) { 66 if err := h.checkDone(); err != nil { 67 return nil, err 68 } 69 versionedValues, err := h.txmgr.db.GetStateMultipleKeys(namespace, keys) 70 if err != nil { 71 return nil, nil 72 } 73 values := make([][]byte, len(versionedValues)) 74 for i, versionedValue := range versionedValues { 75 val, _, ver := decomposeVersionedValue(versionedValue) 76 if h.rwsetBuilder != nil { 77 h.rwsetBuilder.AddToReadSet(namespace, keys[i], ver) 78 } 79 values[i] = val 80 } 81 return values, nil 82 } 83 84 func (h *queryHelper) getStateRangeScanIterator(namespace string, startKey string, endKey string) (ledger.QueryResultsIterator, error) { 85 if err := h.checkDone(); err != nil { 86 return nil, err 87 } 88 itr, err := newResultsItr( 89 namespace, 90 startKey, 91 endKey, 92 nil, 93 h.txmgr.db, 94 h.rwsetBuilder, 95 queryReadsHashingEnabled, 96 maxDegreeQueryReadsHashing, 97 h.hasher, 98 ) 99 if err != nil { 100 return nil, err 101 } 102 h.itrs = append(h.itrs, itr) 103 return itr, nil 104 } 105 106 func (h *queryHelper) getStateRangeScanIteratorWithMetadata(namespace string, startKey string, endKey string, metadata map[string]interface{}) (ledger.QueryResultsIterator, error) { 107 if err := h.checkDone(); err != nil { 108 return nil, err 109 } 110 itr, err := newResultsItr( 111 namespace, 112 startKey, 113 endKey, 114 metadata, 115 h.txmgr.db, 116 h.rwsetBuilder, 117 queryReadsHashingEnabled, 118 maxDegreeQueryReadsHashing, 119 h.hasher, 120 ) 121 if err != nil { 122 return nil, err 123 } 124 h.itrs = append(h.itrs, itr) 125 return itr, nil 126 } 127 128 func (h *queryHelper) executeQuery(namespace, query string) (commonledger.ResultsIterator, error) { 129 if err := h.checkDone(); err != nil { 130 return nil, err 131 } 132 dbItr, err := h.txmgr.db.ExecuteQuery(namespace, query) 133 if err != nil { 134 return nil, err 135 } 136 return &queryResultsItr{DBItr: dbItr, RWSetBuilder: h.rwsetBuilder}, nil 137 } 138 139 func (h *queryHelper) executeQueryWithMetadata(namespace, query string, metadata map[string]interface{}) (ledger.QueryResultsIterator, error) { 140 if err := h.checkDone(); err != nil { 141 return nil, err 142 } 143 dbItr, err := h.txmgr.db.ExecuteQueryWithMetadata(namespace, query, metadata) 144 if err != nil { 145 return nil, err 146 } 147 return &queryResultsItr{DBItr: dbItr, RWSetBuilder: h.rwsetBuilder}, nil 148 } 149 150 func (h *queryHelper) getPrivateData(ns, coll, key string) ([]byte, error) { 151 if err := h.validateCollName(ns, coll); err != nil { 152 return nil, err 153 } 154 if err := h.checkDone(); err != nil { 155 return nil, err 156 } 157 158 var err error 159 var hashVersion *version.Height 160 var versionedValue *statedb.VersionedValue 161 162 if versionedValue, err = h.txmgr.db.GetPrivateData(ns, coll, key); err != nil { 163 return nil, err 164 } 165 166 // metadata is always nil for private data - because, the metadata is part of the hashed key (instead of raw key) 167 val, _, ver := decomposeVersionedValue(versionedValue) 168 169 keyHash := util.ComputeStringHash(key) 170 if hashVersion, err = h.txmgr.db.GetKeyHashVersion(ns, coll, keyHash); err != nil { 171 return nil, err 172 } 173 if !version.AreSame(hashVersion, ver) { 174 return nil, &txmgr.ErrPvtdataNotAvailable{Msg: fmt.Sprintf( 175 "private data matching public hash version is not available. Public hash version = %s, Private data version = %s", 176 hashVersion, ver)} 177 } 178 if h.rwsetBuilder != nil { 179 h.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver) 180 } 181 return val, nil 182 } 183 184 func (h *queryHelper) getPrivateDataValueHash(ns, coll, key string) (valueHash, metadataBytes []byte, err error) { 185 if err := h.validateCollName(ns, coll); err != nil { 186 return nil, nil, err 187 } 188 if err := h.checkDone(); err != nil { 189 return nil, nil, err 190 } 191 var versionedValue *statedb.VersionedValue 192 if versionedValue, err = h.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil { 193 return nil, nil, err 194 } 195 valHash, metadata, ver := decomposeVersionedValue(versionedValue) 196 if h.rwsetBuilder != nil { 197 h.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver) 198 } 199 return valHash, metadata, nil 200 } 201 202 func (h *queryHelper) getPrivateDataMultipleKeys(ns, coll string, keys []string) ([][]byte, error) { 203 if err := h.validateCollName(ns, coll); err != nil { 204 return nil, err 205 } 206 if err := h.checkDone(); err != nil { 207 return nil, err 208 } 209 versionedValues, err := h.txmgr.db.GetPrivateDataMultipleKeys(ns, coll, keys) 210 if err != nil { 211 return nil, nil 212 } 213 values := make([][]byte, len(versionedValues)) 214 for i, versionedValue := range versionedValues { 215 val, _, ver := decomposeVersionedValue(versionedValue) 216 if h.rwsetBuilder != nil { 217 h.rwsetBuilder.AddToHashedReadSet(ns, coll, keys[i], ver) 218 } 219 values[i] = val 220 } 221 return values, nil 222 } 223 224 func (h *queryHelper) getPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (commonledger.ResultsIterator, error) { 225 if err := h.validateCollName(namespace, collection); err != nil { 226 return nil, err 227 } 228 if err := h.checkDone(); err != nil { 229 return nil, err 230 } 231 dbItr, err := h.txmgr.db.GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey) 232 if err != nil { 233 return nil, err 234 } 235 return &pvtdataResultsItr{namespace, collection, dbItr}, nil 236 } 237 238 func (h *queryHelper) executeQueryOnPrivateData(namespace, collection, query string) (commonledger.ResultsIterator, error) { 239 if err := h.validateCollName(namespace, collection); err != nil { 240 return nil, err 241 } 242 if err := h.checkDone(); err != nil { 243 return nil, err 244 } 245 dbItr, err := h.txmgr.db.ExecuteQueryOnPrivateData(namespace, collection, query) 246 if err != nil { 247 return nil, err 248 } 249 return &pvtdataResultsItr{namespace, collection, dbItr}, nil 250 } 251 252 func (h *queryHelper) getStateMetadata(ns string, key string) (map[string][]byte, error) { 253 if err := h.checkDone(); err != nil { 254 return nil, err 255 } 256 var metadataBytes []byte 257 var err error 258 if h.rwsetBuilder == nil { 259 // reads versions are not getting recorded, retrieve metadata value via optimized path 260 if metadataBytes, err = h.txmgr.db.GetStateMetadata(ns, key); err != nil { 261 return nil, err 262 } 263 } else { 264 if _, metadataBytes, err = h.getState(ns, key); err != nil { 265 return nil, err 266 } 267 } 268 return storageutil.DeserializeMetadata(metadataBytes) 269 } 270 271 func (h *queryHelper) getPrivateDataMetadata(ns, coll, key string) (map[string][]byte, error) { 272 if h.rwsetBuilder == nil { 273 // reads versions are not getting recorded, retrieve metadata value via optimized path 274 return h.getPrivateDataMetadataByHash(ns, coll, util.ComputeStringHash(key)) 275 } 276 if err := h.validateCollName(ns, coll); err != nil { 277 return nil, err 278 } 279 if err := h.checkDone(); err != nil { 280 return nil, err 281 } 282 _, metadataBytes, err := h.getPrivateDataValueHash(ns, coll, key) 283 if err != nil { 284 return nil, err 285 } 286 return storageutil.DeserializeMetadata(metadataBytes) 287 } 288 289 func (h *queryHelper) getPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) { 290 if err := h.validateCollName(ns, coll); err != nil { 291 return nil, err 292 } 293 if err := h.checkDone(); err != nil { 294 return nil, err 295 } 296 if h.rwsetBuilder != nil { 297 // this requires to improve rwset builder to accept a keyhash 298 return nil, errors.New("retrieving private data metadata by keyhash is not supported in simulation. This function is only available for query as yet") 299 } 300 metadataBytes, err := h.txmgr.db.GetPrivateDataMetadataByHash(ns, coll, keyhash) 301 if err != nil { 302 return nil, err 303 } 304 return storageutil.DeserializeMetadata(metadataBytes) 305 } 306 307 func (h *queryHelper) done() { 308 if h.doneInvoked { 309 return 310 } 311 312 defer func() { 313 h.txmgr.commitRWLock.RUnlock() 314 h.doneInvoked = true 315 for _, itr := range h.itrs { 316 itr.Close() 317 } 318 }() 319 } 320 321 func (h *queryHelper) addRangeQueryInfo() { 322 for _, itr := range h.itrs { 323 if h.rwsetBuilder != nil { 324 results, hash, err := itr.rangeQueryResultsHelper.Done() 325 if err != nil { 326 h.err = err 327 return 328 } 329 if results != nil { 330 rwsetutil.SetRawReads(itr.rangeQueryInfo, results) 331 } 332 if hash != nil { 333 rwsetutil.SetMerkelSummary(itr.rangeQueryInfo, hash) 334 } 335 h.rwsetBuilder.AddToRangeQuerySet(itr.ns, itr.rangeQueryInfo) 336 } 337 } 338 } 339 340 func (h *queryHelper) checkDone() error { 341 if h.doneInvoked { 342 return errors.New("this instance should not be used after calling Done()") 343 } 344 return nil 345 } 346 347 func (h *queryHelper) validateCollName(ns, coll string) error { 348 return h.collNameValidator.validateCollName(ns, coll) 349 } 350 351 // resultsItr implements interface ledger.ResultsIterator 352 // this wraps the actual db iterator and intercept the calls 353 // to build rangeQueryInfo in the ReadWriteSet that is used 354 // for performing phantom read validation during commit 355 type resultsItr struct { 356 ns string 357 endKey string 358 dbItr statedb.ResultsIterator 359 rwSetBuilder *rwsetutil.RWSetBuilder 360 rangeQueryInfo *kvrwset.RangeQueryInfo 361 rangeQueryResultsHelper *rwsetutil.RangeQueryResultsHelper 362 } 363 364 func newResultsItr(ns string, startKey string, endKey string, metadata map[string]interface{}, 365 db statedb.VersionedDB, rwsetBuilder *rwsetutil.RWSetBuilder, enableHashing bool, maxDegree uint32, hasher ledger.Hasher) (*resultsItr, error) { 366 var err error 367 var dbItr statedb.ResultsIterator 368 if metadata == nil { 369 dbItr, err = db.GetStateRangeScanIterator(ns, startKey, endKey) 370 } else { 371 dbItr, err = db.GetStateRangeScanIteratorWithMetadata(ns, startKey, endKey, metadata) 372 } 373 if err != nil { 374 return nil, err 375 } 376 itr := &resultsItr{ns: ns, dbItr: dbItr} 377 // it's a simulation request so, enable capture of range query info 378 if rwsetBuilder != nil { 379 itr.rwSetBuilder = rwsetBuilder 380 itr.endKey = endKey 381 // just set the StartKey... set the EndKey later below in the Next() method. 382 itr.rangeQueryInfo = &kvrwset.RangeQueryInfo{StartKey: startKey} 383 resultsHelper, err := rwsetutil.NewRangeQueryResultsHelper(enableHashing, maxDegree, hasher) 384 if err != nil { 385 return nil, err 386 } 387 itr.rangeQueryResultsHelper = resultsHelper 388 } 389 return itr, nil 390 } 391 392 // Next implements method in interface ledger.ResultsIterator 393 // Before returning the next result, update the EndKey and ItrExhausted in rangeQueryInfo 394 // If we set the EndKey in the constructor (as we do for the StartKey) to what is 395 // supplied in the original query, we may be capturing the unnecessary longer range if the 396 // caller decides to stop iterating at some intermediate point. Alternatively, we could have 397 // set the EndKey and ItrExhausted in the Close() function but it may not be desirable to change 398 // transactional behaviour based on whether the Close() was invoked or not 399 func (itr *resultsItr) Next() (commonledger.QueryResult, error) { 400 queryResult, err := itr.dbItr.Next() 401 if err != nil { 402 return nil, err 403 } 404 itr.updateRangeQueryInfo(queryResult) 405 if queryResult == nil { 406 return nil, nil 407 } 408 versionedKV := queryResult.(*statedb.VersionedKV) 409 return &queryresult.KV{Namespace: versionedKV.Namespace, Key: versionedKV.Key, Value: versionedKV.Value}, nil 410 } 411 412 // GetBookmarkAndClose implements method in interface ledger.ResultsIterator 413 func (itr *resultsItr) GetBookmarkAndClose() string { 414 returnBookmark := "" 415 if queryResultIterator, ok := itr.dbItr.(statedb.QueryResultsIterator); ok { 416 returnBookmark = queryResultIterator.GetBookmarkAndClose() 417 } 418 return returnBookmark 419 } 420 421 // updateRangeQueryInfo updates two attributes of the rangeQueryInfo 422 // 1) The EndKey - set to either a) latest key that is to be returned to the caller (if the iterator is not exhausted) 423 // because, we do not know if the caller is again going to invoke Next() or not. 424 // or b) the last key that was supplied in the original query (if the iterator is exhausted) 425 // 2) The ItrExhausted - set to true if the iterator is going to return nil as a result of the Next() call 426 func (itr *resultsItr) updateRangeQueryInfo(queryResult statedb.QueryResult) { 427 if itr.rwSetBuilder == nil { 428 return 429 } 430 431 if queryResult == nil { 432 // caller scanned till the iterator got exhausted. 433 // So, set the endKey to the actual endKey supplied in the query 434 itr.rangeQueryInfo.ItrExhausted = true 435 itr.rangeQueryInfo.EndKey = itr.endKey 436 return 437 } 438 versionedKV := queryResult.(*statedb.VersionedKV) 439 itr.rangeQueryResultsHelper.AddResult(rwsetutil.NewKVRead(versionedKV.Key, versionedKV.Version)) 440 // Set the end key to the latest key retrieved by the caller. 441 // Because, the caller may actually not invoke the Next() function again 442 itr.rangeQueryInfo.EndKey = versionedKV.Key 443 } 444 445 // Close implements method in interface ledger.ResultsIterator 446 func (itr *resultsItr) Close() { 447 itr.dbItr.Close() 448 } 449 450 type queryResultsItr struct { 451 DBItr statedb.ResultsIterator 452 RWSetBuilder *rwsetutil.RWSetBuilder 453 } 454 455 // Next implements method in interface ledger.ResultsIterator 456 func (itr *queryResultsItr) Next() (commonledger.QueryResult, error) { 457 458 queryResult, err := itr.DBItr.Next() 459 if err != nil { 460 return nil, err 461 } 462 if queryResult == nil { 463 return nil, nil 464 } 465 versionedQueryRecord := queryResult.(*statedb.VersionedKV) 466 logger.Debugf("queryResultsItr.Next() returned a record:%s", string(versionedQueryRecord.Value)) 467 468 if itr.RWSetBuilder != nil { 469 itr.RWSetBuilder.AddToReadSet(versionedQueryRecord.Namespace, versionedQueryRecord.Key, versionedQueryRecord.Version) 470 } 471 return &queryresult.KV{Namespace: versionedQueryRecord.Namespace, Key: versionedQueryRecord.Key, Value: versionedQueryRecord.Value}, nil 472 } 473 474 // Close implements method in interface ledger.ResultsIterator 475 func (itr *queryResultsItr) Close() { 476 itr.DBItr.Close() 477 } 478 479 func (itr *queryResultsItr) GetBookmarkAndClose() string { 480 returnBookmark := "" 481 if queryResultIterator, ok := itr.DBItr.(statedb.QueryResultsIterator); ok { 482 returnBookmark = queryResultIterator.GetBookmarkAndClose() 483 } 484 return returnBookmark 485 } 486 487 func decomposeVersionedValue(versionedValue *statedb.VersionedValue) ([]byte, []byte, *version.Height) { 488 var value []byte 489 var metadata []byte 490 var ver *version.Height 491 if versionedValue != nil { 492 value = versionedValue.Value 493 ver = versionedValue.Version 494 metadata = versionedValue.Metadata 495 } 496 return value, metadata, ver 497 } 498 499 // pvtdataResultsItr iterates over results of a query on pvt data 500 type pvtdataResultsItr struct { 501 ns string 502 coll string 503 dbItr statedb.ResultsIterator 504 } 505 506 // Next implements method in interface ledger.ResultsIterator 507 func (itr *pvtdataResultsItr) Next() (commonledger.QueryResult, error) { 508 queryResult, err := itr.dbItr.Next() 509 if err != nil { 510 return nil, err 511 } 512 if queryResult == nil { 513 return nil, nil 514 } 515 versionedQueryRecord := queryResult.(*statedb.VersionedKV) 516 return &queryresult.KV{ 517 Namespace: itr.ns, 518 Key: versionedQueryRecord.Key, 519 Value: versionedQueryRecord.Value, 520 }, nil 521 } 522 523 // Close implements method in interface ledger.ResultsIterator 524 func (itr *pvtdataResultsItr) Close() { 525 itr.dbItr.Close() 526 }