generated from alecodes/base-template
feat: add basic worker pool
This commit is contained in:
parent
d1a0212cb1
commit
56d2e72528
3 changed files with 144 additions and 12 deletions
|
|
@ -164,15 +164,15 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pokedex := range pokeApi.Collections {
|
// for _, pokedex := range pokeApi.Collections {
|
||||||
if pokedex.IsDefault() {
|
// if pokedex.IsDefault() {
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
err = pokedex.FetchNodes(getPokemons, synchronizator.StartPagination)
|
// err = pokedex.FetchNodes(getPokemons, synchronizator.StartPagination)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
fmt.Println(err)
|
// fmt.Println(err)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
109
pkg/fetcher.go
109
pkg/fetcher.go
|
|
@ -1,5 +1,24 @@
|
||||||
package synchronizator
|
package synchronizator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"iter"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fetcher is the concurrent manager
|
||||||
|
// upon invocation, should create a worker pool of 1 to get the first set of results
|
||||||
|
// then base on the Patination Total and Limit, should distribute the workload
|
||||||
|
//
|
||||||
|
// It also needs to handle errors, rate-limits, retries strategies, and gracefull rejections
|
||||||
|
//
|
||||||
|
// It should return the pages not fetched for later retry
|
||||||
|
//
|
||||||
|
// Pagination should include a max-concurrent connection and rate-limit
|
||||||
|
// configuration to prevent having errors from external sources
|
||||||
|
//
|
||||||
|
// Maybe change the name to pagination or embed in another struct
|
||||||
|
|
||||||
type Fetcher = func(pagination Pagination) ([]*Collection, Pagination, error)
|
type Fetcher = func(pagination Pagination) ([]*Collection, Pagination, error)
|
||||||
|
|
||||||
type Pagination struct {
|
type Pagination struct {
|
||||||
|
|
@ -15,3 +34,93 @@ var StartPagination = Pagination{
|
||||||
Limit: 10,
|
Limit: 10,
|
||||||
Offset: 0,
|
Offset: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Work[T any] func(value T) T
|
||||||
|
|
||||||
|
type Worker[T any] struct {
|
||||||
|
id uint8
|
||||||
|
receptor <-chan T
|
||||||
|
transmiter chan<- T
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
work Work[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorkerManager[T any] struct {
|
||||||
|
active_workers sync.WaitGroup
|
||||||
|
is_open_to_work bool
|
||||||
|
workers_receptor chan T
|
||||||
|
workers_transmiter chan T
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *WorkerManager[T]) AddWork(value T) error {
|
||||||
|
if !manager.is_open_to_work {
|
||||||
|
return fmt.Errorf("The manager is closed to add more work.")
|
||||||
|
}
|
||||||
|
manager.workers_receptor <- value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *WorkerManager[T]) Stop() {
|
||||||
|
// Stop receiving new units of work
|
||||||
|
manager.is_open_to_work = false
|
||||||
|
close(manager.workers_receptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *WorkerManager[T]) GetWorkUnit() iter.Seq[T] {
|
||||||
|
// send a message through the done channel when all workers have stopped
|
||||||
|
done_channel := make(chan bool)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
manager.active_workers.Wait()
|
||||||
|
close(done_channel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
manager.Stop()
|
||||||
|
|
||||||
|
return func(yield func(T) bool) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case value := <-manager.workers_transmiter:
|
||||||
|
if !yield(value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-done_channel:
|
||||||
|
close(manager.workers_transmiter)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func spawn_worker[T any](worker *Worker[T]) {
|
||||||
|
defer worker.wg.Done()
|
||||||
|
|
||||||
|
for unit := range worker.receptor {
|
||||||
|
worker.transmiter <- worker.work(unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createWorkerPool[T any](max_workers uint8, work Work[T]) *WorkerManager[T] {
|
||||||
|
manager := &WorkerManager[T]{
|
||||||
|
workers_receptor: make(chan T, max_workers),
|
||||||
|
workers_transmiter: make(chan T, max_workers),
|
||||||
|
}
|
||||||
|
|
||||||
|
// create pool of workers
|
||||||
|
for i := range max_workers {
|
||||||
|
worker := &Worker[T]{
|
||||||
|
id: uint8(i),
|
||||||
|
receptor: manager.workers_receptor,
|
||||||
|
transmiter: manager.workers_transmiter,
|
||||||
|
wg: &manager.active_workers,
|
||||||
|
work: work,
|
||||||
|
}
|
||||||
|
|
||||||
|
go spawn_worker(worker)
|
||||||
|
manager.active_workers.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.is_open_to_work = true
|
||||||
|
|
||||||
|
return manager
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package synchronizator
|
package synchronizator
|
||||||
|
|
||||||
import "slices"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
// Utility struct to represent a collection of nodes, it's a [Node] itself so all
|
// Utility struct to represent a collection of nodes, it's a [Node] itself so all
|
||||||
// the node's functionality is available.
|
// the node's functionality is available.
|
||||||
|
|
@ -10,6 +13,26 @@ type Platform struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (platform *Platform) FetchCollections(fetcher Fetcher, start_pagination Pagination) error {
|
func (platform *Platform) FetchCollections(fetcher Fetcher, start_pagination Pagination) error {
|
||||||
|
square := func(value int) int {
|
||||||
|
return value * value
|
||||||
|
}
|
||||||
|
|
||||||
|
manager := createWorkerPool[int](5, square)
|
||||||
|
|
||||||
|
err := manager.AddWork(5)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for value := range manager.GetWorkUnit() {
|
||||||
|
fmt.Printf("%v: Concurrent value: %v \n", start_pagination.Offset, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = manager.AddWork(45)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
collections, pagination, err := fetcher(start_pagination)
|
collections, pagination, err := fetcher(start_pagination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
Reference in a new issue