From 1f19c5a403c1c6931de5ec29f5a476334ddcdfe6 Mon Sep 17 00:00:00 2001 From: mazrean Date: Wed, 6 Mar 2024 00:05:30 +0900 Subject: [PATCH 1/7] datasize int64 --- formstream.go | 2 +- parse.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/formstream.go b/formstream.go index 146b5fa..fe30475 100644 --- a/formstream.go +++ b/formstream.go @@ -41,7 +41,7 @@ type parserConfig struct { type ParserOption func(*parserConfig) -type DataSize uint64 +type DataSize int64 const ( _ DataSize = 1 << (iota * 10) diff --git a/parse.go b/parse.go index 540553f..a08baa6 100644 --- a/parse.go +++ b/parse.go @@ -158,13 +158,14 @@ func (pp *preProcessor) run(normalParam *normalParam) (*abnormalParam, error) { buf := bufPool.Get().(*bytes.Buffer) buf.Reset() - n, err := io.CopyN(buf, normalParam.r, min(int64(pp.config.maxMemFileSize), int64(pp.config.maxMemSize))+1) + memLimit := min(pp.config.maxMemFileSize, pp.config.maxMemSize) + n, err := io.CopyN(buf, normalParam.r, int64(memLimit)+1) if err != nil && !errors.Is(err, io.EOF) { return nil, fmt.Errorf("failed to copy: %w", err) } var content io.ReadCloser - if n > int64(pp.config.maxMemFileSize) || n > int64(pp.config.maxMemSize) { + if DataSize(n) > memLimit { if pp.file == nil { f, err := os.CreateTemp("", "formstream-") if err != nil { From 09080edd0aecef6ef5d6c371e33fa73b16361a09 Mon Sep 17 00:00:00 2001 From: mazrean Date: Wed, 6 Mar 2024 00:05:52 +0900 Subject: [PATCH 2/7] http benchmark --- http/parser_test.go | 255 +++++++++++++++++++++++++++++++++++++++++++ internal/myio/nop.go | 13 +++ 2 files changed, 268 insertions(+) create mode 100644 internal/myio/nop.go diff --git a/http/parser_test.go b/http/parser_test.go index 6e1476e..b882302 100644 --- a/http/parser_test.go +++ b/http/parser_test.go @@ -1,17 +1,22 @@ package httpform_test import ( + "bytes" "context" "fmt" "io" "log" + "mime/multipart" "net/http" "net/http/httptest" + "net/textproto" + "os" "strings" "testing" "github.com/mazrean/formstream" httpform "github.com/mazrean/formstream/http" + "github.com/mazrean/formstream/internal/myio" ) func TestExample(t *testing.T) { @@ -101,3 +106,253 @@ func saveUser(_ context.Context, name string, password string, iconReader io.Rea return nil } + +const boundary = "boundary" + +func sampleForm(fileSize formstream.DataSize, boundary string, reverse bool) (io.ReadSeekCloser, error) { + if fileSize > 1*formstream.GB { + f, err := os.CreateTemp("", "formstream-test-form-") + if err != nil { + return nil, fmt.Errorf("failed to create temp file: %w", err) + } + + err = createSampleForm(f, fileSize, boundary, reverse) + if err != nil { + return nil, fmt.Errorf("failed to create sample form: %w", err) + } + + return f, nil + } + + buf := bytes.NewBuffer(nil) + + err := createSampleForm(buf, fileSize, boundary, reverse) + if err != nil { + return nil, fmt.Errorf("failed to create sample form: %w", err) + } + + return myio.NopSeekCloser(bytes.NewReader(buf.Bytes())), nil +} + +func createSampleForm(w io.Writer, fileSize formstream.DataSize, boundary string, reverse bool) error { + mw := multipart.NewWriter(w) + defer mw.Close() + + err := mw.SetBoundary(boundary) + if err != nil { + return fmt.Errorf("failed to set boundary: %w", err) + } + + if !reverse { + err := mw.WriteField("field", "value") + if err != nil { + return fmt.Errorf("failed to write field: %w", err) + } + } + + mh := make(textproto.MIMEHeader) + mh.Set("Content-Disposition", `form-data; name="stream"; filename="file.txt"`) + mh.Set("Content-Type", "text/plain") + pw, err := mw.CreatePart(mh) + if err != nil { + return fmt.Errorf("failed to create part: %w", err) + } + for i := 0; i < int(fileSize/formstream.MB); i++ { + _, err := pw.Write([]byte(strings.Repeat("a", int(formstream.MB)))) + if err != nil { + return fmt.Errorf("failed to write: %w", err) + } + } + + if reverse { + err := mw.WriteField("field", "value") + if err != nil { + return fmt.Errorf("failed to write field: %w", err) + } + } + + return nil +} + +func BenchmarkFormStreamFastPath(b *testing.B) { + b.Run("1MB", func(b *testing.B) { + benchmarkFormStream(b, 1*formstream.MB, false) + }) + b.Run("10MB", func(b *testing.B) { + benchmarkFormStream(b, 10*formstream.MB, false) + }) + b.Run("100MB", func(b *testing.B) { + benchmarkFormStream(b, 100*formstream.MB, false) + }) + b.Run("1GB", func(b *testing.B) { + benchmarkFormStream(b, 1*formstream.GB, false) + }) + b.Run("5GB", func(b *testing.B) { + benchmarkFormStream(b, 5*formstream.GB, false) + }) + b.Run("10GB", func(b *testing.B) { + benchmarkFormStream(b, 10*formstream.GB, false) + }) +} + +func BenchmarkFormStreamSlowPath(b *testing.B) { + b.Run("1MB", func(b *testing.B) { + benchmarkFormStream(b, 1*formstream.MB, true) + }) + b.Run("10MB", func(b *testing.B) { + benchmarkFormStream(b, 10*formstream.MB, true) + }) + b.Run("100MB", func(b *testing.B) { + benchmarkFormStream(b, 100*formstream.MB, true) + }) + b.Run("1GB", func(b *testing.B) { + benchmarkFormStream(b, 1*formstream.GB, true) + }) + b.Run("5GB", func(b *testing.B) { + benchmarkFormStream(b, 5*formstream.GB, true) + }) + b.Run("10GB", func(b *testing.B) { + benchmarkFormStream(b, 10*formstream.GB, true) + }) +} + +func benchmarkFormStream(b *testing.B, fileSize formstream.DataSize, reverse bool) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + parser, err := httpform.NewParser(r) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + err = parser.Register("stream", func(r io.Reader, header formstream.Header) error { + // get field value + _, _, _ = parser.Value("field") + + _, err := io.Copy(io.Discard, r) + if err != nil { + return fmt.Errorf("failed to copy: %w", err) + } + + return nil + }, formstream.WithRequiredPart("field")) + if err != nil { + b.Fatal(err) + } + + err = parser.Parse() + if err != nil { + b.Fatal(err) + } + + w.WriteHeader(http.StatusCreated) + })) + defer srv.Close() + + r, err := sampleForm(fileSize, boundary, reverse) + if err != nil { + b.Fatal(err) + } + defer r.Close() + + req, err := http.NewRequest(http.MethodPost, srv.URL, r) + if err != nil { + b.Fatal(err) + } + req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", boundary)) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + _, err := r.Seek(0, io.SeekStart) + if err != nil { + b.Fatal(err) + } + b.StartTimer() + + _, err = http.DefaultClient.Do(req) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkStdMultipart_ReadForm(b *testing.B) { + // default value in http package + const maxMemory = 32 * formstream.MB + + b.Run("1MB", func(b *testing.B) { + benchmarkStdMultipart_ReadForm(b, 1*formstream.MB, maxMemory) + }) + b.Run("10MB", func(b *testing.B) { + benchmarkStdMultipart_ReadForm(b, 10*formstream.MB, maxMemory) + }) + b.Run("100MB", func(b *testing.B) { + benchmarkStdMultipart_ReadForm(b, 100*formstream.MB, maxMemory) + }) + b.Run("1GB", func(b *testing.B) { + benchmarkStdMultipart_ReadForm(b, 1*formstream.GB, maxMemory) + }) + b.Run("5GB", func(b *testing.B) { + benchmarkStdMultipart_ReadForm(b, 5*formstream.GB, maxMemory) + }) +} + +func benchmarkStdMultipart_ReadForm(b *testing.B, fileSize formstream.DataSize, maxMemory formstream.DataSize) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mr := multipart.NewReader(r.Body, boundary) + form, err := mr.ReadForm(int64(maxMemory)) + if err != nil { + b.Fatal(err) + } + defer func() { + err := form.RemoveAll() + if err != nil { + b.Fatal(err) + } + }() + + f, err := form.File["stream"][0].Open() + if err != nil { + b.Fatal(err) + } + defer f.Close() + + _, err = io.Copy(io.Discard, f) + if err != nil { + b.Fatal(err) + } + + // get field value + _ = form.Value["field"][0] + + w.WriteHeader(http.StatusCreated) + })) + defer srv.Close() + + r, err := sampleForm(fileSize, boundary, false) + if err != nil { + b.Fatal(err) + } + defer r.Close() + + req, err := http.NewRequest(http.MethodPost, srv.URL, r) + if err != nil { + b.Fatal(err) + } + req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", boundary)) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + _, err := r.Seek(0, io.SeekStart) + if err != nil { + b.Fatal(err) + } + b.StartTimer() + + _, err = http.DefaultClient.Do(req) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/internal/myio/nop.go b/internal/myio/nop.go new file mode 100644 index 0000000..d632439 --- /dev/null +++ b/internal/myio/nop.go @@ -0,0 +1,13 @@ +package myio + +import "io" + +type nopSeekCloser struct { + io.ReadSeeker +} + +func NopSeekCloser(r io.ReadSeeker) io.ReadSeekCloser { + return nopSeekCloser{r} +} + +func (nopSeekCloser) Close() error { return nil } From 1f83a9e030e374f7d0b22c5acb2ec8e0805c454f Mon Sep 17 00:00:00 2001 From: mazrean Date: Wed, 6 Mar 2024 00:10:17 +0900 Subject: [PATCH 3/7] add 5GB,10GB benchmark --- docs/images/memory.png | Bin 32008 -> 33359 bytes docs/images/time.png | Bin 30807 -> 32371 bytes formstream_test.go | 87 ++++++++++++++++++++++++++++++++++------- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/docs/images/memory.png b/docs/images/memory.png index 3ed0100f3663511f4604782ea9aa4385dd8eeea8..b94984f4e4445e212d84884b0031890e2fc8ed59 100644 GIT binary patch literal 33359 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 diff --git a/docs/images/time.png b/docs/images/time.png index 967d89aa419e221835546a9cffa8d924b3d4388c..db465af40f982a8388f0a0b52c0d7fba569224de 100644 GIT binary patch literal 32371 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 literal 30807 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 aZ$r$(*qX%W?d~Dz^%Ue6oqv^E+VmIE0t>VN diff --git a/formstream_test.go b/formstream_test.go index 961c7d5..b5b3ac4 100644 --- a/formstream_test.go +++ b/formstream_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/mazrean/formstream" + "github.com/mazrean/formstream/internal/myio" ) func ExampleNewParser() { @@ -69,44 +70,69 @@ large file contents const boundary = "boundary" -func sampleForm(fileSize formstream.DataSize, boundary string, reverse bool) (io.Reader, error) { - b := bytes.NewBuffer(nil) +func sampleForm(fileSize formstream.DataSize, boundary string, reverse bool) (io.ReadSeekCloser, error) { + if fileSize > 1*formstream.GB { + f, err := os.CreateTemp("", "formstream-test-form-") + if err != nil { + return nil, fmt.Errorf("failed to create temp file: %w", err) + } + + err = createSampleForm(f, fileSize, boundary, reverse) + if err != nil { + return nil, fmt.Errorf("failed to create sample form: %w", err) + } + + return f, nil + } + + buf := bytes.NewBuffer(nil) - mw := multipart.NewWriter(b) + err := createSampleForm(buf, fileSize, boundary, reverse) + if err != nil { + return nil, fmt.Errorf("failed to create sample form: %w", err) + } + + return myio.NopSeekCloser(bytes.NewReader(buf.Bytes())), nil +} + +func createSampleForm(w io.Writer, fileSize formstream.DataSize, boundary string, reverse bool) error { + mw := multipart.NewWriter(w) defer mw.Close() err := mw.SetBoundary(boundary) if err != nil { - return nil, fmt.Errorf("failed to set boundary: %w", err) + return fmt.Errorf("failed to set boundary: %w", err) } if !reverse { err := mw.WriteField("field", "value") if err != nil { - return nil, fmt.Errorf("failed to write field: %w", err) + return fmt.Errorf("failed to write field: %w", err) } } mh := make(textproto.MIMEHeader) mh.Set("Content-Disposition", `form-data; name="stream"; filename="file.txt"`) mh.Set("Content-Type", "text/plain") - w, err := mw.CreatePart(mh) + pw, err := mw.CreatePart(mh) if err != nil { - return nil, fmt.Errorf("failed to create part: %w", err) + return fmt.Errorf("failed to create part: %w", err) } - _, err = io.CopyN(w, strings.NewReader(strings.Repeat("a", int(fileSize))), int64(fileSize)) - if err != nil { - return nil, fmt.Errorf("failed to copy: %w", err) + for i := 0; i < int(fileSize/formstream.MB); i++ { + _, err := pw.Write([]byte(strings.Repeat("a", int(formstream.MB)))) + if err != nil { + return fmt.Errorf("failed to write: %w", err) + } } if reverse { err := mw.WriteField("field", "value") if err != nil { - return nil, fmt.Errorf("failed to write field: %w", err) + return fmt.Errorf("failed to write field: %w", err) } } - return b, nil + return nil } func BenchmarkFormStreamFastPath(b *testing.B) { @@ -122,6 +148,12 @@ func BenchmarkFormStreamFastPath(b *testing.B) { b.Run("1GB", func(b *testing.B) { benchmarkFormStream(b, 1*formstream.GB, false) }) + b.Run("5GB", func(b *testing.B) { + benchmarkFormStream(b, 5*formstream.GB, false) + }) + b.Run("10GB", func(b *testing.B) { + benchmarkFormStream(b, 10*formstream.GB, false) + }) } func BenchmarkFormStreamSlowPath(b *testing.B) { @@ -137,12 +169,25 @@ func BenchmarkFormStreamSlowPath(b *testing.B) { b.Run("1GB", func(b *testing.B) { benchmarkFormStream(b, 1*formstream.GB, true) }) + b.Run("5GB", func(b *testing.B) { + benchmarkFormStream(b, 5*formstream.GB, true) + }) + b.Run("10GB", func(b *testing.B) { + benchmarkFormStream(b, 10*formstream.GB, true) + }) } func benchmarkFormStream(b *testing.B, fileSize formstream.DataSize, reverse bool) { + r, err := sampleForm(fileSize, boundary, reverse) + if err != nil { + b.Fatal(err) + } + defer r.Close() + + b.ResetTimer() for i := 0; i < b.N; i++ { b.StopTimer() - r, err := sampleForm(fileSize, boundary, reverse) + _, err := r.Seek(0, io.SeekStart) if err != nil { b.Fatal(err) } @@ -169,7 +214,6 @@ func benchmarkFormStream(b *testing.B, fileSize formstream.DataSize, reverse boo if err != nil { b.Fatal(err) } - } } @@ -189,12 +233,25 @@ func BenchmarkStdMultipart_ReadForm(b *testing.B) { b.Run("1GB", func(b *testing.B) { benchmarkStdMultipart_ReadForm(b, 1*formstream.GB, maxMemory) }) + b.Run("5GB", func(b *testing.B) { + benchmarkStdMultipart_ReadForm(b, 5*formstream.GB, maxMemory) + }) + b.Run("10GB", func(b *testing.B) { + benchmarkStdMultipart_ReadForm(b, 10*formstream.GB, maxMemory) + }) } func benchmarkStdMultipart_ReadForm(b *testing.B, fileSize formstream.DataSize, maxMemory formstream.DataSize) { + r, err := sampleForm(fileSize, boundary, false) + if err != nil { + b.Fatal(err) + } + defer r.Close() + + b.ResetTimer() for i := 0; i < b.N; i++ { b.StopTimer() - r, err := sampleForm(fileSize, boundary, false) + _, err := r.Seek(0, io.SeekStart) if err != nil { b.Fatal(err) } From 93f0148327722935d7b860a62d0ff6fcdbd55b12 Mon Sep 17 00:00:00 2001 From: mazrean Date: Wed, 6 Mar 2024 01:26:32 +0900 Subject: [PATCH 4/7] add http slow stream handler test --- .github/workflows/ci.yaml | 2 +- go.mod | 1 + go.sum | 2 + http/parser_test.go | 81 ++++++++++++++++++++++++++++++++++++ internal/myio/slow_writer.go | 17 ++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 internal/myio/slow_writer.go diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f0c4633..14d1db5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,7 +27,7 @@ jobs: - uses: actions/setup-go@v5 with: go-version-file: go.mod - - run: go test ./... -v -coverprofile=./coverage.txt -race -vet=off + - run: go test ./... -short -v -coverprofile=./coverage.txt -race -vet=off - name: Upload coverage data uses: codecov/codecov-action@v4.1.0 with: diff --git a/go.mod b/go.mod index bf06d98..4c92da0 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/gin-gonic/gin v1.9.1 github.com/labstack/echo/v4 v4.11.4 golang.org/x/mod v0.11.0 // indirect + golang.org/x/sync v0.6.0 golang.org/x/sys v0.15.0 // indirect golang.org/x/tools v0.6.0 // indirect ) diff --git a/go.sum b/go.sum index 3ebaad5..3fb3240 100644 --- a/go.sum +++ b/go.sum @@ -76,6 +76,8 @@ golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= diff --git a/http/parser_test.go b/http/parser_test.go index b882302..2656def 100644 --- a/http/parser_test.go +++ b/http/parser_test.go @@ -17,6 +17,7 @@ import ( "github.com/mazrean/formstream" httpform "github.com/mazrean/formstream/http" "github.com/mazrean/formstream/internal/myio" + "golang.org/x/sync/errgroup" ) func TestExample(t *testing.T) { @@ -174,6 +175,86 @@ func createSampleForm(w io.Writer, fileSize formstream.DataSize, boundary string return nil } +func TestSlowWriter(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + parser, err := httpform.NewParser(r) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + err = parser.Register("stream", func(r io.Reader, header formstream.Header) error { + // get field value + _, _, _ = parser.Value("field") + + _, err := io.Copy(myio.SlowWriter(), r) + if err != nil { + return fmt.Errorf("failed to copy: %w", err) + } + + return nil + }, formstream.WithRequiredPart("field")) + if err != nil { + t.Fatal(err) + } + + err = parser.Parse() + if err != nil { + t.Fatal(err) + } + + w.WriteHeader(http.StatusCreated) + })) + defer srv.Close() + + f, err := os.CreateTemp("", "formstream-test-form-") + if err != nil { + t.Fatal(err) + } + defer f.Close() + + err = createSampleForm(f, 1*formstream.GB, boundary, false) + if err != nil { + t.Fatal(err) + } + + eg := &errgroup.Group{} + for i := 0; i < 100; i++ { + f, err := os.Open(f.Name()) + if err != nil { + t.Fatal(err) + } + + req, err := http.NewRequest(http.MethodPost, srv.URL, f) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", boundary)) + + eg.Go(func() error { + res, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Errorf("failed to request: %w", err) + } + + if res.StatusCode != http.StatusCreated { + return fmt.Errorf("status code is wrong: expected: %d, actual: %d", http.StatusCreated, res.StatusCode) + } + + return nil + }) + } + + err = eg.Wait() + if err != nil { + t.Error(err) + } +} + func BenchmarkFormStreamFastPath(b *testing.B) { b.Run("1MB", func(b *testing.B) { benchmarkFormStream(b, 1*formstream.MB, false) diff --git a/internal/myio/slow_writer.go b/internal/myio/slow_writer.go new file mode 100644 index 0000000..883dc89 --- /dev/null +++ b/internal/myio/slow_writer.go @@ -0,0 +1,17 @@ +package myio + +import ( + "io" + "time" +) + +type slowWriter struct{} + +func SlowWriter() io.Writer { + return &slowWriter{} +} + +func (w *slowWriter) Write(p []byte) (n int, err error) { + time.Sleep(time.Duration(len(p)) * 50 * time.Nanosecond) + return len(p), nil +} From 03d617b256f3f4ab9b4496504afbb9e07d9a008e Mon Sep 17 00:00:00 2001 From: mazrean Date: Wed, 6 Mar 2024 01:36:02 +0900 Subject: [PATCH 5/7] fix benchmark function name --- formstream_test.go | 2 +- http/parser_test.go | 2 +- scripts/graph.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/formstream_test.go b/formstream_test.go index b5b3ac4..fe63175 100644 --- a/formstream_test.go +++ b/formstream_test.go @@ -217,7 +217,7 @@ func benchmarkFormStream(b *testing.B, fileSize formstream.DataSize, reverse boo } } -func BenchmarkStdMultipart_ReadForm(b *testing.B) { +func BenchmarkStdMultipartReadForm(b *testing.B) { // default value in http package const maxMemory = 32 * formstream.MB diff --git a/http/parser_test.go b/http/parser_test.go index 2656def..76f5ed4 100644 --- a/http/parser_test.go +++ b/http/parser_test.go @@ -357,7 +357,7 @@ func benchmarkFormStream(b *testing.B, fileSize formstream.DataSize, reverse boo } } -func BenchmarkStdMultipart_ReadForm(b *testing.B) { +func BenchmarkStdMultipartReadForm(b *testing.B) { // default value in http package const maxMemory = 32 * formstream.MB diff --git a/scripts/graph.py b/scripts/graph.py index 5c2646b..327e43e 100644 --- a/scripts/graph.py +++ b/scripts/graph.py @@ -10,8 +10,8 @@ def parse_group(group: str): if group.startswith("FormStream"): group = group.removeprefix("FormStream") return f"FormStream({group})" - elif group.startswith("StdMultipart_"): - group = group.split("_")[1] + elif group.startswith("StdMultipart"): + group = group.removeprefix("StdMultipart") return f"std(with {group})" return group From f1d1e86d7445757d2419d13d6857388652f24d83 Mon Sep 17 00:00:00 2001 From: mazrean Date: Wed, 6 Mar 2024 01:49:23 +0900 Subject: [PATCH 6/7] fix lint error --- .golangci.yaml | 3 --- formstream_test.go | 20 +++++++++---------- http/parser_test.go | 18 ++++++++--------- .../condition_judge/condition_judger_test.go | 10 ++++++---- parse.go | 5 ++++- register.go | 6 +++--- 6 files changed, 32 insertions(+), 30 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 762b560..92e5988 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -8,8 +8,6 @@ linters: - staticcheck - unused - gosimple - - structcheck - - varcheck - ineffassign - typecheck - revive @@ -32,7 +30,6 @@ linters: - nilerr - nosprintfhostport - sqlclosecheck - - testpackage - unconvert - unparam - whitespace diff --git a/formstream_test.go b/formstream_test.go index fe63175..7f1d3f8 100644 --- a/formstream_test.go +++ b/formstream_test.go @@ -218,30 +218,30 @@ func benchmarkFormStream(b *testing.B, fileSize formstream.DataSize, reverse boo } func BenchmarkStdMultipartReadForm(b *testing.B) { - // default value in http package - const maxMemory = 32 * formstream.MB - b.Run("1MB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 1*formstream.MB, maxMemory) + benchmarkStdMultipartReadForm(b, 1*formstream.MB) }) b.Run("10MB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 10*formstream.MB, maxMemory) + benchmarkStdMultipartReadForm(b, 10*formstream.MB) }) b.Run("100MB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 100*formstream.MB, maxMemory) + benchmarkStdMultipartReadForm(b, 100*formstream.MB) }) b.Run("1GB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 1*formstream.GB, maxMemory) + benchmarkStdMultipartReadForm(b, 1*formstream.GB) }) b.Run("5GB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 5*formstream.GB, maxMemory) + benchmarkStdMultipartReadForm(b, 5*formstream.GB) }) b.Run("10GB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 10*formstream.GB, maxMemory) + benchmarkStdMultipartReadForm(b, 10*formstream.GB) }) } -func benchmarkStdMultipart_ReadForm(b *testing.B, fileSize formstream.DataSize, maxMemory formstream.DataSize) { +func benchmarkStdMultipartReadForm(b *testing.B, fileSize formstream.DataSize) { + // default value in http package + const maxMemory = 32 * formstream.MB + r, err := sampleForm(fileSize, boundary, false) if err != nil { b.Fatal(err) diff --git a/http/parser_test.go b/http/parser_test.go index 76f5ed4..99cc072 100644 --- a/http/parser_test.go +++ b/http/parser_test.go @@ -358,27 +358,27 @@ func benchmarkFormStream(b *testing.B, fileSize formstream.DataSize, reverse boo } func BenchmarkStdMultipartReadForm(b *testing.B) { - // default value in http package - const maxMemory = 32 * formstream.MB - b.Run("1MB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 1*formstream.MB, maxMemory) + benchmarkStdMultipartReadForm(b, 1*formstream.MB) }) b.Run("10MB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 10*formstream.MB, maxMemory) + benchmarkStdMultipartReadForm(b, 10*formstream.MB) }) b.Run("100MB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 100*formstream.MB, maxMemory) + benchmarkStdMultipartReadForm(b, 100*formstream.MB) }) b.Run("1GB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 1*formstream.GB, maxMemory) + benchmarkStdMultipartReadForm(b, 1*formstream.GB) }) b.Run("5GB", func(b *testing.B) { - benchmarkStdMultipart_ReadForm(b, 5*formstream.GB, maxMemory) + benchmarkStdMultipartReadForm(b, 5*formstream.GB) }) } -func benchmarkStdMultipart_ReadForm(b *testing.B, fileSize formstream.DataSize, maxMemory formstream.DataSize) { +func benchmarkStdMultipartReadForm(b *testing.B, fileSize formstream.DataSize) { + // default value in http package + const maxMemory = 32 * formstream.MB + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { mr := multipart.NewReader(r.Body, boundary) form, err := mr.ReadForm(int64(maxMemory)) diff --git a/internal/condition_judge/condition_judger_test.go b/internal/condition_judge/condition_judger_test.go index 24b0e24..55df615 100644 --- a/internal/condition_judge/condition_judger_test.go +++ b/internal/condition_judge/condition_judger_test.go @@ -1,9 +1,11 @@ -package conditionjudge +package conditionjudge_test import ( "errors" "fmt" "testing" + + conditionjudge "github.com/mazrean/formstream/internal/condition_judge" ) var errTest = errors.New("test error") @@ -84,7 +86,7 @@ func TestConditionJudger(t *testing.T) { hooks: map[string]*mockHook{}, events: []event{ {"key", "field", "", nil, "", "", ""}, - {"hook", "stream", "one", ErrNoHooks, "", "", ""}, + {"hook", "stream", "one", conditionjudge.ErrNoHooks, "", "", ""}, }, }, "no call": { @@ -159,11 +161,11 @@ func TestConditionJudger(t *testing.T) { for name, tt := range tests { t.Run(name, func(t *testing.T) { - hookMap := make(map[string]Hook[string, string, string], len(tt.hooks)) + hookMap := make(map[string]conditionjudge.Hook[string, string, string], len(tt.hooks)) for key, hook := range tt.hooks { hookMap[key] = hook } - cj := NewConditionJudger(hookMap, preProcessFunc) + cj := conditionjudge.NewConditionJudger(hookMap, preProcessFunc) EVENT_LOOP: for _, event := range tt.events { diff --git a/parse.go b/parse.go index a08baa6..5d4b478 100644 --- a/parse.go +++ b/parse.go @@ -155,7 +155,10 @@ var bufPool = sync.Pool{ } func (pp *preProcessor) run(normalParam *normalParam) (*abnormalParam, error) { - buf := bufPool.Get().(*bytes.Buffer) + buf, ok := bufPool.Get().(*bytes.Buffer) + if !ok { + buf = new(bytes.Buffer) + } buf.Reset() memLimit := min(pp.config.maxMemFileSize, pp.config.maxMemSize) diff --git a/register.go b/register.go index aca5fd3..ae2399b 100644 --- a/register.go +++ b/register.go @@ -6,7 +6,7 @@ import ( func (p *Parser) Register(name string, fn StreamHookFunc, options ...RegisterOption) error { if _, ok := p.hookMap[name]; ok { - return ErrDuplicateHookName{Name: name} + return DuplicateHookNameError{Name: name} } c := ®isterConfig{} @@ -22,11 +22,11 @@ func (p *Parser) Register(name string, fn StreamHookFunc, options ...RegisterOpt return nil } -type ErrDuplicateHookName struct { +type DuplicateHookNameError struct { Name string } -func (e ErrDuplicateHookName) Error() string { +func (e DuplicateHookNameError) Error() string { return fmt.Sprintf("duplicate hook name: %s", e.Name) } From 6a335f594b73c1172cb3261f25ca6845208bff4c Mon Sep 17 00:00:00 2001 From: mazrean Date: Wed, 6 Mar 2024 01:55:38 +0900 Subject: [PATCH 7/7] short benchmark --- .github/workflows/benchmark.yaml | 2 +- formstream_test.go | 18 ++++++++++++++++++ http/parser_test.go | 21 +++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yaml b/.github/workflows/benchmark.yaml index 81b52fe..2712b45 100644 --- a/.github/workflows/benchmark.yaml +++ b/.github/workflows/benchmark.yaml @@ -20,7 +20,7 @@ jobs: mkdir -p ./profile { echo "stdout<> $GITHUB_OUTPUT id: bench diff --git a/formstream_test.go b/formstream_test.go index 7f1d3f8..ca6b40c 100644 --- a/formstream_test.go +++ b/formstream_test.go @@ -149,9 +149,15 @@ func BenchmarkFormStreamFastPath(b *testing.B) { benchmarkFormStream(b, 1*formstream.GB, false) }) b.Run("5GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkFormStream(b, 5*formstream.GB, false) }) b.Run("10GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkFormStream(b, 10*formstream.GB, false) }) } @@ -170,9 +176,15 @@ func BenchmarkFormStreamSlowPath(b *testing.B) { benchmarkFormStream(b, 1*formstream.GB, true) }) b.Run("5GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkFormStream(b, 5*formstream.GB, true) }) b.Run("10GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkFormStream(b, 10*formstream.GB, true) }) } @@ -231,9 +243,15 @@ func BenchmarkStdMultipartReadForm(b *testing.B) { benchmarkStdMultipartReadForm(b, 1*formstream.GB) }) b.Run("5GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkStdMultipartReadForm(b, 5*formstream.GB) }) b.Run("10GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkStdMultipartReadForm(b, 10*formstream.GB) }) } diff --git a/http/parser_test.go b/http/parser_test.go index 99cc072..877ab79 100644 --- a/http/parser_test.go +++ b/http/parser_test.go @@ -269,9 +269,15 @@ func BenchmarkFormStreamFastPath(b *testing.B) { benchmarkFormStream(b, 1*formstream.GB, false) }) b.Run("5GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkFormStream(b, 5*formstream.GB, false) }) b.Run("10GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkFormStream(b, 10*formstream.GB, false) }) } @@ -290,9 +296,15 @@ func BenchmarkFormStreamSlowPath(b *testing.B) { benchmarkFormStream(b, 1*formstream.GB, true) }) b.Run("5GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkFormStream(b, 5*formstream.GB, true) }) b.Run("10GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkFormStream(b, 10*formstream.GB, true) }) } @@ -371,8 +383,17 @@ func BenchmarkStdMultipartReadForm(b *testing.B) { benchmarkStdMultipartReadForm(b, 1*formstream.GB) }) b.Run("5GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } benchmarkStdMultipartReadForm(b, 5*formstream.GB) }) + b.Run("10GB", func(b *testing.B) { + if testing.Short() { + b.Skip("skipping test in short mode.") + } + benchmarkStdMultipartReadForm(b, 10*formstream.GB) + }) } func benchmarkStdMultipartReadForm(b *testing.B, fileSize formstream.DataSize) {