github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/store/nbs/s3_table_reader_test.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // This file incorporates work covered by the following copyright and 16 // permission notice: 17 // 18 // Copyright 2016 Attic Labs, Inc. All rights reserved. 19 // Licensed under the Apache License, version 2.0: 20 // http://www.apache.org/licenses/LICENSE-2.0 21 22 package nbs 23 24 import ( 25 "context" 26 "io" 27 "net" 28 "os" 29 "syscall" 30 "testing" 31 32 "github.com/aws/aws-sdk-go/aws" 33 "github.com/aws/aws-sdk-go/aws/request" 34 "github.com/aws/aws-sdk-go/service/s3" 35 "github.com/aws/aws-sdk-go/service/s3/s3iface" 36 "github.com/stretchr/testify/assert" 37 "github.com/stretchr/testify/require" 38 ) 39 40 func TestS3TableReaderAt(t *testing.T) { 41 s3 := makeFakeS3(t) 42 43 chunks := [][]byte{ 44 []byte("hello2"), 45 []byte("goodbye2"), 46 []byte("badbye2"), 47 } 48 49 tableData, h, err := buildTable(chunks) 50 require.NoError(t, err) 51 s3.data[h.String()] = tableData 52 53 t.Run("TolerateFailingReads", func(t *testing.T) { 54 assert := assert.New(t) 55 56 baseline := s3.getCount 57 tra := &s3TableReaderAt{&s3ObjectReader{makeFlakyS3(s3), "bucket", nil, ""}, h} 58 scratch := make([]byte, len(tableData)) 59 _, err := tra.ReadAtWithStats(context.Background(), scratch, 0, &Stats{}) 60 require.NoError(t, err) 61 // constructing the table reader should have resulted in 2 reads 62 assert.Equal(2, s3.getCount-baseline) 63 assert.Equal(tableData, scratch) 64 }) 65 } 66 67 func TestS3TableReaderAtNamespace(t *testing.T) { 68 assert := assert.New(t) 69 70 s3 := makeFakeS3(t) 71 72 chunks := [][]byte{ 73 []byte("hello2"), 74 []byte("goodbye2"), 75 []byte("badbye2"), 76 } 77 78 ns := "a-prefix-here" 79 80 tableData, h, err := buildTable(chunks) 81 require.NoError(t, err) 82 s3.data["a-prefix-here/"+h.String()] = tableData 83 84 tra := &s3TableReaderAt{&s3ObjectReader{s3, "bucket", nil, ns}, h} 85 scratch := make([]byte, len(tableData)) 86 _, err = tra.ReadAtWithStats(context.Background(), scratch, 0, &Stats{}) 87 require.NoError(t, err) 88 assert.Equal(tableData, scratch) 89 } 90 91 type flakyS3 struct { 92 s3iface.S3API 93 alreadyFailed map[string]struct{} 94 } 95 96 func makeFlakyS3(svc s3iface.S3API) *flakyS3 { 97 return &flakyS3{svc, map[string]struct{}{}} 98 } 99 100 func (fs3 *flakyS3) GetObjectWithContext(ctx aws.Context, input *s3.GetObjectInput, opts ...request.Option) (*s3.GetObjectOutput, error) { 101 output, err := fs3.S3API.GetObjectWithContext(ctx, input) 102 103 if err != nil { 104 return nil, err 105 } 106 107 if _, ok := fs3.alreadyFailed[*input.Key]; !ok { 108 fs3.alreadyFailed[*input.Key] = struct{}{} 109 output.Body = io.NopCloser(resettingReader{}) 110 } 111 112 return output, nil 113 } 114 115 type resettingReader struct{} 116 117 func (rr resettingReader) Read(p []byte) (n int, err error) { 118 return 0, &net.OpError{Op: "read", Net: "tcp", Err: &os.SyscallError{Syscall: "read", Err: syscall.ECONNRESET}} 119 }