Worker Pool Pattern in Go Lang

Worker Pool Pattern in Go Lang

Mentor

Blog

Dealing with numerous tasks using a limited number of resources is always a challenging thing. In Go, we would always like to use more go routines, but the catch here is what type of data we are dealing with, how much time it will take to complete the task, can we hold resources for an infinite time, and how much memory a fired go routine consumes. Do we have enough memory to serve all fired go routines? etc..

An OOM issue can occur if we don't have enough memory and go routines are executing the tasks for an extended time, and it may halt the current executions.

To avoid such kind of issues, we can make use of the worker pool pattern in Go. For example, if you have opened a restaurant in a prime place but have a limited number of staff, how would you serve all customers without losing business?

Here staff needs to serve concurrently to the customers so they don't feel any delay in serving.We can apply a similar pattern in Go to create a worker pool pattern.

let’s jump into the code without any further explanations 😋

package main

import (
"context"
"errors"
"fmt"
"time"
)

func main() {
maxWorkers := 3
ctx := context.Background()

// initialise the worker pool setup
h := NewHotelStaff(maxWorkers)

for i := 0; i < maxWorkers; i++ {
// start workers
go h.Run(ctx, i+1)
}

// listen to errors
go h.ListerToComplaints(ctx)

// trigger jobs
for i := 1; i <= 10; i++ {
k := i
task := func() (int, error) {
if k%3 == 0 {
return k, errors.New("error..!!")
}

return k, nil
}

h.Task <- task
}
time.Sleep(time.Second * 5)
fmt.Println("hotel is closed...!! see you tomorrow ...!!!")

}

type HotelStaff struct {
NoWorkers int
Task chan func() (int, error)
err chan error
}

func NewHotelStaff(noWorkers int) *HotelStaff {
return &HotelStaff{NoWorkers: noWorkers,
Task: make(chan func() (int, error), noWorkers), err: make(chan error, noWorkers)}
}

func (h *HotelStaff) Run(ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
return

case x, ok := <-h.Task:
if !ok {
return
}
tableNo, err := x()
fmt.Printf("%d table order served by %d\n", tableNo, id)
if err != nil {
h.err <- fmt.Errorf("A compliant raised from table %d which is served by %d", tableNo, id)
}
}
}
}

func (h *HotelStaff) ListerToComplaints(ctx context.Context) {
for {
select {
case <-ctx.Done():
return

case x, ok := <-h.err:
if !ok {
return
}
fmt.Println(x)
}
}
}
1 table order served by 3
4 table order served by 3
2 table order served by 2
3 table order served by 1
7 table order served by 1
8 table order served by 1
9 table order served by 1
10 table order served by 1
5 table order served by 3
6 table order served by 2
A compliant raised from table 3 which is served by 1
A compliant raised from table 9 which is served by 1
A compliant raised from table 6 which is served by 2
hotel is closed...!! see you tomorrow ...!!!