Improve delete performance by reducing the number of calls (#9092)

- Remove the requirement to honor storage class for deletes
- Improve `posix.DeleteFileBulk` code to Stat the volumeDir
  only once per call, rather than for all object paths.
master
Harshavardhana 5 years ago committed by GitHub
parent 23a0415eb7
commit 88ae0f1196
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 33
      cmd/posix.go
  2. 49
      cmd/xl-v1-object.go

@ -1340,9 +1340,40 @@ func (s *posix) DeleteFile(volume, path string) (err error) {
} }
func (s *posix) DeleteFileBulk(volume string, paths []string) (errs []error, err error) { func (s *posix) DeleteFileBulk(volume string, paths []string) (errs []error, err error) {
atomic.AddInt32(&s.activeIOCount, 1)
defer func() {
atomic.AddInt32(&s.activeIOCount, -1)
}()
volumeDir, err := s.getVolDir(volume)
if err != nil {
return nil, err
}
// Stat a volume entry.
_, err = os.Stat(volumeDir)
if err != nil {
if os.IsNotExist(err) {
return nil, errVolumeNotFound
} else if os.IsPermission(err) {
return nil, errVolumeAccessDenied
} else if isSysErrIO(err) {
return nil, errFaultyDisk
}
return nil, err
}
errs = make([]error, len(paths)) errs = make([]error, len(paths))
// Following code is needed so that we retain SlashSeparator
// suffix if any in path argument.
for idx, path := range paths { for idx, path := range paths {
errs[idx] = s.DeleteFile(volume, path) filePath := pathJoin(volumeDir, path)
errs[idx] = checkPathLength(filePath)
if errs[idx] != nil {
continue
}
// Delete file and delete parent directory as well if its empty.
errs[idx] = deleteFile(volumeDir, filePath)
} }
return return
} }

@ -747,32 +747,37 @@ func (xl xlObjects) deleteObject(ctx context.Context, bucket, object string, wri
// object. // object.
func (xl xlObjects) doDeleteObjects(ctx context.Context, bucket string, objects []string, errs []error, writeQuorums []int, isDirs []bool) ([]error, error) { func (xl xlObjects) doDeleteObjects(ctx context.Context, bucket string, objects []string, errs []error, writeQuorums []int, isDirs []bool) ([]error, error) {
var tmpObjs = make([]string, len(objects)) var tmpObjs = make([]string, len(objects))
disks := xl.getDisks()
if bucket == minioMetaTmpBucket { if bucket == minioMetaTmpBucket {
copy(tmpObjs, objects) copy(tmpObjs, objects)
} else { } else {
for i, object := range objects { for idx := range objects {
if errs[i] != nil { if errs[idx] != nil {
continue continue
} }
tmpObjs[idx] = mustGetUUID()
var err error var err error
tmpObjs[i] = mustGetUUID() // Rename the current object while requiring
// Rename the current object while requiring write quorum, but also consider // write quorum, but also consider that a non
// that a non found object in a given disk as a success since it already // found object in a given disk as a success
// confirms that the object doesn't have a part in that disk (already removed) // since it already confirms that the object
if isDirs[i] { // doesn't have a part in that disk (already removed)
disks, err = rename(ctx, disks, bucket, object, minioMetaTmpBucket, tmpObjs[i], true, writeQuorums[i], if isDirs[idx] {
_, err = rename(ctx, xl.getDisks(), bucket, objects[idx],
minioMetaTmpBucket, tmpObjs[idx], true, writeQuorums[idx],
[]error{errFileNotFound, errFileAccessDenied}) []error{errFileNotFound, errFileAccessDenied})
} else { } else {
disks, err = rename(ctx, disks, bucket, object, minioMetaTmpBucket, tmpObjs[i], true, writeQuorums[i], _, err = rename(ctx, xl.getDisks(), bucket, objects[idx],
minioMetaTmpBucket, tmpObjs[idx], true, writeQuorums[idx],
[]error{errFileNotFound}) []error{errFileNotFound})
} }
if err != nil { if err != nil {
errs[i] = err errs[idx] = err
} }
} }
} }
disks := xl.getDisks()
// Initialize list of errors. // Initialize list of errors.
var opErrs = make([]error, len(disks)) var opErrs = make([]error, len(disks))
var delObjErrs = make([][]error, len(disks)) var delObjErrs = make([][]error, len(disks))
@ -840,23 +845,17 @@ func (xl xlObjects) deleteObjects(ctx context.Context, bucket string, objects []
} }
} }
for i, object := range objects { for i := range objects {
if errs[i] != nil { if errs[i] != nil {
continue continue
} }
if isObjectDirs[i] { // Assume (N/2 + 1) quorums for all objects
writeQuorums[i] = len(xl.getDisks())/2 + 1 // this is a theoretical assumption such that
} else { // for delete's we do not need to honor storage
var err error // class for objects which have reduced quorum
// Read metadata associated with the object from all disks. // storage class only needs to be honored for
partsMetadata, readXLErrs := readAllXLMetadata(ctx, xl.getDisks(), bucket, object) // Read() requests alone which we already do.
// get Quorum for this object writeQuorums[i] = len(xl.getDisks())/2 + 1
_, writeQuorums[i], err = objectQuorumFromMeta(ctx, xl, partsMetadata, readXLErrs)
if err != nil {
errs[i] = toObjectErr(err, bucket, object)
continue
}
}
} }
return xl.doDeleteObjects(ctx, bucket, objects, errs, writeQuorums, isObjectDirs) return xl.doDeleteObjects(ctx, bucket, objects, errs, writeQuorums, isObjectDirs)

Loading…
Cancel
Save