Skip to content

Commit

Permalink
Merge "FAB-2531 Range queries fail iterating beyond 100 items"
Browse files Browse the repository at this point in the history
  • Loading branch information
Srinivasan Muralidharan authored and Gerrit Code Review committed Mar 8, 2017
2 parents 1f5bc46 + 0fc6c4d commit babdeee
Show file tree
Hide file tree
Showing 10 changed files with 402 additions and 98 deletions.
54 changes: 16 additions & 38 deletions core/chaincode/chaincodetest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ chaincode:
lccc: enable
escc: enable
vscc: enable

###############################################################################
#
# Ledger section - ledger configuration encompases both the blockchain
Expand All @@ -402,45 +403,22 @@ ledger:

blockchain:

# Setting the deploy-system-chaincode property to false will prevent the
# deploying of system chaincode at genesis time.
deploy-system-chaincode: false

state:

# Control the number state deltas that are maintained. This takes additional
# disk space, but allow the state to be rolled backwards and forwards
# without the need to replay transactions.
deltaHistorySize: 500

# The data structure in which the state will be stored. Different data
# structures may offer different performance characteristics.
# Options are 'buckettree', 'trie' and 'raw'.
# ( Note:'raw' is experimental and incomplete. )
# If not set, the default data structure is the 'buckettree'.
# This CANNOT be changed after the DB has been created.
dataStructure:
# The name of the data structure is for storing the state
name: buckettree
# The data structure specific configurations
configs:
# configurations for 'bucketree'. These CANNOT be changed after the DB
# has been created. 'numBuckets' defines the number of bins that the
# state key-values are to be divided
numBuckets: 1000003
# 'maxGroupingAtEachLevel' defines the number of bins that are grouped
#together to construct next level of the merkle-tree (this is applied
# repeatedly for constructing the entire tree).
maxGroupingAtEachLevel: 5
# 'bucketCacheSize' defines the size (in MBs) of the cache that is used to keep
# the buckets (from root upto secondlast level) in memory. This cache helps
# in making state hash computation faster. A value less than or equals to zero
# leads to disabling this caching. This caching helps more if transactions
# perform significant writes.
bucketCacheSize: 100

# configurations for 'trie'
# 'tire' has no additional configurations exposed as yet
# stateDatabase - options are "goleveldb", "CouchDB"
# goleveldb - default state database stored in goleveldb.
# CouchDB - store state database in CouchDB
stateDatabase: goleveldb
couchDBConfig:
couchDBAddress: 127.0.0.1:5984
username:
password:

# historyDatabase - options are true or false
# Indicates if the history of key updates should be stored in goleveldb
historyDatabase: true

# Limit on the number of records to return per query
queryLimit: 10000


################################################################################
Expand Down
236 changes: 228 additions & 8 deletions core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package chaincode

