diff options
Diffstat (limited to 'model')
-rw-r--r-- | model/websocket_message.go | 25 | ||||
-rw-r--r-- | model/websocket_message_test.go | 48 |
2 files changed, 73 insertions, 0 deletions
diff --git a/model/websocket_message.go b/model/websocket_message.go index 0256e400f..8d1abecfa 100644 --- a/model/websocket_message.go +++ b/model/websocket_message.go @@ -5,6 +5,7 @@ package model import ( "encoding/json" + "fmt" "io" ) @@ -58,11 +59,32 @@ type WebsocketBroadcast struct { TeamId string `json:"team_id"` // broadcast only occurs for users in this team } +type precomputedWebSocketEventJSON struct { + Event json.RawMessage + Data json.RawMessage + Broadcast json.RawMessage +} + type WebSocketEvent struct { Event string `json:"event"` Data map[string]interface{} `json:"data"` Broadcast *WebsocketBroadcast `json:"broadcast"` Sequence int64 `json:"seq"` + + precomputedJSON *precomputedWebSocketEventJSON +} + +// PrecomputeJSON precomputes and stores the serialized JSON for all fields other than Sequence. +// This makes ToJson much more efficient when sending the same event to multiple connections. +func (m *WebSocketEvent) PrecomputeJSON() { + event, _ := json.Marshal(m.Event) + data, _ := json.Marshal(m.Data) + broadcast, _ := json.Marshal(m.Broadcast) + m.precomputedJSON = &precomputedWebSocketEventJSON{ + Event: json.RawMessage(event), + Data: json.RawMessage(data), + Broadcast: json.RawMessage(broadcast), + } } func (m *WebSocketEvent) Add(key string, value interface{}) { @@ -83,6 +105,9 @@ func (o *WebSocketEvent) EventType() string { } func (o *WebSocketEvent) ToJson() string { + if o.precomputedJSON != nil { + return fmt.Sprintf(`{"event": %s, "data": %s, "broadcast": %s, "seq": %d}`, o.precomputedJSON.Event, o.precomputedJSON.Data, o.precomputedJSON.Broadcast, o.Sequence) + } b, _ := json.Marshal(o) return string(b) } diff --git a/model/websocket_message_test.go b/model/websocket_message_test.go index 1b75d0f6e..10404c299 100644 --- a/model/websocket_message_test.go +++ b/model/websocket_message_test.go @@ -6,6 +6,8 @@ package model import ( "strings" "testing" + + "github.com/stretchr/testify/assert" ) func TestWebSocketEvent(t *testing.T) { @@ -54,3 +56,49 @@ func TestWebSocketResponse(t *testing.T) { t.Fatal("Ids do not match") } } + +func TestWebSocketEvent_PrecomputeJSON(t *testing.T) { + event := NewWebSocketEvent(WEBSOCKET_EVENT_POSTED, "foo", "bar", "baz", nil) + event.Sequence = 7 + + before := event.ToJson() + event.PrecomputeJSON() + after := event.ToJson() + + assert.JSONEq(t, before, after) +} + +var stringSink string + +func BenchmarkWebSocketEvent_ToJson(b *testing.B) { + event := NewWebSocketEvent(WEBSOCKET_EVENT_POSTED, "foo", "bar", "baz", nil) + for i := 0; i < 100; i++ { + event.Data[NewId()] = NewId() + } + + b.Run("SerializedNTimes", func(b *testing.B) { + for i := 0; i < b.N; i++ { + stringSink = event.ToJson() + } + }) + + b.Run("PrecomputedNTimes", func(b *testing.B) { + for i := 0; i < b.N; i++ { + event.PrecomputeJSON() + } + }) + + b.Run("PrecomputedAndSerializedNTimes", func(b *testing.B) { + for i := 0; i < b.N; i++ { + event.PrecomputeJSON() + stringSink = event.ToJson() + } + }) + + event.PrecomputeJSON() + b.Run("PrecomputedOnceAndSerializedNTimes", func(b *testing.B) { + for i := 0; i < b.N; i++ { + stringSink = event.ToJson() + } + }) +} |