I want to process from channel if channel is full or certain time is elapsed. My use case is similar to existing question and I have tried to modify the existing ans
My code is at https://go.dev/play/p/HaGZ9HHqj0i ,
package main
import (
"fmt"
"sync"
"time"
)
type Audit struct {
ID int
}
const batchSize = 5
var batch = make([]Audit, 0, batchSize)
func upsertBigQueryAudits(audits []Audit) {
fmt.Printf("Processing batch of %d\n", len(audits))
for _, a := range audits {
fmt.Printf("%d ", a.ID)
}
fmt.Println()
batch = []Audit{}
}
func process(full <-chan struct{}) {
ticker := time.NewTicker(1 * time.Nanosecond)
for {
select {
case <-full:
fmt.Println("From full")
upsertBigQueryAudits(batch)
case <-ticker.C:
fmt.Println("From ticker")
if len(batch) > 0 {
fmt.Println("From ticker sending batch")
upsertBigQueryAudits(batch)
}
}
}
}
func processAudits(audits <-chan Audit, full chan<- struct{}, batchSize int) {
for audit := range audits {
batch = append(batch, audit)
if len(batch) == cap(batch) {
// upsertBigQueryAudits(batch)
fmt.Println("Sending full")
full <- struct{}{}
}
}
}
func produceAudits(x int, to chan Audit) {
for i := 0; i < x; i++ {
to <- Audit{
ID: i,
}
}
}
func main() {
var wg sync.WaitGroup
audits := make(chan Audit)
full := make(chan struct{})
wg.Add(1)
go func() {
defer wg.Done()
process(full)
}()
wg.Add(1)
go func() {
defer wg.Done()
processAudits(audits, full, batchSize)
}()
wg.Add(1)
go func() {
defer wg.Done()
produceAudits(25, audits)
close(audits)
}()
wg.Wait()
fmt.Println("Complete")
}
Here I want to process the batch when it is filled with batchSize
elements Or when certain time (for eg 1 nano second) is elapsed.
Edit: Added my concern inline. This is sample output.
timeout running program
Sending full <--- this line comes twice
Sending full
From full
Processing batch of 10 <--- instead of 5 it is processing 10 items
0 1 2 3 4 5 6 7 8 9
From full
Processing batch of 1
10
Sending full
Sending full
From full
Processing batch of 1 <-- instead of 5 it is processing 1 items. It does not process more than 1 item.
11
From full
Processing batch of 0
...
...
Sending full
Sending full
From full
Processing batch of 1 <-- instead of 5 it is processing 1 items
24
From full
Processing batch of 0
From ticker <-- this line comes only after consuming 25 items.
From ticker
From ticker
From ticker
I found answer at https://elliotchance.medium.com/batch-a-channel-by-size-or-time-in-go-92fa3098f65
package main
import (
"context"
"fmt"
"time"
)
func BatchStringsCtx[T any](ctx context.Context, values <-chan T, maxItems int, maxTimeout time.Duration) chan []T {
batches := make(chan []T)
go func() {
defer close(batches)
for keepGoing := true; keepGoing; {
var batch []T
expire := time.After(maxTimeout)
for {
select {
case <-ctx.Done():
keepGoing = false
goto done
case value, ok := <-values:
if !ok {
keepGoing = false
goto done
}
batch = append(batch, value)
if len(batch) == maxItems {
goto done
}
case <-expire:
goto done
}
}
done:
if len(batch) > 0 {
batches <- batch
}
}
}()
return batches
}
func main() {
strings := make(chan string)
go func() {
strings <- "hello"
strings <- "there" // hit limit of 2
strings <- "how"
time.Sleep(15 * time.Millisecond) // hit timeout
strings <- "are"
strings <- "you" // hit limit of 2
// A really long time without any new values.
// The context was cancelled around 300ms,
// before this sleep finished.
time.Sleep(500 * time.Millisecond)
strings <- "doing?" // never read
close(strings)
}()
//ctx, cancel := context.WithTimeout(context.Background(), 300 * time.Millisecond)
ctx := context.Background()
start := time.Now()
batches := BatchStringsCtx[string](ctx, strings, 2, 10*time.Millisecond)
for batch := range batches {
fmt.Println(time.Now().Sub(start), batch)
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.