Skip to content

Commit 0ae8d5e

Browse files
authored
fix: prevent races from processing build logs after channel close (#4984)
1 parent 3c10c7f commit 0ae8d5e

File tree

1 file changed

+28
-0
lines changed

1 file changed

+28
-0
lines changed

coderd/provisionerjobs.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010
"sort"
1111
"strconv"
12+
"sync"
1213
"time"
1314

1415
"github.com/google/uuid"
@@ -371,6 +372,7 @@ func (api *API) followLogs(jobID uuid.UUID) (<-chan database.ProvisionerJobLog,
371372
var (
372373
closed = make(chan struct{})
373374
bufferedLogs = make(chan database.ProvisionerJobLog, 128)
375+
logMut = &sync.Mutex{}
374376
)
375377
closeSubscribe, err := api.Pubsub.Subscribe(
376378
provisionerJobLogsChannel(jobID),
@@ -380,12 +382,14 @@ func (api *API) followLogs(jobID uuid.UUID) (<-chan database.ProvisionerJobLog,
380382
return
381383
default:
382384
}
385+
383386
jlMsg := provisionerJobLogsMessage{}
384387
err := json.Unmarshal(message, &jlMsg)
385388
if err != nil {
386389
logger.Warn(ctx, "invalid provisioner job log on channel", slog.Error(err))
387390
return
388391
}
392+
389393
if jlMsg.CreatedAfter != 0 {
390394
logs, err := api.Database.GetProvisionerLogsByIDBetween(ctx, database.GetProvisionerLogsByIDBetweenParams{
391395
JobID: jobID,
@@ -397,6 +401,18 @@ func (api *API) followLogs(jobID uuid.UUID) (<-chan database.ProvisionerJobLog,
397401
}
398402

399403
for _, log := range logs {
404+
// Sadly we have to use a mutex here because events may be
405+
// handled out of order due to golang goroutine scheduling
406+
// semantics (even though Postgres guarantees ordering of
407+
// notifications).
408+
logMut.Lock()
409+
select {
410+
case <-closed:
411+
logMut.Unlock()
412+
return
413+
default:
414+
}
415+
400416
select {
401417
case bufferedLogs <- log:
402418
logger.Debug(ctx, "subscribe buffered log", slog.F("stage", log.Stage))
@@ -406,12 +422,24 @@ func (api *API) followLogs(jobID uuid.UUID) (<-chan database.ProvisionerJobLog,
406422
// so just drop them.
407423
logger.Warn(ctx, "provisioner job log overflowing channel")
408424
}
425+
logMut.Unlock()
409426
}
410427
}
428+
411429
if jlMsg.EndOfLogs {
430+
// This mutex is to guard double-closes.
431+
logMut.Lock()
432+
select {
433+
case <-closed:
434+
logMut.Unlock()
435+
return
436+
default:
437+
}
412438
logger.Debug(ctx, "got End of Logs")
439+
413440
close(closed)
414441
close(bufferedLogs)
442+
logMut.Unlock()
415443
}
416444
},
417445
)

0 commit comments

Comments
 (0)