Skip to content

Commit 0bd76d6

Browse files
committed
fix(listener): warn on dropped coverage signals instead of ignoring
The listener's 1000-slot signal buffer could overflow under heavy load. Dropped signals were pushed to an errors channel that was never drained, so lost coverage was completely invisible to the user. Replace the dead-letter error channel push with an atomic counter (sync/atomic.Int64) on the Listener struct. Add DroppedSignals() method to query the count. In executor.go, check the counter after CollectSignals and print a WARNING with the test name and drop count when non-zero. The warning is unconditional (not gated by --verbose) since lost coverage data always warrants user attention.
1 parent d51d3bf commit 0bd76d6

2 files changed

Lines changed: 23 additions & 10 deletions

File tree

internal/database/listener.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package database
33
import (
44
"context"
55
"fmt"
6+
"sync/atomic"
67
"time"
78

89
"github.com/cybertec-postgresql/pgcov/pkg/types"
@@ -13,11 +14,12 @@ import (
1314

1415
// Listener handles PostgreSQL LISTEN/NOTIFY for coverage signals
1516
type Listener struct {
16-
conn *pgx.Conn
17-
channel string
18-
signals chan types.CoverageSignal
19-
errors chan error
20-
done chan struct{}
17+
conn *pgx.Conn
18+
channel string
19+
signals chan types.CoverageSignal
20+
errors chan error
21+
done chan struct{}
22+
droppedSignals atomic.Int64
2123
}
2224

2325
// NewListener creates a new LISTEN/NOTIFY listener using the config from a pool.
@@ -105,11 +107,9 @@ func (l *Listener) receiveLoop(ctx context.Context) {
105107
select {
106108
case l.signals <- signal:
107109
default:
108-
// Buffer full, log warning but don't block
109-
select {
110-
case l.errors <- fmt.Errorf("signal buffer full, dropping signal: %s", notification.Payload):
111-
default:
112-
}
110+
// Buffer full — increment counter so the caller can
111+
// detect and report lost signals after test execution.
112+
l.droppedSignals.Add(1)
113113
}
114114
}
115115
}
@@ -126,6 +126,13 @@ func (l *Listener) Errors() <-chan error {
126126
return l.errors
127127
}
128128

129+
// DroppedSignals returns the number of signals that were dropped because
130+
// the internal buffer was full. A non-zero value means coverage data is
131+
// incomplete.
132+
func (l *Listener) DroppedSignals() int64 {
133+
return l.droppedSignals.Load()
134+
}
135+
129136
// Close stops the listener and closes the connection
130137
func (l *Listener) Close(ctx context.Context) error {
131138
close(l.done)

internal/runner/executor.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ func (e *Executor) executeTestWorkflow(ctx context.Context, testRun *TestRun, so
261261
fmt.Printf("[DEBUG] Collected %d signals\n", len(signals))
262262
}
263263

264+
// Check for dropped signals — warns about incomplete coverage data
265+
if dropped := listener.DroppedSignals(); dropped > 0 {
266+
fmt.Printf("[WARNING] %d coverage signal(s) dropped for %s (buffer full) — coverage data is incomplete\n",
267+
dropped, testRun.Test.RelativePath)
268+
}
269+
264270
// Append NOTIFY signals to the implicit coverage signals
265271
testRun.CoverageSigs = append(testRun.CoverageSigs, signals...)
266272

0 commit comments

Comments
 (0)