import (
"encoding/json"
"fmt"
"net"
"os"
Expand All @@ -35,6 +36,7 @@ import (
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/ledger/ledgerconfig"
"github.com/hyperledger/fabric/core/ledger/ledgermgmt"
"github.com/hyperledger/fabric/core/ledger/util/couchdb"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/core/scc"
"github.com/hyperledger/fabric/msp"
Expand Down Expand Up @@ -112,6 +114,22 @@ func finitPeer(lis net.Listener, chainIDs ...string) {
ledgerPath := viper.GetString("peer.fileSystemPath")
os.RemoveAll(ledgerPath)
os.RemoveAll(filepath.Join(os.TempDir(), "hyperledger"))

//if couchdb is enabled, then cleanup the test couchdb
if ledgerconfig.IsCouchDBEnabled() == true {

chainID := util.GetTestChainID()

connectURL := viper.GetString("ledger.state.couchDBConfig.couchDBAddress")
username := viper.GetString("ledger.state.couchDBConfig.username")
password := viper.GetString("ledger.state.couchDBConfig.password")

couchInstance, _ := couchdb.CreateCouchInstance(connectURL, username, password)
db, _ := couchdb.CreateCouchDatabase(*couchInstance, chainID)
//drop the test database
db.DropDatabase()

}
}

func startTxSimulation(ctxt context.Context, chainID string) (context.Context, ledger.TxSimulator, error) {
Expand Down Expand Up @@ -911,9 +929,10 @@ func TestQueries(t *testing.T) {
return
}

// Invoke second chaincode, which will inturn invoke the first chaincode
// Add 12 marbles for testing range queries and rich queries (for capable ledgers)
// The tests will test both range and rich queries and queries with query limits
f = "put"
args = util.ToChaincodeArgs(f, "key1", "{\"shipmentID\":\"161003PKC7300\",\"customsInvoice\":{\"methodOfTransport\":\"GROUND\",\"invoiceNumber\":\"00091622\"},\"weightUnitOfMeasure\":\"KGM\",\"volumeUnitOfMeasure\": \"CO\",\"dimensionUnitOfMeasure\":\"CM\",\"currency\":\"USD\"}")
args = util.ToChaincodeArgs(f, "marble01", "{\"docType\":\"marble\",\"name\":\"marble01\",\"color\":\"blue\",\"size\":35,\"owner\":\"tom\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
Expand All @@ -926,7 +945,7 @@ func TestQueries(t *testing.T) {
}

f = "put"
args = util.ToChaincodeArgs(f, "key2", "{\"shipmentID\":\"161003PKC7300\",\"customsInvoice\":{\"methodOfTransport\":\"GROUND\",\"invoiceNumber\":\"00091622\"},\"weightUnitOfMeasure\":\"KGM\",\"volumeUnitOfMeasure\": \"CO\",\"dimensionUnitOfMeasure\":\"CM\",\"currency\":\"USD\"}")
args = util.ToChaincodeArgs(f, "marble02", "{\"docType\":\"marble\",\"name\":\"marble02\",\"color\":\"red\",\"size\":25,\"owner\":\"tom\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
Expand All @@ -939,7 +958,7 @@ func TestQueries(t *testing.T) {
}

f = "put"
args = util.ToChaincodeArgs(f, "key3", "{\"shipmentID\":\"161003PKC7300\",\"customsInvoice\":{\"methodOfTransport\":\"GROUND\",\"invoiceNumber\":\"00091622\"},\"weightUnitOfMeasure\":\"KGM\",\"volumeUnitOfMeasure\": \"CO\",\"dimensionUnitOfMeasure\":\"CM\",\"currency\":\"USD\"}")
args = util.ToChaincodeArgs(f, "marble03", "{\"docType\":\"marble\",\"name\":\"marble03\",\"color\":\"green\",\"size\":15,\"owner\":\"tom\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
Expand All @@ -950,9 +969,92 @@ func TestQueries(t *testing.T) {
return
}

f = "keys"
args = util.ToChaincodeArgs(f, "key0", "key3")
f = "put"
args = util.ToChaincodeArgs(f, "marble04", "{\"docType\":\"marble\",\"name\":\"marble04\",\"color\":\"green\",\"size\":20,\"owner\":\"jerry\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

f = "put"
args = util.ToChaincodeArgs(f, "marble05", "{\"docType\":\"marble\",\"name\":\"marble05\",\"color\":\"red\",\"size\":25,\"owner\":\"jerry\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

f = "put"
args = util.ToChaincodeArgs(f, "marble06", "{\"docType\":\"marble\",\"name\":\"marble06\",\"color\":\"blue\",\"size\":35,\"owner\":\"jerry\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

f = "put"
args = util.ToChaincodeArgs(f, "marble07", "{\"docType\":\"marble\",\"name\":\"marble07\",\"color\":\"yellow\",\"size\":20,\"owner\":\"jerry\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

f = "put"
args = util.ToChaincodeArgs(f, "marble08", "{\"docType\":\"marble\",\"name\":\"marble08\",\"color\":\"green\",\"size\":40,\"owner\":\"jerry\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

f = "put"
args = util.ToChaincodeArgs(f, "marble09", "{\"docType\":\"marble\",\"name\":\"marble09\",\"color\":\"yellow\",\"size\":10,\"owner\":\"jerry\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

f = "put"
args = util.ToChaincodeArgs(f, "marble10", "{\"docType\":\"marble\",\"name\":\"marble10\",\"color\":\"red\",\"size\":20,\"owner\":\"jerry\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

f = "put"
args = util.ToChaincodeArgs(f, "marble11", "{\"docType\":\"marble\",\"name\":\"marble11\",\"color\":\"green\",\"size\":40,\"owner\":\"jerry\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
Expand All @@ -963,19 +1065,137 @@ func TestQueries(t *testing.T) {
return
}

f = "put"
args = util.ToChaincodeArgs(f, "marble12", "{\"docType\":\"marble\",\"name\":\"marble12\",\"color\":\"red\",\"size\":30,\"owner\":\"jerry\"}")
spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

//TODO - the following query tests for queryLimits may change due to future designs
// for batch "paging"

//The following range query for "marble01" to "marble11" should return 10 marbles
f = "keys"
args = util.ToChaincodeArgs(f, "marble01", "marble11")

spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, retval, err := invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

var keys []interface{}
err = json.Unmarshal(retval, &keys)

//default query limit of 10000 is used, query should return all records that meet the criteria
if len(keys) != 10 {
t.Fail()
t.Logf("Error detected with the range query, should have returned 10 but returned %v", len(keys))
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

//Reset the query limit to 5
viper.Set("ledger.state.queryLimit", 5)

//The following range query for "marble01" to "marble11" should return 5 marbles due to the queryLimit
f = "keys"
args = util.ToChaincodeArgs(f, "marble01", "marble11")

spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, retval, err = invoke(ctxt, chainID, spec, nextBlockNumber)

nextBlockNumber++
if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

//unmarshal the results
err = json.Unmarshal(retval, &keys)

//check to see if there are 5 values
if len(keys) != 5 {
t.Fail()
t.Logf("Error detected with the range query, should have returned 5 but returned %v", len(keys))
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

//Reset the query limit to default
viper.Set("ledger.state.queryLimit", 10000)

if ledgerconfig.IsCouchDBEnabled() == true {

//The following rich query for should return 9 marbles
f = "query"
args = util.ToChaincodeArgs(f, "{\"selector\":{\"owner\":\"jerry\"}}")

spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, retval, err = invoke(ctxt, chainID, spec, nextBlockNumber)
nextBlockNumber++

if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

//unmarshal the results
err = json.Unmarshal(retval, &keys)

//check to see if there are 9 values
//default query limit of 10000 is used, this query is effectively unlimited
if len(keys) != 9 {
t.Fail()
t.Logf("Error detected with the rich query, should have returned 9 but returned %v", len(keys))
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

//Reset the query limit to 5
viper.Set("ledger.state.queryLimit", 5)

//The following rich query should return 5 marbles due to the queryLimit
f = "query"
args = util.ToChaincodeArgs(f, "{\"selector\":{\"currency\":\"USD\"}}")
args = util.ToChaincodeArgs(f, "{\"selector\":{\"owner\":\"jerry\"}}")

spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}
_, _, _, err = invoke(ctxt, chainID, spec, nextBlockNumber)
_, _, retval, err = invoke(ctxt, chainID, spec, nextBlockNumber)

if err != nil {
t.Fail()
t.Logf("Error invoking <%s>: %s", ccID, err)
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

//unmarshal the results
err = json.Unmarshal(retval, &keys)

//check to see if there are 5 values
if len(keys) != 5 {
t.Fail()
t.Logf("Error detected with the rich query, should have returned 5 but returned %v", len(keys))
theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
return
}

}

theChaincodeSupport.Stop(ctxt, cccid, &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec})
}

Expand Down
Loading

0 comments on commit babdeee

Please sign in to comment.