Skip to content

Commit

Permalink
Use retry mechanism when initializing configuration (minio#6475)
Browse files Browse the repository at this point in the history
Currently, one node in a cluster can fail to boot with the following error message:

```
ERROR Unable to initialize config system: Storage resources are insufficient for the write operation
```

  This happens when disks are formatted, read quorum is met but write
quorum is not met. In checkServerConfig(), a insufficient read quorum
error is replaced by errConfigNotFound, the code will generate a
new config json and try to save it, but it will fail because write
quorum is not met.

  Replacing read quorum with errConfigNotFound is also wrong because it
can lead, in rare cases, to overwrite the config set by the user.

  So, this commit adds a retry mechanism in configuration initialization
to retry only with read or write quorum errors.

  This commit will also fix the following cases:
 - Read quorum is lost just after the initialization of the object layer.
 - Write quorum not met when upgrading configuration version.
  • Loading branch information
vadmeste authored and harshavardhana committed Sep 16, 2018
1 parent a63bc92 commit 66fda7a
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
35 changes: 30 additions & 5 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ func checkServerConfig(ctx context.Context, objAPI ObjectLayer) error {
}

if _, err := objAPI.GetObjectInfo(ctx, minioMetaBucket, configFile, ObjectOptions{}); err != nil {
// Convert ObjectNotFound, Quorum errors into errConfigNotFound
if isErrObjectNotFound(err) || isInsufficientReadQuorum(err) {
// Convert ObjectNotFound to errConfigNotFound
if isErrObjectNotFound(err) {
return errConfigNotFound
}
logger.GetReqInfo(ctx).AppendTags("configFile", configFile)
Expand All @@ -187,8 +187,8 @@ func readConfig(ctx context.Context, objAPI ObjectLayer, configFile string) (*by
var buffer bytes.Buffer
// Read entire content by setting size to -1
if err := objAPI.GetObject(ctx, minioMetaBucket, configFile, 0, -1, &buffer, "", ObjectOptions{}); err != nil {
// Convert ObjectNotFound, IncompleteBody and Quorum errors into errConfigNotFound
if isErrObjectNotFound(err) || isErrIncompleteBody(err) || isInsufficientReadQuorum(err) {
// Convert ObjectNotFound and IncompleteBody errors into errConfigNotFound
if isErrObjectNotFound(err) || isErrIncompleteBody(err) {
return nil, errConfigNotFound
}

Expand Down Expand Up @@ -218,7 +218,32 @@ func (sys *ConfigSys) Init(objAPI ObjectLayer) error {
if objAPI == nil {
return errInvalidArgument
}
return initConfig(objAPI)

doneCh := make(chan struct{})
defer close(doneCh)

// Initializing configuration needs a retry mechanism for
// the following reasons:
// - Read quorum is lost just after the initialization
// of the object layer.
// - Write quorum not met when upgrading configuration
// version is needed.
retryTimerCh := newRetryTimerSimple(doneCh)
for {
select {
case _ = <-retryTimerCh:
err := initConfig(objAPI)
if err != nil {
if isInsufficientReadQuorum(err) || isInsufficientWriteQuorum(err) {
logger.Info("Waiting for configuration to be initialized..")
continue
}
return err
}

return nil
}
}
}

// NewConfigSys - creates new config system object.
Expand Down
6 changes: 6 additions & 0 deletions cmd/object-api-errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,9 @@ func isInsufficientReadQuorum(err error) bool {
_, ok := err.(InsufficientReadQuorum)
return ok
}

// isInsufficientWriteQuorum - Check if error type is InsufficientWriteQuorum.
func isInsufficientWriteQuorum(err error) bool {
_, ok := err.(InsufficientWriteQuorum)
return ok
}

0 comments on commit 66fda7a

Please sign in to comment.