@@ -28,13 +28,8 @@ type Job struct {
28
28
29
29
// Stats holds basic stats for the worker
30
30
type Stats struct {
31
- ProcessedJobs int64
32
- TotalElapsedTime int64 // NOTE: nanoseconds
33
- }
34
-
35
- type workerStats struct {
36
- Stats
37
- mx sync.RWMutex
31
+ ProcessedJobs int64
32
+ TotalJobsExecutionTime int64 // NOTE: nanoseconds
38
33
}
39
34
40
35
// Config holds needed data to start worker pool
@@ -43,8 +38,14 @@ type Config struct {
43
38
MaxJobs int64
44
39
}
45
40
46
- // WorkerPool holds data needed for pool operation
47
- type WorkerPool struct {
41
+ type workerStats struct {
42
+ Stats
43
+ mx sync.RWMutex
44
+ }
45
+
46
+ // workerPool holds data needed for pool operation
47
+ type workerPool struct {
48
+ mx sync.RWMutex
48
49
config Config
49
50
workersChan []chan Job
50
51
workersStats []* workerStats
@@ -60,7 +61,7 @@ func worker(jobs chan Job, terminate chan bool, s *workerStats) {
60
61
elapsedTime := time .Since (start ).Nanoseconds ()
61
62
s .mx .Lock ()
62
63
s .ProcessedJobs ++
63
- s .TotalElapsedTime += elapsedTime
64
+ s .TotalJobsExecutionTime += elapsedTime
64
65
s .mx .Unlock ()
65
66
j .ResultChan <- res
66
67
close (j .ResultChan )
@@ -72,69 +73,58 @@ func worker(jobs chan Job, terminate chan bool, s *workerStats) {
72
73
}
73
74
}
74
75
75
- // New created new worker pool
76
- func New (config Config ) * WorkerPool {
77
- pool := & WorkerPool {
78
- config : config ,
79
- workersChan : make ([]chan Job , config .NWorkers ),
80
- workersStats : make ([]* workerStats , config .NWorkers ),
81
- terminateChan : make ([]chan bool , config .NWorkers ),
82
- }
83
- for w := 0 ; w < config .NWorkers ; w ++ {
84
- pool .workersChan [w ] = make (chan Job , config .MaxJobs )
85
- pool .terminateChan [w ] = make (chan bool )
86
- pool .workersStats [w ] = & workerStats {}
87
- go worker (
88
- pool .workersChan [w ],
89
- pool .terminateChan [w ],
90
- pool .workersStats [w ],
91
- )
92
- }
93
- return pool
94
- }
76
+ // getCurrentJobsAmount calculates amount of jobs across all workers queues
77
+ func (wp * workerPool ) getCurrentJobsAmount () int64 {
78
+ wp .mx .RLock ()
79
+ defer wp .mx .RUnlock ()
95
80
96
- // GetCurrentJobsNumber calculates amount of jobs across all workers queues
97
- func (wp WorkerPool ) GetCurrentJobsNumber () int64 {
98
- var currentJobsN int64 = 0
81
+ var currentJobsAmount int64 = 0
99
82
for _ , ch := range wp .workersChan {
100
- currentJobsN += int64 (len (ch ))
83
+ currentJobsAmount += int64 (len (ch ))
101
84
}
102
- return currentJobsN
85
+ return currentJobsAmount
103
86
}
104
87
105
- // GetWorkerStats by specified worker id
106
- func (wp * WorkerPool ) GetWorkerStats (workerId int ) (Stats , error ) {
88
+ // getWorkerStats by specified worker id
89
+ func (wp * workerPool ) getWorkerStats (workerId int ) (Stats , error ) {
107
90
resStats := Stats {}
108
91
if workerId > len (wp .workersChan ) || workerId < 0 {
109
92
return resStats , workerIndexIsOutOfBoundsErr
110
93
}
94
+ wp .mx .RLock ()
111
95
s := wp .workersStats [workerId ]
96
+ wp .mx .RUnlock ()
97
+
112
98
s .mx .RLock ()
113
99
defer s .mx .RUnlock ()
114
100
resStats .ProcessedJobs = s .ProcessedJobs
115
- resStats .TotalElapsedTime = s .TotalElapsedTime
101
+ resStats .TotalJobsExecutionTime = s .TotalJobsExecutionTime
116
102
return resStats , nil
117
103
}
118
104
119
- // TerminateWorker sends termination signal to the specified worker
120
- func (wp * WorkerPool ) TerminateWorker (workerId int ) error {
105
+ // terminateWorker sends termination signal to the specified worker
106
+ func (wp * workerPool ) terminateWorker (workerId int ) error {
121
107
if workerId > len (wp .terminateChan ) || workerId < 0 {
122
108
return workerIndexIsOutOfBoundsErr
123
109
}
110
+ wp .mx .RLock ()
124
111
wp .terminateChan [workerId ] <- true
112
+ wp .mx .RUnlock ()
125
113
return nil
126
114
}
127
115
128
- // ReloadWorker terminates worker by id, and spawns new one
129
- func (wp * WorkerPool ) ReloadWorker (workerId int ) error {
116
+ // reloadWorker terminates worker by id, and spawns new one
117
+ func (wp * workerPool ) reloadWorker (workerId int ) error {
130
118
if workerId > len (wp .workersChan ) || workerId < 0 {
131
119
return workerIndexIsOutOfBoundsErr
132
120
}
133
- err := wp .TerminateWorker (workerId )
121
+ err := wp .terminateWorker (workerId )
134
122
if err != nil {
135
123
return err
136
124
}
125
+ wp .mx .Lock ()
137
126
wp .workersStats [workerId ] = & workerStats {}
127
+ wp .mx .Unlock ()
138
128
go worker (
139
129
wp .workersChan [workerId ],
140
130
wp .terminateChan [workerId ],
@@ -143,45 +133,87 @@ func (wp *WorkerPool) ReloadWorker(workerId int) error {
143
133
return nil
144
134
}
145
135
146
- // Manafer interface that holds implementation of balancing strategy
147
- type Manager interface {
148
- ScheduleJob ( f JobFunc ) ( chan Result , error )
136
+ // Strategy interface that holds implementation of balancing strategy
137
+ type BalancingStrategy interface {
138
+ NextWorkerId ( workersStats [] Stats ) int
149
139
}
150
140
151
- /*------------------------------------------------------------------------*/
152
- // NOTE: default implmementation of the Manager
153
-
154
- // RoundRobin evenly distribute jobs across workers
155
- type RoundRobin struct {
156
- * WorkerPool
157
- mx sync.RWMutex
158
- nextWorkerId int
141
+ // Manager spawns workers and schedule jobs
142
+ type Manager struct {
143
+ wp * workerPool
144
+ balancer BalancingStrategy
159
145
}
160
146
161
- // NewRoundRobin ...
162
- func NewRoundRobin (pool * WorkerPool ) * RoundRobin {
163
- return & RoundRobin {
164
- WorkerPool : pool ,
165
- nextWorkerId : 0 ,
147
+ // New creates new worker pool
148
+ func New (config Config , balancer BalancingStrategy ) * Manager {
149
+ pool := & workerPool {
150
+ config : config ,
151
+ workersChan : make ([]chan Job , config .NWorkers ),
152
+ workersStats : make ([]* workerStats , config .NWorkers ),
153
+ terminateChan : make ([]chan bool , config .NWorkers ),
166
154
}
155
+ for w := 0 ; w < config .NWorkers ; w ++ {
156
+ pool .workersChan [w ] = make (chan Job , config .MaxJobs )
157
+ pool .terminateChan [w ] = make (chan bool )
158
+ pool .workersStats [w ] = & workerStats {}
159
+ go worker (
160
+ pool .workersChan [w ],
161
+ pool .terminateChan [w ],
162
+ pool .workersStats [w ],
163
+ )
164
+ }
165
+ manager := & Manager {
166
+ wp : pool ,
167
+ balancer : balancer ,
168
+ }
169
+ // TODO: add routine that reloads workers
170
+ return manager
167
171
}
168
172
169
173
// ScheduleJob puts job in a queue
170
- func (rr * RoundRobin ) ScheduleJob (f JobFunc ) (chan Result , error ) {
171
- rr .mx .RLock ()
172
- config := rr .config
173
- nextWorkerId := rr .nextWorkerId
174
- rr .mx .RUnlock ()
175
-
176
- currentJobsN := rr .GetCurrentJobsNumber ()
177
- if currentJobsN >= config .MaxJobs {
174
+ func (m * Manager ) ScheduleJob (f JobFunc ) (chan Result , error ) {
175
+ m .wp .mx .RLock ()
176
+ config := m .wp .config
177
+ workersStats := make ([]Stats , config .NWorkers )
178
+ for i , s := range m .wp .workersStats {
179
+ s .mx .RLock ()
180
+ workersStats [i ] = s .Stats
181
+ s .mx .RUnlock ()
182
+ }
183
+ m .wp .mx .RUnlock ()
184
+
185
+ currentJobsAmount := m .wp .getCurrentJobsAmount ()
186
+ if currentJobsAmount >= config .MaxJobs {
178
187
return nil , maxJobsLimitReachedErr
179
188
}
180
189
ch := make (chan Result )
181
- rr .workersChan [nextWorkerId ] <- Job {f , ch }
182
190
183
- rr .mx .Lock ()
184
- rr .nextWorkerId = (nextWorkerId + 1 ) % config .NWorkers
185
- rr .mx .Unlock ()
191
+ nextWorkerId := m .balancer .NextWorkerId (workersStats )
192
+ m .wp .workersChan [nextWorkerId ] <- Job {f , ch }
186
193
return ch , nil
187
194
}
195
+
196
+ /*------------------------------------------------------------------------*/
197
+
198
+ // NOTE: default balancing strategy
199
+
200
+ // RoundRobin evenly distributes jobs across workers
201
+ type RoundRobin struct {
202
+ mx sync.RWMutex
203
+ nextWorkerId int
204
+ }
205
+
206
+ // NewRoundRobin ...
207
+ func NewRoundRobin () * RoundRobin {
208
+ return & RoundRobin {nextWorkerId : 0 }
209
+ }
210
+
211
+ // NextWorkerId generates worker id to schedule the job
212
+ func (rr * RoundRobin ) NextWorkerId (workerStats []Stats ) int {
213
+ rr .mx .Lock ()
214
+ defer rr .mx .Unlock ()
215
+
216
+ workerId := rr .nextWorkerId
217
+ rr .nextWorkerId = (workerId + 1 ) % len (workerStats )
218
+ return workerId
219
+ }
0 commit comments