diff --git a/docs/storage.md b/docs/storage.md index 989767feb46..8b62d290670 100644 --- a/docs/storage.md +++ b/docs/storage.md @@ -92,7 +92,7 @@ config: trace: enable: false list_objects_version: "" - part_size: 134217728 + part_size: 67108864 sse_config: type: "" kms_key_id: "" diff --git a/go.mod b/go.mod index e3ce8199d3f..926e451a468 100644 --- a/go.mod +++ b/go.mod @@ -76,6 +76,9 @@ replace ( github.com/bradfitz/gomemcache => github.com/themihai/gomemcache v0.0.0-20180902122335-24332e2d58ab // Update to v1.1.1 to make sure windows CI pass. github.com/elastic/go-sysinfo => github.com/elastic/go-sysinfo v1.1.1 + + // TODO: Remove this: https://github.com/thanos-io/thanos/issues/3967. + github.com/minio/minio-go/v7 => github.com/bwplotka/minio-go/v7 v7.0.11-0.20210324165441-f9927e5255a6 // Make sure Prometheus version is pinned as Prometheus semver does not include Go APIs. github.com/prometheus/prometheus => github.com/prometheus/prometheus v1.8.2-0.20210215121130-6f488061dfb4 github.com/sercand/kuberesolver => github.com/sercand/kuberesolver v2.4.0+incompatible diff --git a/go.sum b/go.sum index f530ac84c64..bb70670be0b 100644 --- a/go.sum +++ b/go.sum @@ -195,6 +195,8 @@ github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/bwplotka/minio-go/v7 v7.0.11-0.20210324165441-f9927e5255a6 h1:h9SZ0jmAKjtrZF6iZ77/jdXdHr+Usn29itI669SVRp4= +github.com/bwplotka/minio-go/v7 v7.0.11-0.20210324165441-f9927e5255a6/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v0.0.0-20181003080854-62661b46c409/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -900,10 +902,6 @@ github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4= github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= github.com/minio/minio-go/v6 v6.0.44/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg= github.com/minio/minio-go/v6 v6.0.56/go.mod h1:KQMM+/44DSlSGSQWSfRrAZ12FVMmpWNuX37i2AX0jfI= -github.com/minio/minio-go/v7 v7.0.2 h1:P/7wFd4KrRBHVo7AKdcqO+9ReoS+XpMjfRFoE5quH0E= -github.com/minio/minio-go/v7 v7.0.2/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6JUMSQ2zAns= -github.com/minio/minio-go/v7 v7.0.10 h1:1oUKe4EOPUEhw2qnPQaPsJ0lmVTYLFu03SiItauXs94= -github.com/minio/minio-go/v7 v7.0.10/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo= github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU= github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= diff --git a/pkg/objstore/s3/s3.go b/pkg/objstore/s3/s3.go index 8814412e898..6a490fd5eb5 100644 --- a/pkg/objstore/s3/s3.go +++ b/pkg/objstore/s3/s3.go @@ -66,9 +66,7 @@ var DefaultConfig = Config{ MaxIdleConnsPerHost: 100, MaxConnsPerHost: 0, }, - // Minimum file size after which an HTTP multipart request should be used to upload objects to storage. - // Set to 128 MiB as in the minio client. - PartSize: 1024 * 1024 * 128, + PartSize: 1024 * 1024 * 64, // 64MB. } // Config stores the configuration for s3 bucket. @@ -85,6 +83,7 @@ type Config struct { TraceConfig TraceConfig `yaml:"trace"` ListObjectsVersion string `yaml:"list_objects_version"` // PartSize used for multipart upload. Only used if uploaded object size is known and larger than configured PartSize. + // NOTE we need to make sure this number does not produce more parts than 10 000. PartSize uint64 `yaml:"part_size"` SSEConfig SSEConfig `yaml:"sse_config"` } @@ -449,7 +448,6 @@ func (b *Bucket) Upload(ctx context.Context, name string, r io.Reader) error { size = -1 } - // partSize cannot be larger than object size. partSize := b.partSize if size < int64(partSize) { partSize = 0 diff --git a/pkg/objstore/s3/s3_e2e_test.go b/pkg/objstore/s3/s3_e2e_test.go new file mode 100644 index 00000000000..e837b9baa58 --- /dev/null +++ b/pkg/objstore/s3/s3_e2e_test.go @@ -0,0 +1,55 @@ +// Copyright (c) The Thanos Authors. +// Licensed under the Apache License 2.0. + +package s3_test + +import ( + "bytes" + "context" + "strings" + "testing" + + "github.com/cortexproject/cortex/integration/e2e" + e2edb "github.com/cortexproject/cortex/integration/e2e/db" + "github.com/go-kit/kit/log" + "github.com/thanos-io/thanos/pkg/objstore/s3" + "github.com/thanos-io/thanos/test/e2e/e2ethanos" + + "github.com/thanos-io/thanos/pkg/testutil" +) + +// Regression benchmark for https://github.com/thanos-io/thanos/issues/3917. +func BenchmarkUpload(b *testing.B) { + b.ReportAllocs() + ctx := context.Background() + + s, err := e2e.NewScenario("e2e_bench_mino_client") + testutil.Ok(b, err) + b.Cleanup(e2ethanos.CleanScenario(b, s)) + + const bucket = "test" + m := e2edb.NewMinio(8080, bucket) + testutil.Ok(b, s.StartAndWaitReady(m)) + + bkt, err := s3.NewBucketWithConfig(log.NewNopLogger(), s3.Config{ + Bucket: bucket, + AccessKey: e2edb.MinioAccessKey, + SecretKey: e2edb.MinioSecretKey, + Endpoint: m.HTTPEndpoint(), + Insecure: true, + }, "test-feed") + testutil.Ok(b, err) + + buf := bytes.Buffer{} + buf.Grow(1028 * 1028 * 100) // 100MB. + word := "abcdefghij" + for i := 0; i < buf.Cap()/len(word); i++ { + _, _ = buf.WriteString(word) + } + str := buf.String() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + testutil.Ok(b, bkt.Upload(ctx, "test", strings.NewReader(str))) + } +} diff --git a/test/e2e/e2ethanos/helpers.go b/test/e2e/e2ethanos/helpers.go index 714bdbb468e..0bcd21c9128 100644 --- a/test/e2e/e2ethanos/helpers.go +++ b/test/e2e/e2ethanos/helpers.go @@ -16,7 +16,7 @@ import ( "github.com/thanos-io/thanos/pkg/testutil" ) -func CleanScenario(t *testing.T, s *e2e.Scenario) func() { +func CleanScenario(t testing.TB, s *e2e.Scenario) func() { return func() { // Make sure Clean can properly delete everything. testutil.Ok(t, exec.Command("chmod", "-R", "777", s.SharedDir()).Run())