go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/tq/internal/sweep/batching_test.go (about) 1 // Copyright 2020 The LUCI Authors. 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 package sweep 16 17 import ( 18 "context" 19 "fmt" 20 "sync/atomic" 21 "testing" 22 23 taskspb "cloud.google.com/go/cloudtasks/apiv2/cloudtaskspb" 24 "google.golang.org/grpc/codes" 25 "google.golang.org/grpc/status" 26 27 "go.chromium.org/luci/server/tq/internal/reminder" 28 "go.chromium.org/luci/server/tq/internal/testutil" 29 30 . "github.com/smartystreets/goconvey/convey" 31 ) 32 33 func TestBatching(t *testing.T) { 34 t.Parallel() 35 36 Convey("With BatchProcessor", t, func() { 37 ctx := context.Background() 38 db := &testutil.FakeDB{} 39 sub := &submitter{} 40 41 p := BatchProcessor{ 42 Context: ctx, 43 DB: db, 44 Submitter: sub, 45 BatchSize: 3, 46 ConcurrentBatches: 20, 47 } 48 49 makeRem := func(id string) *reminder.Reminder { 50 r := &reminder.Reminder{ID: id} 51 r.AttachPayload(&reminder.Payload{ 52 CreateTaskRequest: &taskspb.CreateTaskRequest{ 53 Parent: id + " body", 54 }, 55 }) 56 So(db.SaveReminder(ctx, r), ShouldBeNil) 57 return r 58 } 59 60 var r []*reminder.Reminder 61 for i := 0; i < 100; i++ { 62 r = append(r, makeRem(fmt.Sprintf("rem-%d", i))) 63 } 64 So(db.AllReminders(), ShouldHaveLength, len(r)) 65 66 Convey("Works", func() { 67 So(p.Start(), ShouldBeNil) 68 p.Enqueue(ctx, r) 69 So(p.Stop(), ShouldEqual, len(r)) 70 So(sub.req, ShouldHaveLength, len(r)) 71 So(db.AllReminders(), ShouldHaveLength, 0) 72 }) 73 74 Convey("Waits for processing to finish", func() { 75 p.BatchSize = 1000 // to make sure Enqueue doesn't block 76 77 var stopped int32 78 79 ch := make(chan struct{}) 80 sub.cb = func(*reminder.Payload) error { 81 if atomic.LoadInt32(&stopped) == 1 { 82 panic("processing while stopped") 83 } 84 ch <- struct{}{} 85 return nil 86 } 87 88 So(p.Start(), ShouldBeNil) 89 p.Enqueue(ctx, r) 90 91 result := make(chan int, 1) 92 go func() { 93 result <- p.Stop() 94 atomic.StoreInt32(&stopped, 1) 95 }() 96 97 for i := 0; i < len(r); i++ { 98 <-ch 99 } 100 So(<-result, ShouldEqual, len(r)) 101 }) 102 103 Convey("Context cancel", func() { 104 ctx, cancel := context.WithCancel(ctx) 105 p.Context = ctx 106 107 var stopped int32 108 var count int32 109 sub.cb = func(*reminder.Payload) error { 110 if atomic.LoadInt32(&stopped) == 1 { 111 panic("processing while stopped") 112 } 113 if count := atomic.AddInt32(&count, 1); count >= 50 { 114 if count > 50 { 115 return status.Errorf(codes.Canceled, "boo") 116 } 117 cancel() 118 } 119 return nil 120 } 121 122 p.Start() 123 p.Enqueue(ctx, r) 124 p.Stop() 125 atomic.StoreInt32(&stopped, 1) 126 127 So(sub.req, ShouldHaveLength, 50) 128 }) 129 }) 130 }