fix: Allow Walk to honor load balanced drives (#10610)

master
Harshavardhana 4 years ago committed by GitHub
parent 71403be912
commit 23e8390997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/workflows/go.yml
  2. 4
      buildscripts/verify-build.sh
  3. 27
      cmd/erasure-zones.go
  4. 18
      cmd/net.go
  5. 3
      cmd/object-handlers_test.go
  6. 13
      cmd/web-handlers_test.go
  7. 25
      pkg/disk/stat_windows.go
  8. 4
      pkg/lock/lock_windows.go
  9. 1
      pkg/s3select/internal/parquet-go/gen-go/parquet/parquet-consts.go
  10. 1
      pkg/s3select/internal/parquet-go/gen-go/parquet/parquet.go
  11. 4
      pkg/sys/stats_windows.go

@ -37,6 +37,8 @@ jobs:
GO111MODULE: on GO111MODULE: on
MINIO_CI_CD: 1 MINIO_CI_CD: 1
run: | run: |
sudo sysctl net.ipv6.conf.all.disable_ipv6=0
sudo sysctl net.ipv6.conf.default.disable_ipv6=0
sudo apt-get install devscripts shellcheck sudo apt-get install devscripts shellcheck
nancy_version=$(curl --retry 10 -Ls -o /dev/null -w "%{url_effective}" https://github.com/sonatype-nexus-community/nancy/releases/latest | sed "s/https:\/\/github.com\/sonatype-nexus-community\/nancy\/releases\/tag\///") nancy_version=$(curl --retry 10 -Ls -o /dev/null -w "%{url_effective}" https://github.com/sonatype-nexus-community/nancy/releases/latest | sed "s/https:\/\/github.com\/sonatype-nexus-community\/nancy\/releases\/tag\///")
curl -L -o nancy https://github.com/sonatype-nexus-community/nancy/releases/download/${nancy_version}/nancy-linux.amd64-${nancy_version} && chmod +x nancy curl -L -o nancy https://github.com/sonatype-nexus-community/nancy/releases/download/${nancy_version}/nancy-linux.amd64-${nancy_version} && chmod +x nancy

@ -76,8 +76,8 @@ function start_minio_zone_erasure_sets_ipv6()
export MINIO_ACCESS_KEY=$ACCESS_KEY export MINIO_ACCESS_KEY=$ACCESS_KEY
export MINIO_SECRET_KEY=$SECRET_KEY export MINIO_SECRET_KEY=$SECRET_KEY
"${MINIO[@]}" server --address="[::1]:9000" "http://[::1]:9000${WORK_DIR}/zone-disk-sets{1...4}" "http://[::1]:9001${WORK_DIR}/zone-disk-sets{5...8}" >"$WORK_DIR/zone-minio-9000.log" 2>&1 & "${MINIO[@]}" server --address="[::1]:9000" "http://[::1]:9000${WORK_DIR}/zone-disk-sets{1...4}" "http://[::1]:9001${WORK_DIR}/zone-disk-sets{5...8}" >"$WORK_DIR/zone-minio-ipv6-9000.log" 2>&1 &
"${MINIO[@]}" server --address="[::1]:9001" "http://[::1]:9000${WORK_DIR}/zone-disk-sets{1...4}" "http://[::1]:9001${WORK_DIR}/zone-disk-sets{5...8}" >"$WORK_DIR/zone-minio-9001.log" 2>&1 & "${MINIO[@]}" server --address="[::1]:9001" "http://[::1]:9000${WORK_DIR}/zone-disk-sets{1...4}" "http://[::1]:9001${WORK_DIR}/zone-disk-sets{5...8}" >"$WORK_DIR/zone-minio-ipv6-9001.log" 2>&1 &
sleep 40 sleep 40
} }

