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