Skip to content

Instantly share code, notes, and snippets.

@iscander
Created July 2, 2021 13:59
Show Gist options
  • Save iscander/9a144528e563dc5969346d5e0b61cd4f to your computer and use it in GitHub Desktop.
Save iscander/9a144528e563dc5969346d5e0b61cd4f to your computer and use it in GitHub Desktop.
go-sse data-race condition example
$ go test -race
2021/07/02 16:58:04 server started.
2021/07/02 16:58:04 channel 'siner' created.
2021/07/02 16:58:04 new client connected to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
==================
WARNING: DATA RACE
Write at 0x00c00021a018 by goroutine 10:
github.com/alexandrevicenzi/go-sse.(*Channel).SendMessage()
/Users/iscander/go/pkg/mod/github.com/alexandrevicenzi/go-sse@v1.6.0/channel.go:26 +0x8c
github.com/alexandrevicenzi/go-sse.(*Server).SendMessage()
/Users/iscander/go/pkg/mod/github.com/alexandrevicenzi/go-sse@v1.6.0/sse.go:116 +0x21b
example.com/m.(*servSSE).handle.func2()
/Users/iscander/go-sse/sse_test.go:49 +0x13c
Previous write at 0x00c00021a018 by goroutine 12:
github.com/alexandrevicenzi/go-sse.(*Channel).SendMessage()
/Users/iscander/go/pkg/mod/github.com/alexandrevicenzi/go-sse@v1.6.0/channel.go:26 +0x8c
github.com/alexandrevicenzi/go-sse.(*Server).SendMessage()
/Users/iscander/go/pkg/mod/github.com/alexandrevicenzi/go-sse@v1.6.0/sse.go:116 +0x21b
example.com/m.(*servSSE).handle.func2()
/Users/iscander/go-sse/sse_test.go:49 +0x13c
Goroutine 10 (running) created at:
example.com/m.(*servSSE).handle()
/Users/iscander/go-sse/sse_test.go:30 +0x1f3
example.com/m.TestSimultaneouslyDelivery()
/Users/iscander/go-sse/sse_test.go:80 +0x54c
testing.tRunner()
/usr/local/Cellar/go/1.16.3/libexec/src/testing/testing.go:1193 +0x202
Goroutine 12 (running) created at:
example.com/m.(*servSSE).handle()
/Users/iscander/go-sse/sse_test.go:30 +0x1f3
example.com/m.TestSimultaneouslyDelivery()
/Users/iscander/go-sse/sse_test.go:80 +0x54c
testing.tRunner()
/usr/local/Cellar/go/1.16.3/libexec/src/testing/testing.go:1193 +0x202
==================
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:04 message sent to channel 'siner'.
2021/07/02 16:58:05 message sent to channel 'siner'.
2021/07/02 16:58:05 message sent to channel 'siner'.
2021/07/02 16:58:05 message sent to channel 'siner'.
2021/07/02 16:58:05 message sent to channel 'siner'.
2021/07/02 16:58:05 message sent to channel 'siner'.
2021/07/02 16:58:05 message sent to channel 'siner'.
2021/07/02 16:58:05 message sent to channel 'siner'.
2021/07/02 16:58:05 client disconnected from channel 'siner'.
2021/07/02 16:58:05 channel 'siner' has no clients.
2021/07/02 16:58:05 channel 'siner' closed.
2021/07/02 16:58:05 handler complete
--- FAIL: TestSimultaneouslyDelivery (1.01s)
testing.go:1092: race detected during execution of test
FAIL
exit status 1
FAIL example.com/m 1.040s
package test
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/alexandrevicenzi/go-sse"
"github.com/stretchr/testify/assert"
)
type servSSE struct {
sse *sse.Server
}
func (s *servSSE) handle(w http.ResponseWriter, r *http.Request) {
done := make(chan struct{})
// start an HTTP handler
go func() {
defer close(done)
s.sse.ServeHTTP(w, r)
}()
// start a number of Goroutines to send to the same channel
for _, sin := range []string{"pride", "greed", "lust", "envy", "gluttony", "wrath", "sloth"} {
go func(name string) {
event := fmt.Sprintf("commit:%s", name)
var (
sin = struct {
Count int `json:"number"`
Sin string `json:"sin"`
}{0, name}
)
for {
select {
case <-done:
log.Printf("complete %s", name)
return
default:
}
sin.Count++
js, _ := json.Marshal(sin)
data := string(js)
s.sse.SendMessage("siner", sse.NewMessage(id(), data, event))
time.Sleep(300 * time.Millisecond)
}
}(sin)
}
<-done
log.Println("handler complete")
}
func channelName(r *http.Request) string {
return "siner"
}
func id() string {
return fmt.Sprintf("%d", time.Now().UTC().Unix()*1000)
}
// Test example to rise a data race
func TestSimultaneouslyDelivery(t *testing.T) {
s := servSSE{
sse: sse.NewServer(
&sse.Options{
ChannelNameFunc: channelName,
Logger: log.Default(),
}),
}
r, _ := http.NewRequest("GET", "/", nil)
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Second))
r = r.WithContext(ctx)
w := httptest.NewRecorder()
s.handle(w, r)
<-ctx.Done()
cancel()
result := w.Result()
//body, _ := ioutil.ReadAll(result.Body)
defer result.Body.Close()
//fmt.Printf("\n==\n%s\n=\n", string(body))
assert.Equal(t, http.StatusOK, result.StatusCode)
}
$ go test
2021/07/02 16:56:17 server started.
2021/07/02 16:56:17 message not sent because channel 'siner' has no clients.
2021/07/02 16:56:17 message not sent because channel 'siner' has no clients.
2021/07/02 16:56:17 message not sent because channel 'siner' has no clients.
2021/07/02 16:56:17 channel 'siner' created.
2021/07/02 16:56:17 message not sent because channel 'siner' has no clients.
2021/07/02 16:56:17 message not sent because channel 'siner' has no clients.
2021/07/02 16:56:17 new client connected to channel 'siner'.
2021/07/02 16:56:17 message not sent because channel 'siner' has no clients.
2021/07/02 16:56:17 message not sent because channel 'siner' has no clients.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 message sent to channel 'siner'.
2021/07/02 16:56:18 client disconnected from channel 'siner'.
2021/07/02 16:56:18 channel 'siner' has no clients.
2021/07/02 16:56:18 handler complete
2021/07/02 16:56:18 channel 'siner' closed.
PASS
ok example.com/m 1.016s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment