-
Notifications
You must be signed in to change notification settings - Fork 8
fixed size worker pool to serve new job requests #72
Conversation
cmd/mistryd/server.go
Outdated
type WorkResult struct { | ||
BuildInfo *types.BuildInfo | ||
Err error | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since BuildInfo
already contains an .Err
, can't we just use BuildInfo
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BuildInfo.Err contains build errors. The outer error is about failing to retrieve or construct the BuildInfo.
This struct captures the result of Work() (BuildInfo, error)
which needs to be forwarded through a channel, so I'm capturing the two output parameters into a struct to pass it as one unit through channels
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, fair enough.
cmd/mistryd/server.go
Outdated
type WorkItem struct { | ||
Job *Job | ||
JobRequest types.JobRequest | ||
ResultChan chan WorkResult |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ResultChan -> Result
cmd/mistryd/server.go
Outdated
// WorkItem contains a job and a channel to place the job result | ||
type WorkItem struct { | ||
Job *Job | ||
JobRequest types.JobRequest |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
*types.JobRequest
cmd/mistryd/server.go
Outdated
for i := 0; i < s.maxWorkers; i++ { | ||
go s.worker(i) | ||
} | ||
s.Log.Printf("Setup %d workers", s.maxWorkers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] "Set up" or "Spawned"
cmd/mistryd/server.go
Outdated
@@ -87,6 +92,9 @@ func NewServer(cfg *Config, logger *log.Logger) (*Server, error) { | |||
s.pq = NewProjectQueue() | |||
s.br = broker.NewBroker(s.Log) | |||
s.tq = new(sync.Map) | |||
s.workQueueSize = 100 | |||
s.maxWorkers = 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should just be a cfg.GlobalBuildConcurrency
or something.
cmd/mistryd/server.go
Outdated
s.Log.Printf("%s work items channel closed, returning...", logPrefix) | ||
} | ||
|
||
func (s *Server) writeJobResultResponse(j *Job, r WorkResult, w http.ResponseWriter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure this plays correct? I'd expect it to receive a pointer to the ResponseWriter, not a copy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same goes for WorkResult.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ResponseWriter is an interface. The WorkResult doesn't need to be mutated so we don't need to pass a pointer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
j isn't mutated either.
cmd/mistryd/server.go
Outdated
s.Log.Printf("%s work items channel closed, returning...", logPrefix) | ||
} | ||
|
||
func (s *Server) writeJobResultResponse(j *Job, r WorkResult, w http.ResponseWriter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps writeWorkResult()
?
cmd/mistryd/server.go
Outdated
go s.br.ListenForClients() | ||
return s.srv.ListenAndServe() | ||
} | ||
|
||
func (s *Server) setupWorkerPool() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is too small and I think it should be inlined to ListenAndServe
. We're not going to use it anywhere else for sure.
cmd/mistryd/server.go
Outdated
ResultChan chan WorkResult | ||
} | ||
|
||
func (s *Server) worker(id int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a more accurate name (work?) and documentation. Also, do we need to have an id
?
cmd/mistryd/server.go
Outdated
} | ||
|
||
func (s *Server) worker(id int) { | ||
logPrefix := fmt.Sprintf("Worker %d:", id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We tend to prefix things like this:
[worker] foo
38b925e
to
d8bb7f5
Compare
cmd/mistryd/config.go
Outdated
@@ -21,6 +22,9 @@ type Config struct { | |||
ProjectsPath string `json:"projects_path"` | |||
BuildPath string `json:"build_path"` | |||
Mounts map[string]string `json:"mounts"` | |||
|
|||
PoolMaxWorkers int `json:"pool_max_workers"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not expose implementation details to the user; we can use "Concurrency" or "BuildConcurrency" or "GlobalBuildConcurrency".
cmd/mistryd/config.go
Outdated
@@ -21,6 +22,9 @@ type Config struct { | |||
ProjectsPath string `json:"projects_path"` | |||
BuildPath string `json:"build_path"` | |||
Mounts map[string]string `json:"mounts"` | |||
|
|||
PoolMaxWorkers int `json:"pool_max_workers"` | |||
PoolMaxQueueSize int `json:"pool_max_queue_size"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't this configuration is need to be exposed to the user at all. We can use a reasonable fixed number for it; I believe 100
is a sane choice.
cmd/mistryd/config.sample.json
Outdated
} | ||
}, | ||
"pool_max_workers": 5, | ||
"pool_max_queue_size": 100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also update config.test.json
accordingly.
cmd/mistryd/server.go
Outdated
jq *JobQueue | ||
pq *ProjectQueue | ||
cfg *Config | ||
WorkerPool *WorkerPool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason this is exposed?
cmd/mistryd/server.go
Outdated
return | ||
} | ||
|
||
// if async, we're done, otherwise wait for the result in the result channel | ||
if _, isAsync := r.URL.Query()["async"]; isAsync { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[out of scope] I think this should be renamed to "async". Also, we should be consistent in our style: the if ...
should be on a new line.
cmd/mistryd/worker_pool.go
Outdated
return p | ||
} | ||
|
||
// Stop stops the pool workers and closes the queue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"closes the queue" is implementation detail. How about something like:
Stop signals the workers to close. Stop does not block until they are closed.
cmd/mistryd/worker_pool.go
Outdated
close(p.queue) | ||
} | ||
|
||
// SendWork sends work to the pool, and receives a work result that can be waited on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SendWork schedules work on p and returns a FutureWorkResult. The actual result can be obtained by FutureWorkResult.Wait(). [...]
cmd/mistryd/worker_pool.go
Outdated
|
||
// SendWork sends work to the pool, and receives a work result that can be waited on. | ||
// An error is returned if the backlog is full and cannot accept any new work items | ||
func (p *WorkerPool) SendWork(j *Job, jr types.JobRequest) (FutureWorkResult, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we somehow make this agnostic to Job
and types.JobRequest
? I think we can. Couldn't this accept a WorkItem
? This would seem more intuitive and better since we get rid of the tight coupling with the server.
cmd/mistryd/worker_pool.go
Outdated
case p.queue <- wi: | ||
return result, nil | ||
default: | ||
return result, fmt.Errorf("queue is full") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to use Errorf
since you don't format anything. Use errors.New
. I think go vet
should fail here anyway.
cmd/mistryd/worker_pool.go
Outdated
|
||
// poolWorker listens to the workQueue, runs Work() on any incoming work items, and | ||
// sends the result through the result queue | ||
func poolWorker(s *Server, id int, workQueue <-chan workItem) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Maybe just
spawn
orstart
is a better name? - Also, same goes here regarding to
Server
. I think this is unnecessary coupling that we could and should avoid. - "workQueue" could be renamed to the simpler "queue". The kind of thing that's queued in there is apparent from the type.
#!/bin/bash | ||
set -e | ||
|
||
sleep 10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] consider putting a newline at the end of the file
cmd/mistryd/worker_pool.go
Outdated
p.wg.Wait() | ||
} | ||
|
||
// SendWork chedules work on p and returns a FutureWorkResult. The actual result can be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
replace chedules
with schedules
🐦
cfg.Backlog = backlog | ||
|
||
s, err := NewServer(cfg, nil) | ||
failIfError(err, t) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
As a side note, I opened an issue #74 to package and refactor the test helpers.
LGTM 👍 |
* X amount of goroutines listen to a queue for new job requests * HTTP new job requests place the work into the queue * the result is communicated back to the HTTP request goroutine through a per-request unique result channel
Based on https://gobyexample.com/worker-pools, with error and result handling improvements
a per-request unique result channel