@ -1750,15 +1750,15 @@ func (z *erasureZones) Walk(ctx context.Context, bucket, prefix string, results
return err return err
} }
if opts.WalkVersions { var zoneDrivesPerSet []int
var zonesEntryChs [][]FileInfoVersionsCh
for _, zone := range z.zones { for _, zone := range z.zones {
zonesEntryChs = append(zonesEntryChs, zone.startMergeWalksVersions(ctx, bucket, prefix, "", true, ctx.Done())) zoneDrivesPerSet = append(zoneDrivesPerSet, zone.listTolerancePerSet-2)
} }
var zoneDrivesPerSet []int if opts.WalkVersions {
var zonesEntryChs [][]FileInfoVersionsCh
for _, zone := range z.zones { for _, zone := range z.zones {
zoneDrivesPerSet = append(zoneDrivesPerSet, zone.setDriveCount) zonesEntryChs = append(zonesEntryChs, zone.startMergeWalksVersions(ctx, bucket, prefix, "", true, ctx.Done()))
} }
var zonesEntriesInfos [][]FileInfoVersions var zonesEntriesInfos [][]FileInfoVersions
@ -1772,20 +1772,17 @@ func (z *erasureZones) Walk(ctx context.Context, bucket, prefix string, results
defer close(results) defer close(results)
for { for {
entry, quorumCount, zoneIndex, ok := lexicallySortedEntryZoneVersions(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid) entry, quorumCount, _, ok := lexicallySortedEntryZoneVersions(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid)
if !ok { if !ok {
// We have reached EOF across all entryChs, break the loop. // We have reached EOF across all entryChs, break the loop.
return return
} }
if quorumCount >= zoneDrivesPerSet[zoneIndex]/2 { if quorumCount > 0 {
// Read quorum exists proceed
for _, version := range entry.Versions { for _, version := range entry.Versions {
results <- version.ToObjectInfo(bucket, version.Name) results <- version.ToObjectInfo(bucket, version.Name)
} }
} }
// skip entries which do not have quorum
} }
}() }()
@ -1793,10 +1790,8 @@ func (z *erasureZones) Walk(ctx context.Context, bucket, prefix string, results
} }
zonesEntryChs := make([][]FileInfoCh, 0, len(z.zones)) zonesEntryChs := make([][]FileInfoCh, 0, len(z.zones))
zoneDrivesPerSet := make([]int, 0, len(z.zones))
for _, zone := range z.zones { for _, zone := range z.zones {
zonesEntryChs = append(zonesEntryChs, zone.startMergeWalks(ctx, bucket, prefix, "", true, ctx.Done())) zonesEntryChs = append(zonesEntryChs, zone.startMergeWalks(ctx, bucket, prefix, "", true, ctx.Done()))
zoneDrivesPerSet = append(zoneDrivesPerSet, zone.setDriveCount)
} }
zonesEntriesInfos := make([][]FileInfo, 0, len(zonesEntryChs)) zonesEntriesInfos := make([][]FileInfo, 0, len(zonesEntryChs))
@ -1810,17 +1805,15 @@ func (z *erasureZones) Walk(ctx context.Context, bucket, prefix string, results
defer close(results) defer close(results)
for { for {
entry, quorumCount, zoneIndex, ok := lexicallySortedEntryZone(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid) entry, quorumCount, _, ok := lexicallySortedEntryZone(zonesEntryChs, zonesEntriesInfos, zonesEntriesValid)
if !ok { if !ok {
// We have reached EOF across all entryChs, break the loop. // We have reached EOF across all entryChs, break the loop.
return return
} }
if quorumCount >= zoneDrivesPerSet[zoneIndex]/2 { if quorumCount > 0 {
// Read quorum exists proceed
results <- entry.ToObjectInfo(bucket, entry.Name) results <- entry.ToObjectInfo(bucket, entry.Name)
} }
// skip entries which do not have quorum
} }
}() }()
@ -1859,6 +1852,7 @@ func (z *erasureZones) HealObjects(ctx context.Context, bucket, prefix string, o
if !ok { if !ok {
break break
} }
// Indicate that first attempt was a success and subsequent loop // Indicate that first attempt was a success and subsequent loop
// knows that its not our first attempt at 'prefix' // knows that its not our first attempt at 'prefix'
err = nil err = nil
@ -1866,6 +1860,7 @@ func (z *erasureZones) HealObjects(ctx context.Context, bucket, prefix string, o
if zoneIndex >= len(zoneDrivesPerSet) || zoneIndex < 0 { if zoneIndex >= len(zoneDrivesPerSet) || zoneIndex < 0 {
return fmt.Errorf("invalid zone index returned: %d", zoneIndex) return fmt.Errorf("invalid zone index returned: %d", zoneIndex)
} }
if quorumCount == zoneDrivesPerSet[zoneIndex] && opts.ScanMode == madmin.HealNormalScan { if quorumCount == zoneDrivesPerSet[zoneIndex] && opts.ScanMode == madmin.HealNormalScan {
// Skip good entries. // Skip good entries.
continue continue

@ -189,21 +189,13 @@ func isHostIP(ipAddress string) bool {
// Note: The check method tries to listen on given port and closes it. // Note: The check method tries to listen on given port and closes it.
// It is possible to have a disconnected client in this tiny window of time. // It is possible to have a disconnected client in this tiny window of time.
func checkPortAvailability(host, port string) (err error) { func checkPortAvailability(host, port string) (err error) {
network := []string{"tcp", "tcp4", "tcp6"} l, err := net.Listen("tcp", net.JoinHostPort(host, port))
for _, n := range network { if err != nil {
l, err := net.Listen(n, net.JoinHostPort(host, port))
if err == nil {
// As we are able to listen on this network, the port is not in use.
// Close the listener and continue check other networks.
if err = l.Close(); err != nil {
return err
}
} else {
return err return err
} }
} // As we are able to listen on this network, the port is not in use.
// Close the listener and continue check other networks.
return nil return l.Close()
} }
// extractHostPort - extracts host/port from many address formats // extractHostPort - extracts host/port from many address formats

@ -2788,7 +2788,8 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s
if rec.Code == http.StatusOK { if rec.Code == http.StatusOK {
// Verify whether the bucket obtained object is same as the one created. // Verify whether the bucket obtained object is same as the one created.
if !bytes.Equal(testCase.expectedContent, actualContent) { if !bytes.Equal(testCase.expectedContent, actualContent) {
t.Errorf("Test %d : MinIO %s: Object content differs from expected value.", i+1, instanceType) t.Errorf("Test %d : MinIO %s: CompleteMultipart response content differs from expected value. got %s, expecte %s", i+1, instanceType,
string(actualContent), string(testCase.expectedContent))
} }
continue continue
} }

@ -935,9 +935,16 @@ func testWebHandlerDownloadZip(obj ObjectLayer, instanceType string, t TestErrHa
t.Fatalf("%s : %s", instanceType, err) t.Fatalf("%s : %s", instanceType, err)
} }
obj.PutObject(context.Background(), bucket, "a/one", mustGetPutObjReader(t, strings.NewReader(fileOne), int64(len(fileOne)), "", ""), opts) for objName, value := range map[string]string{
obj.PutObject(context.Background(), bucket, "a/b/two", mustGetPutObjReader(t, strings.NewReader(fileTwo), int64(len(fileTwo)), "", ""), opts) "a/one": fileOne,
obj.PutObject(context.Background(), bucket, "a/c/three", mustGetPutObjReader(t, strings.NewReader(fileThree), int64(len(fileThree)), "", ""), opts) "a/b/two": fileTwo,
"a/c/three": fileThree,
} {
_, err = obj.PutObject(context.Background(), bucket, objName, mustGetPutObjReader(t, strings.NewReader(value), int64(len(value)), "", ""), opts)
if err != nil {
t.Fatal(err)
}
}
test := func(token string) (int, []byte) { test := func(token string) (int, []byte) {
rec := httptest.NewRecorder() rec := httptest.NewRecorder()

@ -23,16 +23,19 @@ import (
"os" "os"
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/windows"
) )
var ( var (
kernel32 = syscall.NewLazyDLL("kernel32.dll") kernel32 = windows.NewLazySystemDLL("kernel32.dll")
// GetDiskFreeSpaceEx - https://msdn.microsoft.com/en-us/library/windows/desktop/aa364937(v=vs.85).aspx // GetDiskFreeSpaceEx - https://msdn.microsoft.com/en-us/library/windows/desktop/aa364937(v=vs.85).aspx
// Retrieves information about the amount of space that is available on a disk volume, // Retrieves information about the amount of space that is available on a disk volume,
// which is the total amount of space, the total amount of free space, and the total // which is the total amount of space, the total amount of free space, and the total
// amount of free space available to the user that is associated with the calling thread. // amount of free space available to the user that is associated with the calling thread.
GetDiskFreeSpaceEx = kernel32.NewProc("GetDiskFreeSpaceExW") GetDiskFreeSpaceEx = kernel32.NewProc("GetDiskFreeSpaceExW")
// GetDiskFreeSpace - https://msdn.microsoft.com/en-us/library/windows/desktop/aa364935(v=vs.85).aspx // GetDiskFreeSpace - https://msdn.microsoft.com/en-us/library/windows/desktop/aa364935(v=vs.85).aspx
// Retrieves information about the specified disk, including the amount of free space on the disk. // Retrieves information about the specified disk, including the amount of free space on the disk.
GetDiskFreeSpace = kernel32.NewProc("GetDiskFreeSpaceW") GetDiskFreeSpace = kernel32.NewProc("GetDiskFreeSpaceW")
@ -63,10 +66,18 @@ func GetInfo(path string) (info Info, err error) {
uintptr(unsafe.Pointer(&lpFreeBytesAvailable)), uintptr(unsafe.Pointer(&lpFreeBytesAvailable)),
uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)), uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)),
uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes))) uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes)))
info = Info{}
info.Total = uint64(lpTotalNumberOfBytes) if uint64(lpTotalNumberOfFreeBytes) > uint64(lpTotalNumberOfBytes) {
info.Free = uint64(lpFreeBytesAvailable) return info, fmt.Errorf("detected free space (%d) > total disk space (%d), fs corruption at (%s). please run 'fsck'",
info.FSType = getFSType(path) uint64(lpTotalNumberOfFreeBytes), uint64(lpTotalNumberOfBytes), path)
}
info = Info{
Total: uint64(lpTotalNumberOfBytes),
Free: uint64(lpTotalNumberOfFreeBytes),
Used: uint64(lpTotalNumberOfBytes) - uint64(lpTotalNumberOfFreeBytes),
FSType: getFSType(path),
}
// Return values of GetDiskFreeSpace() // Return values of GetDiskFreeSpace()
lpSectorsPerCluster := uint32(0) lpSectorsPerCluster := uint32(0)
@ -91,9 +102,5 @@ func GetInfo(path string) (info Info, err error) {
info.Files = uint64(lpTotalNumberOfClusters) info.Files = uint64(lpTotalNumberOfClusters)
info.Ffree = uint64(lpNumberOfFreeClusters) info.Ffree = uint64(lpNumberOfFreeClusters)
if info.Free > info.Total {
return info, fmt.Errorf("detected free space (%d) > total disk space (%d), fs corruption at (%s). please run 'fsck'", info.Free, info.Total, path)
}
info.Used = info.Total - info.Free
return info, nil return info, nil
} }

@ -24,10 +24,12 @@ import (
"path/filepath" "path/filepath"
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/windows"
) )
var ( var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procLockFileEx = modkernel32.NewProc("LockFileEx") procLockFileEx = modkernel32.NewProc("LockFileEx")
) )

@ -6,6 +6,7 @@ package parquet
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"git.apache.org/thrift.git/lib/go/thrift" "git.apache.org/thrift.git/lib/go/thrift"
) )

@ -8,6 +8,7 @@ import (
"database/sql/driver" "database/sql/driver"
"errors" "errors"
"fmt" "fmt"
"git.apache.org/thrift.git/lib/go/thrift" "git.apache.org/thrift.git/lib/go/thrift"
) )

@ -21,10 +21,12 @@ package sys
import ( import (
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/windows"
) )
var ( var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx") procGlobalMemoryStatusEx = modkernel32.NewProc("GlobalMemoryStatusEx")
) )

Loading…
Cancel
Save