Skip to content

Commit

Permalink
[FAB-6938] Cannot create CouchDB database for channel
Browse files Browse the repository at this point in the history
In rocket.chat there was a post about an error:
when i am running join peer request some times it's giving
Error:file_exists, Status Code:412, Reason:The database could
not be created, the file already exists

I believe this can happen if peer is retrying a create database
request against CouchDB.  A prior attempt to create database came
back as a timeout failure to peer, but in the meantime the database
got created in CouchDB.  The next retry would get the error above.
This scenario should be handled in peer code.

Change-Id: I01ecd3a5fde8a9042f6a18181376dec267d0cc15
Signed-off-by: Chris Elder <chris.elder@us.ibm.com>
  • Loading branch information
Chris Elder committed Feb 5, 2018
1 parent 5185bad commit c0b6e8c
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 45 deletions.
66 changes: 38 additions & 28 deletions core/ledger/util/couchdb/couchdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,57 +259,67 @@ func CreateConnectionDefinition(couchDBAddress, username, password string, maxRe
}

//CreateDatabaseIfNotExist method provides function to create database
func (dbclient *CouchDatabase) CreateDatabaseIfNotExist() (*DBOperationResponse, error) {
func (dbclient *CouchDatabase) CreateDatabaseIfNotExist() error {

logger.Debugf("Entering CreateDatabaseIfNotExist()")

dbInfo, couchDBReturn, err := dbclient.GetDatabaseInfo()
if err != nil {
if couchDBReturn == nil || couchDBReturn.StatusCode != 404 {
return nil, err
return err
}
}

if dbInfo == nil && couchDBReturn.StatusCode == 404 {
//If the dbInfo returns populated and status code is 200, then the database exists
if dbInfo != nil && couchDBReturn.StatusCode == 200 {

logger.Debugf("Database %s does not exist.", dbclient.DBName)
logger.Debugf("Database %s already exists", dbclient.DBName)

connectURL, err := url.Parse(dbclient.CouchInstance.conf.URL)
if err != nil {
logger.Errorf("URL parse error: %s", err.Error())
return nil, err
}
connectURL.Path = dbclient.DBName
logger.Debugf("Exiting CreateDatabaseIfNotExist()")

//get the number of retries
maxRetries := dbclient.CouchInstance.conf.MaxRetries
return nil
}

//process the URL with a PUT, creates the database
resp, _, err := dbclient.CouchInstance.handleRequest(http.MethodPut, connectURL.String(), nil, "", "", maxRetries, true)
if err != nil {
return nil, err
}
defer closeResponseBody(resp)
logger.Debugf("Database %s does not exist.", dbclient.DBName)

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

//Get the response from the create REST call
dbResponse := &DBOperationResponse{}
json.NewDecoder(resp.Body).Decode(&dbResponse)
//get the number of retries
maxRetries := dbclient.CouchInstance.conf.MaxRetries

if dbResponse.Ok == true {
logger.Infof("Created state database %s", dbclient.DBName)
}
//process the URL with a PUT, creates the database
resp, _, err := dbclient.CouchInstance.handleRequest(http.MethodPut, connectURL.String(), nil, "", "", maxRetries, true)

logger.Debugf("Exiting CreateDatabaseIfNotExist()")
if err != nil {

return dbResponse, nil
// Check to see if the database exists
// Even though handleRequest() returned an error, the
// database may have been created and a false error
// returned due to a timeout or race condition.
// Do a final check to see if the database really got created.
dbInfo, couchDBReturn, errDbInfo := dbclient.GetDatabaseInfo()
//If there is no error, then the database exists, return without an error
if errDbInfo == nil && dbInfo != nil && couchDBReturn.StatusCode == 200 {
logger.Infof("Created state database %s", dbclient.DBName)
logger.Debugf("Exiting CreateDatabaseIfNotExist()")
return nil
}

return err
}

logger.Debugf("Database %s already exists", dbclient.DBName)
defer closeResponseBody(resp)

logger.Infof("Created state database %s", dbclient.DBName)

logger.Debugf("Exiting CreateDatabaseIfNotExist()")

return nil, nil
return nil

}

Expand Down
26 changes: 13 additions & 13 deletions core/ledger/util/couchdb/couchdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func TestBadCouchDBInstance(t *testing.T) {
testutil.AssertError(t, err, "Error should have been thrown with CreateSystemDatabasesIfNotExist and invalid connection")

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

//Test GetDatabaseInfo with bad connection
Expand Down Expand Up @@ -242,7 +242,7 @@ func TestDBCreateSaveWithoutRevision(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Save the test document
Expand All @@ -269,7 +269,7 @@ func TestDBCreateEnsureFullCommit(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Save the test document
Expand Down Expand Up @@ -372,7 +372,7 @@ func TestDBCreateDatabaseAndPersist(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Retrieve the info for the new database and make sure the name matches
Expand Down Expand Up @@ -624,7 +624,7 @@ func TestDBTimeoutConflictRetry(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Retrieve the info for the new database and make sure the name matches
Expand Down Expand Up @@ -691,7 +691,7 @@ func TestDBBadJSON(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Retrieve the info for the new database and make sure the name matches
Expand Down Expand Up @@ -728,7 +728,7 @@ func TestPrefixScan(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Retrieve the info for the new database and make sure the name matches
Expand Down Expand Up @@ -802,7 +802,7 @@ func TestDBSaveAttachment(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Save the test document
Expand Down Expand Up @@ -836,7 +836,7 @@ func TestDBDeleteDocument(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Save the test document
Expand Down Expand Up @@ -875,7 +875,7 @@ func TestDBDeleteNonExistingDocument(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Save the test document
Expand Down Expand Up @@ -926,7 +926,7 @@ func TestIndexOperations(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

batchUpdateDocs := []*CouchDoc{}
Expand Down Expand Up @@ -1175,7 +1175,7 @@ func TestRichQuery(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

//Save the test document
Expand Down Expand Up @@ -1404,7 +1404,7 @@ func TestBatchBatchOperations(t *testing.T) {
db := CouchDatabase{CouchInstance: *couchInstance, DBName: database}

//create a new database
_, errdb := db.CreateDatabaseIfNotExist()
errdb := db.CreateDatabaseIfNotExist()
testutil.AssertNoError(t, errdb, fmt.Sprintf("Error when trying to create database"))

batchUpdateDocs := []*CouchDoc{}
Expand Down
8 changes: 4 additions & 4 deletions core/ledger/util/couchdb/couchdbutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func CreateCouchDatabase(couchInstance CouchInstance, dbName string) (*CouchData
couchDBDatabase := CouchDatabase{CouchInstance: couchInstance, DBName: databaseName}

// Create CouchDB database upon ledger startup, if it doesn't already exist
_, err = couchDBDatabase.CreateDatabaseIfNotExist()
err = couchDBDatabase.CreateDatabaseIfNotExist()
if err != nil {
logger.Errorf("Error during CouchDB CreateDatabaseIfNotExist() for dbName: %s error: %s\n", dbName, err.Error())
return nil, err
Expand All @@ -123,23 +123,23 @@ func CreateSystemDatabasesIfNotExist(couchInstance CouchInstance) error {

dbName := "_users"
systemCouchDBDatabase := CouchDatabase{CouchInstance: couchInstance, DBName: dbName}
_, err := systemCouchDBDatabase.CreateDatabaseIfNotExist()
err := systemCouchDBDatabase.CreateDatabaseIfNotExist()
if err != nil {
logger.Errorf("Error during CouchDB CreateDatabaseIfNotExist() for system dbName: %s error: %s\n", dbName, err.Error())
return err
}

dbName = "_replicator"
systemCouchDBDatabase = CouchDatabase{CouchInstance: couchInstance, DBName: dbName}
_, err = systemCouchDBDatabase.CreateDatabaseIfNotExist()
err = systemCouchDBDatabase.CreateDatabaseIfNotExist()
if err != nil {
logger.Errorf("Error during CouchDB CreateDatabaseIfNotExist() for system dbName: %s error: %s\n", dbName, err.Error())
return err
}

dbName = "_global_changes"
systemCouchDBDatabase = CouchDatabase{CouchInstance: couchInstance, DBName: dbName}
_, err = systemCouchDBDatabase.CreateDatabaseIfNotExist()
err = systemCouchDBDatabase.CreateDatabaseIfNotExist()
if err != nil {
logger.Errorf("Error during CouchDB CreateDatabaseIfNotExist() for system dbName: %s error: %s\n", dbName, err.Error())
return err
Expand Down

0 comments on commit c0b6e8c

Please sign in to comment.