mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 02:22:38 +08:00
unistore: close event stream on context cancelation (#101293)
* add tests for broacaster * fix sql notifier not closing the stream * fix sql notifier not closing the stream * close sub * fix broadcaster test * fix broadcaster test * suggestion
This commit is contained in:
@ -104,3 +104,41 @@ func TestCache(t *testing.T) {
|
|||||||
// slice should return all values
|
// slice should return all values
|
||||||
require.Equal(t, []int{4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, c.Slice())
|
require.Equal(t, []int{4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, c.Slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBroadcaster(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
ch := make(chan int)
|
||||||
|
input := []int{1, 2, 3}
|
||||||
|
go func() {
|
||||||
|
for _, v := range input {
|
||||||
|
ch <- v
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
t.Cleanup(func() {
|
||||||
|
close(ch)
|
||||||
|
})
|
||||||
|
|
||||||
|
b, err := NewBroadcaster(ctx, func(out chan<- int) error {
|
||||||
|
go func() {
|
||||||
|
for v := range ch {
|
||||||
|
out <- v
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
sub, err := b.Subscribe(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, expected := range input {
|
||||||
|
v, ok := <-sub
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, expected, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancel the context should close the stream
|
||||||
|
cancel()
|
||||||
|
_, ok := <-sub
|
||||||
|
require.False(t, ok)
|
||||||
|
}
|
||||||
|
@ -919,10 +919,7 @@ func (s *server) initWatcher() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for v := range events {
|
||||||
// pipe all events
|
|
||||||
v := <-events
|
|
||||||
|
|
||||||
if v == nil {
|
if v == nil {
|
||||||
s.log.Error("received nil event")
|
s.log.Error("received nil event")
|
||||||
continue
|
continue
|
||||||
|
@ -120,6 +120,8 @@ func (p *pollingNotifier) poller(ctx context.Context, since groupResourceRV, str
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
case <-p.done:
|
case <-p.done:
|
||||||
return
|
return
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
|
@ -357,4 +357,41 @@ func TestPollingNotifier(t *testing.T) {
|
|||||||
t.Fatal("timeout waiting for events channel to close")
|
t.Fatal("timeout waiting for events channel to close")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("stops polling when context is cancelled", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
cfg := &pollingNotifierConfig{
|
||||||
|
dialect: sqltemplate.SQLite,
|
||||||
|
pollingInterval: 10 * time.Millisecond,
|
||||||
|
watchBufferSize: 10,
|
||||||
|
log: log.NewNopLogger(),
|
||||||
|
tracer: noop.NewTracerProvider().Tracer("test"),
|
||||||
|
batchLock: &batchLock{},
|
||||||
|
listLatestRVs: func(ctx context.Context) (groupResourceRV, error) { return nil, nil },
|
||||||
|
historyPoll: func(ctx context.Context, grp string, res string, since int64) ([]*historyPollResponse, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
notifier, err := newPollingNotifier(cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, notifier)
|
||||||
|
|
||||||
|
events, err := notifier.notify(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, events)
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case _, ok := <-events:
|
||||||
|
require.False(t, ok, "events channel should be closed")
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("timeout waiting for events channel to close")
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func RunStorageBackendTest(t *testing.T, newBackend NewBackendFunc, opts *TestOp
|
|||||||
fn func(*testing.T, resource.StorageBackend)
|
fn func(*testing.T, resource.StorageBackend)
|
||||||
}{
|
}{
|
||||||
{TestHappyPath, runTestIntegrationBackendHappyPath},
|
{TestHappyPath, runTestIntegrationBackendHappyPath},
|
||||||
{TestWatchWriteEvents, runTestIntegrationBackendWatchWriteEventsFromLastest},
|
{TestWatchWriteEvents, runTestIntegrationBackendWatchWriteEvents},
|
||||||
{TestList, runTestIntegrationBackendList},
|
{TestList, runTestIntegrationBackendList},
|
||||||
{TestBlobSupport, runTestIntegrationBlobSupport},
|
{TestBlobSupport, runTestIntegrationBlobSupport},
|
||||||
{TestGetResourceStats, runTestIntegrationBackendGetResourceStats},
|
{TestGetResourceStats, runTestIntegrationBackendGetResourceStats},
|
||||||
@ -272,7 +272,7 @@ func runTestIntegrationBackendGetResourceStats(t *testing.T, backend resource.St
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTestIntegrationBackendWatchWriteEventsFromLastest(t *testing.T, backend resource.StorageBackend) {
|
func runTestIntegrationBackendWatchWriteEvents(t *testing.T, backend resource.StorageBackend) {
|
||||||
ctx := testutil.NewTestContext(t, time.Now().Add(5*time.Second))
|
ctx := testutil.NewTestContext(t, time.Now().Add(5*time.Second))
|
||||||
|
|
||||||
// Create a few resources before initing the watch
|
// Create a few resources before initing the watch
|
||||||
@ -287,6 +287,12 @@ func runTestIntegrationBackendWatchWriteEventsFromLastest(t *testing.T, backend
|
|||||||
_, err = writeEvent(ctx, backend, "item2", resource.WatchEvent_ADDED)
|
_, err = writeEvent(ctx, backend, "item2", resource.WatchEvent_ADDED)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "item2", (<-stream).Key.Name)
|
require.Equal(t, "item2", (<-stream).Key.Name)
|
||||||
|
|
||||||
|
// Should close the stream
|
||||||
|
ctx.Cancel()
|
||||||
|
|
||||||
|
_, ok := <-stream
|
||||||
|
require.False(t, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runTestIntegrationBackendList(t *testing.T, backend resource.StorageBackend) {
|
func runTestIntegrationBackendList(t *testing.T, backend resource.StorageBackend) {
|
||||||
|
Reference in New Issue
Block a user