diff --git a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val.go b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val.go index 35acc95c12d..37d20f764b2 100644 --- a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val.go +++ b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val.go @@ -16,12 +16,12 @@ import ( type collNameValidator struct { ledgerID string ccInfoProvider ledger.DeployedChaincodeInfoProvider - queryExecutor *lockBasedQueryExecutor + queryExecutor *queryExecutor cache collConfigCache noop bool } -func newCollNameValidator(ledgerID string, ccInfoProvider ledger.DeployedChaincodeInfoProvider, qe *lockBasedQueryExecutor, noop bool) *collNameValidator { +func newCollNameValidator(ledgerID string, ccInfoProvider ledger.DeployedChaincodeInfoProvider, qe *queryExecutor, noop bool) *collNameValidator { return &collNameValidator{ledgerID, ccInfoProvider, qe, make(collConfigCache), noop} } diff --git a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val_test.go b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val_test.go index 14002976204..bc7d8824410 100644 --- a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val_test.go +++ b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/collection_val_test.go @@ -60,8 +60,8 @@ func TestPvtGetNoCollection(t *testing.T) { txMgr := testEnv.getTxMgr().(*LockBasedTxMgr) cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) assert.NoError(t, err) - queryHelper := newQueryHelper(txMgr, nil, true, cryptoProvider) - valueHash, metadataBytes, err := queryHelper.getPrivateDataValueHash("cc", "coll", "key") + qe := newQueryExecutor(txMgr, "", nil, true, cryptoProvider) + valueHash, metadataBytes, err := qe.getPrivateDataValueHash("cc", "coll", "key") assert.Nil(t, valueHash) assert.Nil(t, metadataBytes) assert.Error(t, err) diff --git a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_query_executer.go b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_query_executer.go deleted file mode 100644 index 16cd9780675..00000000000 --- a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_query_executer.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright IBM Corp. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package lockbasedtxmgr - -import ( - commonledger "github.com/hyperledger/fabric/common/ledger" - "github.com/hyperledger/fabric/core/ledger" -) - -// lockBasedQueryExecutor is a query executor used in `LockBasedTxMgr` -type lockBasedQueryExecutor struct { - helper *queryHelper - txid string -} - -func newQueryExecutor(txmgr *LockBasedTxMgr, txid string, performCollCheck bool, hasher ledger.Hasher) *lockBasedQueryExecutor { - helper := newQueryHelper(txmgr, nil, performCollCheck, hasher) - logger.Debugf("constructing new query executor txid = [%s]", txid) - return &lockBasedQueryExecutor{helper, txid} -} - -// GetState implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) GetState(ns string, key string) (val []byte, err error) { - val, _, err = q.helper.getState(ns, key) - return -} - -// GetStateMetadata implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) GetStateMetadata(namespace, key string) (map[string][]byte, error) { - return q.helper.getStateMetadata(namespace, key) -} - -// GetStateMultipleKeys implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) GetStateMultipleKeys(namespace string, keys []string) ([][]byte, error) { - return q.helper.getStateMultipleKeys(namespace, keys) -} - -// GetStateRangeScanIterator implements method in interface `ledger.QueryExecutor` -// startKey is included in the results and endKey is excluded. An empty startKey refers to the first available key -// and an empty endKey refers to the last available key. For scanning all the keys, both the startKey and the endKey -// can be supplied as empty strings. However, a full scan should be used judiciously for performance reasons. -func (q *lockBasedQueryExecutor) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (commonledger.ResultsIterator, error) { - return q.helper.getStateRangeScanIterator(namespace, startKey, endKey) -} - -// GetStateRangeScanIteratorWithPagination implements method in interface `ledger.QueryExecutor` -// startKey is included in the results and endKey is excluded. An empty startKey refers to the first available key -// and an empty endKey refers to the last available key. For scanning all the keys, both the startKey and the endKey -// can be supplied as empty strings. However, a full scan should be used judiciously for performance reasons. -// The page size parameters limits the number of returned results. -func (q *lockBasedQueryExecutor) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (ledger.QueryResultsIterator, error) { - return q.helper.getStateRangeScanIteratorWithPagination(namespace, startKey, endKey, pageSize) -} - -// ExecuteQuery implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) ExecuteQuery(namespace, query string) (commonledger.ResultsIterator, error) { - return q.helper.executeQuery(namespace, query) -} - -// ExecuteQueryWithPagination implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (ledger.QueryResultsIterator, error) { - return q.helper.executeQueryWithPagination(namespace, query, bookmark, pageSize) -} - -// GetPrivateData implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) GetPrivateData(namespace, collection, key string) ([]byte, error) { - return q.helper.getPrivateData(namespace, collection, key) -} - -func (q *lockBasedQueryExecutor) GetPrivateDataHash(namespace, collection, key string) ([]byte, error) { - valueHash, _, err := q.helper.getPrivateDataValueHash(namespace, collection, key) - return valueHash, err -} - -// GetPrivateDataMetadata implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) GetPrivateDataMetadata(namespace, collection, key string) (map[string][]byte, error) { - return q.helper.getPrivateDataMetadata(namespace, collection, key) -} - -// GetPrivateDataMetadataByHash implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) GetPrivateDataMetadataByHash(namespace, collection string, keyhash []byte) (map[string][]byte, error) { - return q.helper.getPrivateDataMetadataByHash(namespace, collection, keyhash) -} - -// GetPrivateDataMultipleKeys implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) GetPrivateDataMultipleKeys(namespace, collection string, keys []string) ([][]byte, error) { - return q.helper.getPrivateDataMultipleKeys(namespace, collection, keys) -} - -// GetPrivateDataRangeScanIterator implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (commonledger.ResultsIterator, error) { - return q.helper.getPrivateDataRangeScanIterator(namespace, collection, startKey, endKey) -} - -// ExecuteQueryOnPrivateData implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) ExecuteQueryOnPrivateData(namespace, collection, query string) (commonledger.ResultsIterator, error) { - return q.helper.executeQueryOnPrivateData(namespace, collection, query) -} - -// Done implements method in interface `ledger.QueryExecutor` -func (q *lockBasedQueryExecutor) Done() { - logger.Debugf("Done with transaction simulation / query execution [%s]", q.txid) - q.helper.done() -} diff --git a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_txmgr.go b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_txmgr.go index 50ee4abde4b..aadda51b46e 100644 --- a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_txmgr.go +++ b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_txmgr.go @@ -101,7 +101,7 @@ func (txmgr *LockBasedTxMgr) GetLastSavepoint() (*version.Height, error) { // NewQueryExecutor implements method in interface `txmgmt.TxMgr` func (txmgr *LockBasedTxMgr) NewQueryExecutor(txid string) (ledger.QueryExecutor, error) { - qe := newQueryExecutor(txmgr, txid, true, txmgr.hasher) + qe := newQueryExecutor(txmgr, txid, nil, true, txmgr.hasher) txmgr.commitRWLock.RLock() return qe, nil } @@ -117,7 +117,7 @@ func (txmgr *LockBasedTxMgr) NewQueryExecutor(txid string) (ledger.QueryExecutor // querying the ledger state so that the sequence of initialization is explicitly controlled. // However that needs a bigger refactoring of code. func (txmgr *LockBasedTxMgr) NewQueryExecutorNoCollChecks() (ledger.QueryExecutor, error) { - qe := newQueryExecutor(txmgr, "", false, txmgr.hasher) + qe := newQueryExecutor(txmgr, "", nil, false, txmgr.hasher) txmgr.commitRWLock.RLock() return qe, nil } @@ -125,7 +125,7 @@ func (txmgr *LockBasedTxMgr) NewQueryExecutorNoCollChecks() (ledger.QueryExecuto // NewTxSimulator implements method in interface `txmgmt.TxMgr` func (txmgr *LockBasedTxMgr) NewTxSimulator(txid string) (ledger.TxSimulator, error) { logger.Debugf("constructing new tx simulator") - s, err := newLockBasedTxSimulator(txmgr, txid, txmgr.hasher) + s, err := newTxSimulator(txmgr, txid, txmgr.hasher) if err != nil { return nil, err } diff --git a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/query_executor.go similarity index 57% rename from core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go rename to core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/query_executor.go index c3a60cb45d5..297b216040e 100644 --- a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.go +++ b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/query_executor.go @@ -12,7 +12,7 @@ import ( "github.com/hyperledger/fabric-protos-go/ledger/queryresult" "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" commonledger "github.com/hyperledger/fabric/common/ledger" - ledger "github.com/hyperledger/fabric/core/ledger" + "github.com/hyperledger/fabric/core/ledger" "github.com/hyperledger/fabric/core/ledger/internal/version" "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" @@ -27,63 +27,100 @@ const ( maxDegreeQueryReadsHashing = uint32(50) ) -type queryHelper struct { +// queryExecutor is a query executor used in `LockBasedTxMgr` +type queryExecutor struct { txmgr *LockBasedTxMgr collNameValidator *collNameValidator + collectReadset bool rwsetBuilder *rwsetutil.RWSetBuilder itrs []*resultsItr err error doneInvoked bool hasher ledger.Hasher + txid string } -func newQueryHelper(txmgr *LockBasedTxMgr, rwsetBuilder *rwsetutil.RWSetBuilder, performCollCheck bool, hasher ledger.Hasher) *queryHelper { - helper := &queryHelper{ - txmgr: txmgr, - rwsetBuilder: rwsetBuilder, - hasher: hasher, +func newQueryExecutor(txmgr *LockBasedTxMgr, txid string, rwsetBuilder *rwsetutil.RWSetBuilder, performCollCheck bool, hasher ledger.Hasher) *queryExecutor { + logger.Debugf("constructing new query executor txid = [%s]", txid) + qe := &queryExecutor{} + qe.txid = txid + qe.txmgr = txmgr + if rwsetBuilder != nil { + qe.collectReadset = true + qe.rwsetBuilder = rwsetBuilder } - validator := newCollNameValidator(txmgr.ledgerid, txmgr.ccInfoProvider, &lockBasedQueryExecutor{helper: helper}, !performCollCheck) - helper.collNameValidator = validator - return helper + qe.hasher = hasher + validator := newCollNameValidator(txmgr.ledgerid, txmgr.ccInfoProvider, qe, !performCollCheck) + qe.collNameValidator = validator + return qe +} + +// GetState implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) GetState(ns, key string) ([]byte, error) { + val, _, err := q.getState(ns, key) + return val, err } -func (h *queryHelper) getState(ns string, key string) ([]byte, []byte, error) { - if err := h.checkDone(); err != nil { +func (q *queryExecutor) getState(ns, key string) ([]byte, []byte, error) { + if err := q.checkDone(); err != nil { return nil, nil, err } - versionedValue, err := h.txmgr.db.GetState(ns, key) + versionedValue, err := q.txmgr.db.GetState(ns, key) if err != nil { return nil, nil, err } val, metadata, ver := decomposeVersionedValue(versionedValue) - if h.rwsetBuilder != nil { - h.rwsetBuilder.AddToReadSet(ns, key, ver) + if q.collectReadset { + q.rwsetBuilder.AddToReadSet(ns, key, ver) } return val, metadata, nil } -func (h *queryHelper) getStateMultipleKeys(namespace string, keys []string) ([][]byte, error) { - if err := h.checkDone(); err != nil { +// GetStateMetadata implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) GetStateMetadata(ns, key string) (map[string][]byte, error) { + if err := q.checkDone(); err != nil { return nil, err } - versionedValues, err := h.txmgr.db.GetStateMultipleKeys(namespace, keys) + var metadata []byte + var err error + if !q.collectReadset { + if metadata, err = q.txmgr.db.GetStateMetadata(ns, key); err != nil { + return nil, err + } + } else { + if _, metadata, err = q.getState(ns, key); err != nil { + return nil, err + } + } + return statemetadata.Deserialize(metadata) +} + +// GetStateMultipleKeys implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) GetStateMultipleKeys(ns string, keys []string) ([][]byte, error) { + if err := q.checkDone(); err != nil { + return nil, err + } + versionedValues, err := q.txmgr.db.GetStateMultipleKeys(ns, keys) if err != nil { return nil, nil } values := make([][]byte, len(versionedValues)) for i, versionedValue := range versionedValues { val, _, ver := decomposeVersionedValue(versionedValue) - if h.rwsetBuilder != nil { - h.rwsetBuilder.AddToReadSet(namespace, keys[i], ver) + if q.collectReadset { + q.rwsetBuilder.AddToReadSet(ns, keys[i], ver) } values[i] = val } return values, nil } -func (h *queryHelper) getStateRangeScanIterator(namespace string, startKey string, endKey string) (ledger.QueryResultsIterator, error) { - if err := h.checkDone(); err != nil { +// GetStateRangeScanIterator implements method in interface `ledger.QueryExecutor` +// startKey is included in the results and endKey is excluded. An empty startKey refers to the first available key +// and an empty endKey refers to the last available key. For scanning all the keys, both the startKey and the endKey +// can be supplied as empty strings. However, a full scan should be used judiciously for performance reasons. +func (q *queryExecutor) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (commonledger.ResultsIterator, error) { + if err := q.checkDone(); err != nil { return nil, err } itr, err := newResultsItr( @@ -91,21 +128,21 @@ func (h *queryHelper) getStateRangeScanIterator(namespace string, startKey strin startKey, endKey, 0, - h.txmgr.db, - h.rwsetBuilder, + q.txmgr.db, + q.rwsetBuilder, queryReadsHashingEnabled, maxDegreeQueryReadsHashing, - h.hasher, + q.hasher, ) if err != nil { return nil, err } - h.itrs = append(h.itrs, itr) + q.itrs = append(q.itrs, itr) return itr, nil } -func (h *queryHelper) getStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (ledger.QueryResultsIterator, error) { - if err := h.checkDone(); err != nil { +func (q *queryExecutor) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (ledger.QueryResultsIterator, error) { + if err := q.checkDone(); err != nil { return nil, err } itr, err := newResultsItr( @@ -113,46 +150,48 @@ func (h *queryHelper) getStateRangeScanIteratorWithPagination(namespace string, startKey, endKey, pageSize, - h.txmgr.db, - h.rwsetBuilder, + q.txmgr.db, + q.rwsetBuilder, queryReadsHashingEnabled, maxDegreeQueryReadsHashing, - h.hasher, + q.hasher, ) if err != nil { return nil, err } - h.itrs = append(h.itrs, itr) + q.itrs = append(q.itrs, itr) return itr, nil } -func (h *queryHelper) executeQuery(namespace, query string) (commonledger.ResultsIterator, error) { - if err := h.checkDone(); err != nil { +// ExecuteQuery implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) ExecuteQuery(namespace, query string) (commonledger.ResultsIterator, error) { + if err := q.checkDone(); err != nil { return nil, err } - dbItr, err := h.txmgr.db.ExecuteQuery(namespace, query) + dbItr, err := q.txmgr.db.ExecuteQuery(namespace, query) if err != nil { return nil, err } - return &queryResultsItr{DBItr: dbItr, RWSetBuilder: h.rwsetBuilder}, nil + return &queryResultsItr{DBItr: dbItr, RWSetBuilder: q.rwsetBuilder}, nil } -func (h *queryHelper) executeQueryWithPagination(namespace, query, bookmark string, pageSize int32) (ledger.QueryResultsIterator, error) { - if err := h.checkDone(); err != nil { +func (q *queryExecutor) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (ledger.QueryResultsIterator, error) { + if err := q.checkDone(); err != nil { return nil, err } - dbItr, err := h.txmgr.db.ExecuteQueryWithPagination(namespace, query, bookmark, pageSize) + dbItr, err := q.txmgr.db.ExecuteQueryWithPagination(namespace, query, bookmark, pageSize) if err != nil { return nil, err } - return &queryResultsItr{DBItr: dbItr, RWSetBuilder: h.rwsetBuilder}, nil + return &queryResultsItr{DBItr: dbItr, RWSetBuilder: q.rwsetBuilder}, nil } -func (h *queryHelper) getPrivateData(ns, coll, key string) ([]byte, error) { - if err := h.validateCollName(ns, coll); err != nil { +// GetPrivateData implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) GetPrivateData(ns, coll, key string) ([]byte, error) { + if err := q.validateCollName(ns, coll); err != nil { return nil, err } - if err := h.checkDone(); err != nil { + if err := q.checkDone(); err != nil { return nil, err } @@ -160,7 +199,7 @@ func (h *queryHelper) getPrivateData(ns, coll, key string) ([]byte, error) { var hashVersion *version.Height var versionedValue *statedb.VersionedValue - if versionedValue, err = h.txmgr.db.GetPrivateData(ns, coll, key); err != nil { + if versionedValue, err = q.txmgr.db.GetPrivateData(ns, coll, key); err != nil { return nil, err } @@ -168,7 +207,7 @@ func (h *queryHelper) getPrivateData(ns, coll, key string) ([]byte, error) { val, _, ver := decomposeVersionedValue(versionedValue) keyHash := util.ComputeStringHash(key) - if hashVersion, err = h.txmgr.db.GetKeyHashVersion(ns, coll, keyHash); err != nil { + if hashVersion, err = q.txmgr.db.GetKeyHashVersion(ns, coll, keyHash); err != nil { return nil, err } if !version.AreSame(hashVersion, ver) { @@ -176,177 +215,169 @@ func (h *queryHelper) getPrivateData(ns, coll, key string) ([]byte, error) { "private data matching public hash version is not available. Public hash version = %s, Private data version = %s", hashVersion, ver)} } - if h.rwsetBuilder != nil { - h.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver) + if q.collectReadset { + q.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver) } return val, nil } -func (h *queryHelper) getPrivateDataValueHash(ns, coll, key string) (valueHash, metadataBytes []byte, err error) { - if err := h.validateCollName(ns, coll); err != nil { - return nil, nil, err +func (q *queryExecutor) GetPrivateDataHash(ns, coll, key string) ([]byte, error) { + if err := q.validateCollName(ns, coll); err != nil { + return nil, err } - if err := h.checkDone(); err != nil { - return nil, nil, err + if err := q.checkDone(); err != nil { + return nil, err } var versionedValue *statedb.VersionedValue - if versionedValue, err = h.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil { - return nil, nil, err + var err error + if versionedValue, err = q.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil { + return nil, err } - valHash, metadata, ver := decomposeVersionedValue(versionedValue) - if h.rwsetBuilder != nil { - h.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver) + valHash, _, ver := decomposeVersionedValue(versionedValue) + if q.collectReadset { + q.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver) } - return valHash, metadata, nil + return valHash, nil } -func (h *queryHelper) getPrivateDataMultipleKeys(ns, coll string, keys []string) ([][]byte, error) { - if err := h.validateCollName(ns, coll); err != nil { +// GetPrivateDataMetadata implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) GetPrivateDataMetadata(ns, coll, key string) (map[string][]byte, error) { + if !q.collectReadset { + // reads versions are not getting recorded, retrieve metadata value via optimized path + return q.getPrivateDataMetadataByHash(ns, coll, util.ComputeStringHash(key)) + } + if err := q.validateCollName(ns, coll); err != nil { return nil, err } - if err := h.checkDone(); err != nil { + if err := q.checkDone(); err != nil { return nil, err } - versionedValues, err := h.txmgr.db.GetPrivateDataMultipleKeys(ns, coll, keys) + _, metadataBytes, err := q.getPrivateDataValueHash(ns, coll, key) if err != nil { - return nil, nil - } - values := make([][]byte, len(versionedValues)) - for i, versionedValue := range versionedValues { - val, _, ver := decomposeVersionedValue(versionedValue) - if h.rwsetBuilder != nil { - h.rwsetBuilder.AddToHashedReadSet(ns, coll, keys[i], ver) - } - values[i] = val + return nil, err } - return values, nil + return statemetadata.Deserialize(metadataBytes) } -func (h *queryHelper) getPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (commonledger.ResultsIterator, error) { - if err := h.validateCollName(namespace, collection); err != nil { - return nil, err +func (q *queryExecutor) getPrivateDataValueHash(ns, coll, key string) (valueHash, metadataBytes []byte, err error) { + if err := q.validateCollName(ns, coll); err != nil { + return nil, nil, err } - if err := h.checkDone(); err != nil { - return nil, err + if err := q.checkDone(); err != nil { + return nil, nil, err } - dbItr, err := h.txmgr.db.GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey) - if err != nil { - return nil, err + var versionedValue *statedb.VersionedValue + if versionedValue, err = q.txmgr.db.GetPrivateDataHash(ns, coll, key); err != nil { + return nil, nil, err + } + valHash, metadata, ver := decomposeVersionedValue(versionedValue) + if q.collectReadset { + q.rwsetBuilder.AddToHashedReadSet(ns, coll, key, ver) } - return &pvtdataResultsItr{namespace, collection, dbItr}, nil + return valHash, metadata, nil +} + +// GetPrivateDataMetadataByHash implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) GetPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) { + return q.getPrivateDataMetadataByHash(ns, coll, keyhash) } -func (h *queryHelper) executeQueryOnPrivateData(namespace, collection, query string) (commonledger.ResultsIterator, error) { - if err := h.validateCollName(namespace, collection); err != nil { +func (q *queryExecutor) getPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) { + if err := q.validateCollName(ns, coll); err != nil { return nil, err } - if err := h.checkDone(); err != nil { + if err := q.checkDone(); err != nil { return nil, err } - dbItr, err := h.txmgr.db.ExecuteQueryOnPrivateData(namespace, collection, query) + if q.collectReadset { + // this requires to improve rwset builder to accept a keyhash + return nil, errors.New("retrieving private data metadata by keyhash is not supported in simulation. This function is only available for query as yet") + } + metadataBytes, err := q.txmgr.db.GetPrivateDataMetadataByHash(ns, coll, keyhash) if err != nil { return nil, err } - return &pvtdataResultsItr{namespace, collection, dbItr}, nil + return statemetadata.Deserialize(metadataBytes) } -func (h *queryHelper) getStateMetadata(ns string, key string) (map[string][]byte, error) { - if err := h.checkDone(); err != nil { +// GetPrivateDataMultipleKeys implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) GetPrivateDataMultipleKeys(ns, coll string, keys []string) ([][]byte, error) { + if err := q.validateCollName(ns, coll); err != nil { return nil, err } - var metadataBytes []byte - var err error - if h.rwsetBuilder == nil { - // reads versions are not getting recorded, retrieve metadata value via optimized path - if metadataBytes, err = h.txmgr.db.GetStateMetadata(ns, key); err != nil { - return nil, err - } - } else { - if _, metadataBytes, err = h.getState(ns, key); err != nil { - return nil, err + if err := q.checkDone(); err != nil { + return nil, err + } + versionedValues, err := q.txmgr.db.GetPrivateDataMultipleKeys(ns, coll, keys) + if err != nil { + return nil, nil + } + values := make([][]byte, len(versionedValues)) + for i, versionedValue := range versionedValues { + val, _, ver := decomposeVersionedValue(versionedValue) + if q.collectReadset { + q.rwsetBuilder.AddToHashedReadSet(ns, coll, keys[i], ver) } + values[i] = val } - return statemetadata.Deserialize(metadataBytes) + return values, nil } -func (h *queryHelper) getPrivateDataMetadata(ns, coll, key string) (map[string][]byte, error) { - if h.rwsetBuilder == nil { - // reads versions are not getting recorded, retrieve metadata value via optimized path - return h.getPrivateDataMetadataByHash(ns, coll, util.ComputeStringHash(key)) - } - if err := h.validateCollName(ns, coll); err != nil { +// GetPrivateDataRangeScanIterator implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) GetPrivateDataRangeScanIterator(ns, coll, startKey, endKey string) (commonledger.ResultsIterator, error) { + if err := q.validateCollName(ns, coll); err != nil { return nil, err } - if err := h.checkDone(); err != nil { + if err := q.checkDone(); err != nil { return nil, err } - _, metadataBytes, err := h.getPrivateDataValueHash(ns, coll, key) + dbItr, err := q.txmgr.db.GetPrivateDataRangeScanIterator(ns, coll, startKey, endKey) if err != nil { return nil, err } - return statemetadata.Deserialize(metadataBytes) + return &pvtdataResultsItr{ns, coll, dbItr}, nil } -func (h *queryHelper) getPrivateDataMetadataByHash(ns, coll string, keyhash []byte) (map[string][]byte, error) { - if err := h.validateCollName(ns, coll); err != nil { +// ExecuteQueryOnPrivateData implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) ExecuteQueryOnPrivateData(ns, coll, query string) (commonledger.ResultsIterator, error) { + if err := q.validateCollName(ns, coll); err != nil { return nil, err } - if err := h.checkDone(); err != nil { + if err := q.checkDone(); err != nil { return nil, err } - if h.rwsetBuilder != nil { - // this requires to improve rwset builder to accept a keyhash - return nil, errors.New("retrieving private data metadata by keyhash is not supported in simulation. This function is only available for query as yet") - } - metadataBytes, err := h.txmgr.db.GetPrivateDataMetadataByHash(ns, coll, keyhash) + dbItr, err := q.txmgr.db.ExecuteQueryOnPrivateData(ns, coll, query) if err != nil { return nil, err } - return statemetadata.Deserialize(metadataBytes) + return &pvtdataResultsItr{ns, coll, dbItr}, nil } -func (h *queryHelper) done() { - if h.doneInvoked { +// Done implements method in interface `ledger.QueryExecutor` +func (q *queryExecutor) Done() { + logger.Debugf("Done with transaction simulation / query execution [%s]", q.txid) + if q.doneInvoked { return } defer func() { - h.txmgr.commitRWLock.RUnlock() - h.doneInvoked = true - for _, itr := range h.itrs { + q.txmgr.commitRWLock.RUnlock() + q.doneInvoked = true + for _, itr := range q.itrs { itr.Close() } }() } -func (h *queryHelper) addRangeQueryInfo() { - for _, itr := range h.itrs { - if h.rwsetBuilder != nil { - results, hash, err := itr.rangeQueryResultsHelper.Done() - if err != nil { - h.err = err - return - } - if results != nil { - rwsetutil.SetRawReads(itr.rangeQueryInfo, results) - } - if hash != nil { - rwsetutil.SetMerkelSummary(itr.rangeQueryInfo, hash) - } - h.rwsetBuilder.AddToRangeQuerySet(itr.ns, itr.rangeQueryInfo) - } - } -} - -func (h *queryHelper) checkDone() error { - if h.doneInvoked { +func (q *queryExecutor) checkDone() error { + if q.doneInvoked { return errors.New("this instance should not be used after calling Done()") } return nil } -func (h *queryHelper) validateCollName(ns, coll string) error { - return h.collNameValidator.validateCollName(ns, coll) +func (q *queryExecutor) validateCollName(ns, coll string) error { + return q.collNameValidator.validateCollName(ns, coll) } // resultsItr implements interface ledger.ResultsIterator @@ -525,3 +556,23 @@ func (itr *pvtdataResultsItr) Next() (commonledger.QueryResult, error) { func (itr *pvtdataResultsItr) Close() { itr.dbItr.Close() } + +func (q *queryExecutor) addRangeQueryInfo() { + if !q.collectReadset { + return + } + for _, itr := range q.itrs { + results, hash, err := itr.rangeQueryResultsHelper.Done() + if err != nil { + q.err = err + return + } + if results != nil { + rwsetutil.SetRawReads(itr.rangeQueryInfo, results) + } + if hash != nil { + rwsetutil.SetMerkelSummary(itr.rangeQueryInfo, hash) + } + q.rwsetBuilder.AddToRangeQuerySet(itr.ns, itr.rangeQueryInfo) + } +} diff --git a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper_test.go b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/query_executor_test.go similarity index 90% rename from core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper_test.go rename to core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/query_executor_test.go index ed1bef48ac7..3bbc963e79b 100644 --- a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper_test.go +++ b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/query_executor_test.go @@ -51,13 +51,13 @@ func TestPvtdataResultsItr(t *testing.T) { txMgr.db.ApplyPrivacyAwareUpdates(updates, version.NewHeight(2, 7)) cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) assert.NoError(t, err) - queryHelper := newQueryHelper(txMgr, nil, true, cryptoProvider) + qe := newQueryExecutor(txMgr, "", nil, true, cryptoProvider) - resItr, err := queryHelper.getPrivateDataRangeScanIterator("ns1", "coll1", "key1", "key3") + resItr, err := qe.GetPrivateDataRangeScanIterator("ns1", "coll1", "key1", "key3") assert.NoError(t, err) testItr(t, resItr, "ns1", "coll1", []string{"key1", "key2"}) - resItr, err = queryHelper.getPrivateDataRangeScanIterator("ns4", "coll1", "key1", "key3") + resItr, err = qe.GetPrivateDataRangeScanIterator("ns4", "coll1", "key1", "key3") assert.NoError(t, err) testItr(t, resItr, "ns4", "coll1", []string{}) } @@ -111,8 +111,8 @@ func testPrivateDataMetadataRetrievalByHash(t *testing.T, env testEnv) { t.Run("query-helper-for-queryexecutor", func(t *testing.T) { cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) assert.NoError(t, err) - queryHelper := newQueryHelper(txMgr.(*LockBasedTxMgr), nil, true, cryptoProvider) - metadataRetrieved, err := queryHelper.getPrivateDataMetadataByHash("ns", "coll", util.ComputeStringHash("key1")) + qe := newQueryExecutor(txMgr.(*LockBasedTxMgr), "", nil, true, cryptoProvider) + metadataRetrieved, err := qe.GetPrivateDataMetadataByHash("ns", "coll", util.ComputeStringHash("key1")) assert.NoError(t, err) assert.Equal(t, metadata1, metadataRetrieved) }) @@ -120,8 +120,8 @@ func testPrivateDataMetadataRetrievalByHash(t *testing.T, env testEnv) { t.Run("query-helper-for-txsimulator", func(t *testing.T) { cryptoProvider, err := sw.NewDefaultSecurityLevelWithKeystore(sw.NewDummyKeyStore()) assert.NoError(t, err) - queryHelper := newQueryHelper(txMgr.(*LockBasedTxMgr), rwsetutil.NewRWSetBuilder(), true, cryptoProvider) - _, err = queryHelper.getPrivateDataMetadataByHash("ns", "coll", util.ComputeStringHash("key1")) + qe := newQueryExecutor(txMgr.(*LockBasedTxMgr), "txid-1", rwsetutil.NewRWSetBuilder(), true, cryptoProvider) + _, err = qe.GetPrivateDataMetadataByHash("ns", "coll", util.ComputeStringHash("key1")) assert.EqualError(t, err, "retrieving private data metadata by keyhash is not supported in simulation. This function is only available for query as yet") }) } @@ -154,7 +154,7 @@ func testGetPvtdataHash(t *testing.T, env testEnv) { assert.NoError(t, txMgr.db.ApplyPrivacyAwareUpdates(batch, version.NewHeight(1, 5))) s, _ := txMgr.NewTxSimulator("test_tx1") - simulator := s.(*lockBasedTxSimulator) + simulator := s.(*txSimulator) hash, err := simulator.GetPrivateDataHash("ns", "coll", "non-existing-key") assert.NoError(t, err) assert.Nil(t, hash) diff --git a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_tx_simulator.go b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/tx_simulator.go similarity index 60% rename from core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_tx_simulator.go rename to core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/tx_simulator.go index 1ba5cdb63c1..d6f12094f79 100644 --- a/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/lockbased_tx_simulator.go +++ b/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/tx_simulator.go @@ -16,9 +16,9 @@ import ( "github.com/pkg/errors" ) -// LockBasedTxSimulator is a transaction simulator used in `LockBasedTxMgr` -type lockBasedTxSimulator struct { - lockBasedQueryExecutor +// txSimulator is a transaction simulator used in `LockBasedTxMgr` +type txSimulator struct { + *queryExecutor rwsetBuilder *rwsetutil.RWSetBuilder writePerformed bool pvtdataQueriesPerformed bool @@ -26,15 +26,15 @@ type lockBasedTxSimulator struct { paginatedQueriesPerformed bool } -func newLockBasedTxSimulator(txmgr *LockBasedTxMgr, txid string, hasher ledger.Hasher) (*lockBasedTxSimulator, error) { +func newTxSimulator(txmgr *LockBasedTxMgr, txid string, hasher ledger.Hasher) (*txSimulator, error) { rwsetBuilder := rwsetutil.NewRWSetBuilder() - helper := newQueryHelper(txmgr, rwsetBuilder, true, hasher) + qe := newQueryExecutor(txmgr, txid, rwsetBuilder, true, hasher) logger.Debugf("constructing new tx simulator txid = [%s]", txid) - return &lockBasedTxSimulator{lockBasedQueryExecutor{helper, txid}, rwsetBuilder, false, false, false, false}, nil + return &txSimulator{qe, rwsetBuilder, false, false, false, false}, nil } // SetState implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) SetState(ns string, key string, value []byte) error { +func (s *txSimulator) SetState(ns string, key string, value []byte) error { if err := s.checkWritePrecondition(key, value); err != nil { return err } @@ -43,12 +43,12 @@ func (s *lockBasedTxSimulator) SetState(ns string, key string, value []byte) err } // DeleteState implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) DeleteState(ns string, key string) error { +func (s *txSimulator) DeleteState(ns string, key string) error { return s.SetState(ns, key, nil) } // SetStateMultipleKeys implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) SetStateMultipleKeys(namespace string, kvs map[string][]byte) error { +func (s *txSimulator) SetStateMultipleKeys(namespace string, kvs map[string][]byte) error { for k, v := range kvs { if err := s.SetState(namespace, k, v); err != nil { return err @@ -58,7 +58,7 @@ func (s *lockBasedTxSimulator) SetStateMultipleKeys(namespace string, kvs map[st } // SetStateMetadata implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) SetStateMetadata(namespace, key string, metadata map[string][]byte) error { +func (s *txSimulator) SetStateMetadata(namespace, key string, metadata map[string][]byte) error { if err := s.checkWritePrecondition(key, nil); err != nil { return err } @@ -67,13 +67,13 @@ func (s *lockBasedTxSimulator) SetStateMetadata(namespace, key string, metadata } // DeleteStateMetadata implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) DeleteStateMetadata(namespace, key string) error { +func (s *txSimulator) DeleteStateMetadata(namespace, key string) error { return s.SetStateMetadata(namespace, key, nil) } // SetPrivateData implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) SetPrivateData(ns, coll, key string, value []byte) error { - if err := s.helper.validateCollName(ns, coll); err != nil { +func (s *txSimulator) SetPrivateData(ns, coll, key string, value []byte) error { + if err := s.queryExecutor.validateCollName(ns, coll); err != nil { return err } if err := s.checkWritePrecondition(key, value); err != nil { @@ -85,12 +85,12 @@ func (s *lockBasedTxSimulator) SetPrivateData(ns, coll, key string, value []byte } // DeletePrivateData implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) DeletePrivateData(ns, coll, key string) error { +func (s *txSimulator) DeletePrivateData(ns, coll, key string) error { return s.SetPrivateData(ns, coll, key, nil) } // SetPrivateDataMultipleKeys implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) SetPrivateDataMultipleKeys(ns, coll string, kvs map[string][]byte) error { +func (s *txSimulator) SetPrivateDataMultipleKeys(ns, coll string, kvs map[string][]byte) error { for k, v := range kvs { if err := s.SetPrivateData(ns, coll, k, v); err != nil { return err @@ -100,16 +100,16 @@ func (s *lockBasedTxSimulator) SetPrivateDataMultipleKeys(ns, coll string, kvs m } // GetPrivateDataRangeScanIterator implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (commonledger.ResultsIterator, error) { +func (s *txSimulator) GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey string) (commonledger.ResultsIterator, error) { if err := s.checkBeforePvtdataQueries(); err != nil { return nil, err } - return s.lockBasedQueryExecutor.GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey) + return s.queryExecutor.GetPrivateDataRangeScanIterator(namespace, collection, startKey, endKey) } // SetPrivateDataMetadata implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) SetPrivateDataMetadata(namespace, collection, key string, metadata map[string][]byte) error { - if err := s.helper.validateCollName(namespace, collection); err != nil { +func (s *txSimulator) SetPrivateDataMetadata(namespace, collection, key string, metadata map[string][]byte) error { + if err := s.queryExecutor.validateCollName(namespace, collection); err != nil { return err } if err := s.checkWritePrecondition(key, nil); err != nil { @@ -120,56 +120,56 @@ func (s *lockBasedTxSimulator) SetPrivateDataMetadata(namespace, collection, key } // DeletePrivateMetadata implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) DeletePrivateDataMetadata(namespace, collection, key string) error { +func (s *txSimulator) DeletePrivateDataMetadata(namespace, collection, key string) error { return s.SetPrivateDataMetadata(namespace, collection, key, nil) } // ExecuteQueryOnPrivateData implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) ExecuteQueryOnPrivateData(namespace, collection, query string) (commonledger.ResultsIterator, error) { +func (s *txSimulator) ExecuteQueryOnPrivateData(namespace, collection, query string) (commonledger.ResultsIterator, error) { if err := s.checkBeforePvtdataQueries(); err != nil { return nil, err } - return s.lockBasedQueryExecutor.ExecuteQueryOnPrivateData(namespace, collection, query) + return s.queryExecutor.ExecuteQueryOnPrivateData(namespace, collection, query) } // GetStateRangeScanIteratorWithPagination implements method in interface `ledger.QueryExecutor` -func (s *lockBasedTxSimulator) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, +func (s *txSimulator) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (ledger.QueryResultsIterator, error) { if err := s.checkBeforePaginatedQueries(); err != nil { return nil, err } - return s.lockBasedQueryExecutor.GetStateRangeScanIteratorWithPagination(namespace, startKey, endKey, pageSize) + return s.queryExecutor.GetStateRangeScanIteratorWithPagination(namespace, startKey, endKey, pageSize) } // ExecuteQueryWithPagination implements method in interface `ledger.QueryExecutor` -func (s *lockBasedTxSimulator) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (ledger.QueryResultsIterator, error) { +func (s *txSimulator) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (ledger.QueryResultsIterator, error) { if err := s.checkBeforePaginatedQueries(); err != nil { return nil, err } - return s.lockBasedQueryExecutor.ExecuteQueryWithPagination(namespace, query, bookmark, pageSize) + return s.queryExecutor.ExecuteQueryWithPagination(namespace, query, bookmark, pageSize) } // GetTxSimulationResults implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) GetTxSimulationResults() (*ledger.TxSimulationResults, error) { +func (s *txSimulator) GetTxSimulationResults() (*ledger.TxSimulationResults, error) { if s.simulationResultsComputed { return nil, errors.New("this function should only be called once on a transaction simulator instance") } defer func() { s.simulationResultsComputed = true }() logger.Debugf("Simulation completed, getting simulation results") - if s.helper.err != nil { - return nil, s.helper.err + if s.queryExecutor.err != nil { + return nil, s.queryExecutor.err } - s.helper.addRangeQueryInfo() + s.queryExecutor.addRangeQueryInfo() return s.rwsetBuilder.GetTxSimulationResults() } // ExecuteUpdate implements method in interface `ledger.TxSimulator` -func (s *lockBasedTxSimulator) ExecuteUpdate(query string) error { +func (s *txSimulator) ExecuteUpdate(query string) error { return errors.New("not supported") } -func (s *lockBasedTxSimulator) checkWritePrecondition(key string, value []byte) error { - if err := s.helper.checkDone(); err != nil { +func (s *txSimulator) checkWritePrecondition(key string, value []byte) error { + if err := s.checkDone(); err != nil { return err } if err := s.checkPvtdataQueryPerformed(); err != nil { @@ -179,13 +179,13 @@ func (s *lockBasedTxSimulator) checkWritePrecondition(key string, value []byte) return err } s.writePerformed = true - if err := s.helper.txmgr.db.ValidateKeyValue(key, value); err != nil { + if err := s.queryExecutor.txmgr.db.ValidateKeyValue(key, value); err != nil { return err } return nil } -func (s *lockBasedTxSimulator) checkBeforePvtdataQueries() error { +func (s *txSimulator) checkBeforePvtdataQueries() error { if s.writePerformed { return &txmgr.ErrUnsupportedTransaction{ Msg: fmt.Sprintf("txid [%s]: Queries on pvt data is supported only in a read-only transaction", s.txid), @@ -195,7 +195,7 @@ func (s *lockBasedTxSimulator) checkBeforePvtdataQueries() error { return nil } -func (s *lockBasedTxSimulator) checkPvtdataQueryPerformed() error { +func (s *txSimulator) checkPvtdataQueryPerformed() error { if s.pvtdataQueriesPerformed { return &txmgr.ErrUnsupportedTransaction{ Msg: fmt.Sprintf("txid [%s]: Transaction has already performed queries on pvt data. Writes are not allowed", s.txid), @@ -204,7 +204,7 @@ func (s *lockBasedTxSimulator) checkPvtdataQueryPerformed() error { return nil } -func (s *lockBasedTxSimulator) checkBeforePaginatedQueries() error { +func (s *txSimulator) checkBeforePaginatedQueries() error { if s.writePerformed { return &txmgr.ErrUnsupportedTransaction{ Msg: fmt.Sprintf("txid [%s]: Paginated queries are supported only in a read-only transaction", s.txid), @@ -214,7 +214,7 @@ func (s *lockBasedTxSimulator) checkBeforePaginatedQueries() error { return nil } -func (s *lockBasedTxSimulator) checkPaginatedQueryPerformed() error { +func (s *txSimulator) checkPaginatedQueryPerformed() error { if s.paginatedQueriesPerformed { return &txmgr.ErrUnsupportedTransaction{ Msg: fmt.Sprintf("txid [%s]: Transaction has already performed a paginated query. Writes are not allowed", s.txid),