Skip to content

Commit

Permalink
[FAB-8336] Add create or update msg for CouchDB index
Browse files Browse the repository at this point in the history
Problem Statement:

If the provided index definitions have more than one (lets say N)
index with same DesignDoc and indexName but with potentially different indexes,
the log will contain N messages like `created CouchDB index in the channel...`
but in reality only 1 index will be created in CouchDB. Fabric goes through
each file in alphabetic order, creating and (quietly) replacing the index
created in last round with the same DDoc and indexName, so only the last index
remains in CouchDB.

Solution:

Part of the confusion can be prevented by updating the message when an index
is created or updated in CouchDB.  The create index command responds with
metadata that can be captured and provide a more detailed message and can
determine if the index has been created or updated.  This can be accomplished
with the existing http request.

Change-Id: I84674ceed54dadba9470d768a028a6acb3b237f6
Signed-off-by: Chris Elder <chris.elder@us.ibm.com>
  • Loading branch information
Chris Elder authored and denyeart committed Feb 21, 2018
1 parent 6860525 commit 7c96350
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ func (vdb *VersionedDB) HandleChaincodeDeploy(chaincodeDefinition *cceventmgmt.C
return nil
}
//create the index from the tar entry
if err := db.CreateIndex(string(indexData)); err != nil {
if _, err := db.CreateIndex(string(indexData)); err != nil {
logger.Errorf("Error during creation of index from file=[%s] for chaincode=[%s] on chain=[%s]. Error=%s",
tarHeader.Name, chaincodeDefinition, vdb.chainName, err)
}
Expand Down
46 changes: 40 additions & 6 deletions core/ledger/util/couchdb/couchdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ type DBReturn struct {
Reason string `json:"reason"`
}

//CreateIndexResponse contains an the index creation response from CouchDB
type CreateIndexResponse struct {
Result string `json:"result"`
ID string `json:"id"`
Name string `json:"name"`
}

//AttachmentInfo contains the definition for an attached file for couchdb
type AttachmentInfo struct {
Name string
Expand Down Expand Up @@ -1134,19 +1141,19 @@ func (dbclient *CouchDatabase) ListIndex() (*[]IndexResult, error) {
}

// CreateIndex method provides a function creating an index
func (dbclient *CouchDatabase) CreateIndex(indexdefinition string) error {
func (dbclient *CouchDatabase) CreateIndex(indexdefinition string) (*CreateIndexResponse, error) {

logger.Debugf("Entering CreateIndex() indexdefinition=%s", indexdefinition)

//Test to see if this is a valid JSON
if IsJSON(indexdefinition) != true {
return fmt.Errorf("JSON format is not valid")
return nil, fmt.Errorf("JSON format is not valid")
}

indexURL, err := url.Parse(dbclient.CouchInstance.conf.URL)
if err != nil {
logger.Errorf("URL parse error: %s", err.Error())
return err
return nil, err
}

indexURL.Path = dbclient.DBName + "/_index"
Expand All @@ -1156,14 +1163,41 @@ func (dbclient *CouchDatabase) CreateIndex(indexdefinition string) error {

resp, _, err := dbclient.CouchInstance.handleRequest(http.MethodPost, indexURL.String(), []byte(indexdefinition), "", "", maxRetries, true)
if err != nil {
return err
return nil, err
}
defer closeResponseBody(resp)

logger.Infof("Created CouchDB index in state database %s", dbclient.DBName)
if resp == nil {
return nil, fmt.Errorf("An invalid response was received from CouchDB")
}

return nil
//Read the response body
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}

couchDBReturn := &CreateIndexResponse{}

jsonBytes := []byte(respBody)

//unmarshal the response
err = json.Unmarshal(jsonBytes, &couchDBReturn)
if err != nil {
return nil, err
}

if couchDBReturn.Result == "created" {

logger.Infof("Created CouchDB index [%s] in state database [%s] using design document [%s]", couchDBReturn.Name, dbclient.DBName, couchDBReturn.ID)

return couchDBReturn, nil

}

logger.Infof("Updated CouchDB index [%s] in state database [%s] using design document [%s]", couchDBReturn.Name, dbclient.DBName, couchDBReturn.ID)

return couchDBReturn, nil
}

// DeleteIndex method provides a function deleting an index
Expand Down
27 changes: 20 additions & 7 deletions core/ledger/util/couchdb/couchdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func TestBadCouchDBInstance(t *testing.T) {
testutil.AssertError(t, err, "Error should have been thrown with ListIndex and invalid connection")

//Test CreateIndex with bad connection
err = badDB.CreateIndex("")
_, err = badDB.CreateIndex("")
testutil.AssertError(t, err, "Error should have been thrown with CreateIndex and invalid connection")

//Test DeleteIndex with bad connection
Expand Down Expand Up @@ -964,7 +964,7 @@ func TestIndexOperations(t *testing.T) {
indexDefSize := "{\"index\":{\"fields\":[{\"size\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortName\",\"type\":\"json\"}"

//Create the index
err = db.CreateIndex(indexDefSize)
_, err = db.CreateIndex(indexDefSize)
testutil.AssertNoError(t, err, fmt.Sprintf("Error thrown while creating an index"))

//Retrieve the list of indexes
Expand All @@ -988,7 +988,7 @@ func TestIndexOperations(t *testing.T) {
indexDefColor := "{\"index\":{\"fields\":[{\"color\":\"desc\"}]}}"

//Create the index
err = db.CreateIndex(indexDefColor)
_, err = db.CreateIndex(indexDefColor)
testutil.AssertNoError(t, err, fmt.Sprintf("Error thrown while creating an index"))

//Retrieve the list of indexes
Expand Down Expand Up @@ -1034,7 +1034,7 @@ func TestIndexOperations(t *testing.T) {
testutil.AssertError(t, err, fmt.Sprintf("Error thrown while querying without a valid index"))

//Create the index
err = db.CreateIndex(indexDefSize)
_, err = db.CreateIndex(indexDefSize)
testutil.AssertNoError(t, err, fmt.Sprintf("Error thrown while creating an index"))

//Delay for 100ms since CouchDB index list is updated async after index create/drop
Expand All @@ -1048,9 +1048,22 @@ func TestIndexOperations(t *testing.T) {
indexDefSize = "{\"index\":{\"fields\":[{\"data.size\":\"desc\"},{\"data.owner\":\"desc\"}]},\"ddoc\":\"indexSizeOwnerSortDoc\", \"name\":\"indexSizeOwnerSortName\",\"type\":\"json\"}"

//Create the index
err = db.CreateIndex(indexDefSize)
dbResp, err := db.CreateIndex(indexDefSize)
testutil.AssertNoError(t, err, fmt.Sprintf("Error thrown while creating an index"))

//verify the response is "created" for an index creation
testutil.AssertEquals(t, dbResp.Result, "created")

//Delay for 100ms since CouchDB index list is updated async after index create/drop
time.Sleep(100 * time.Millisecond)

//Update the index
dbResp, err = db.CreateIndex(indexDefSize)
testutil.AssertNoError(t, err, fmt.Sprintf("Error thrown while creating an index"))

//verify the response is "exists" for an update
testutil.AssertEquals(t, dbResp.Result, "exists")

//Retrieve the list of indexes
//Delay for 100ms since CouchDB index list is updated async after index create/drop
time.Sleep(100 * time.Millisecond)
Expand All @@ -1064,14 +1077,14 @@ func TestIndexOperations(t *testing.T) {
indexDefSize = "{\"index\"{\"fields\":[{\"data.size\":\"desc\"},{\"data.owner\":\"desc\"}]},\"ddoc\":\"indexSizeOwnerSortDoc\", \"name\":\"indexSizeOwnerSortName\",\"type\":\"json\"}"

//Create the index
err = db.CreateIndex(indexDefSize)
_, err = db.CreateIndex(indexDefSize)
testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for an invalid index JSON"))

//Create an invalid index definition with a valid JSON and an invalid index definition
indexDefSize = "{\"index\":{\"fields2\":[{\"data.size\":\"desc\"},{\"data.owner\":\"desc\"}]},\"ddoc\":\"indexSizeOwnerSortDoc\", \"name\":\"indexSizeOwnerSortName\",\"type\":\"json\"}"

//Create the index
err = db.CreateIndex(indexDefSize)
_, err = db.CreateIndex(indexDefSize)
testutil.AssertError(t, err, fmt.Sprintf("Error should have been thrown for an invalid index definition"))

}
Expand Down

0 comments on commit 7c96350

Please sign in to comment.