openreplay/backend/internal/builder/builder.go

80 lines
1.9 KiB
Go

package builder
import (
"openreplay/backend/pkg/intervals"
. "openreplay/backend/pkg/messages"
)
type messageProcessor interface {
Handle(message Message, messageID uint64, timestamp uint64) Message
Build() Message
}
type builder struct {
readyMsgs []Message
timestamp uint64
processors []messageProcessor
}
func NewBuilder() *builder {
return &builder{
processors: []messageProcessor{
&performanceTrackAggrBuilder{},
&cpuIssueFinder{},
&memoryIssueFinder{},
// &domDropDetector{},
&clickRageDetector{},
&deadClickDetector{},
},
}
}
func (b *builder) iterateReadyMessage(iter func(msg Message)) {
for _, readyMsg := range b.readyMsgs {
iter(readyMsg)
}
b.readyMsgs = nil
}
func (b *builder) handleMessage(message Message, messageID uint64) {
timestamp := GetTimestamp(message)
if b.timestamp < timestamp {
b.timestamp = timestamp
}
if b.timestamp == 0 {
// in case of SessionStart. TODO: make timestamp system transparent
return
}
for _, p := range b.processors {
/* If nil is not returned explicitely by Handle, but as the typed nil
("var i *IssueEvent; return i;")
The `rm != nil` will be true.
TODO: enforce nil to be nil(?) or add `isNil() bool` to the Message types
because this part is expected to be etendable by user with custom messageProcessor's.
Use of reflrction will be probably bad on millions of messages?
*/
if rm := p.Handle(message, messageID, b.timestamp); rm != nil {
b.readyMsgs = append(b.readyMsgs, rm)
}
}
}
func (b *builder) checkTimeouts(ts int64) bool {
if b.timestamp == 0 {
return false // SessionStart happened only
}
lastTsGap := ts - int64(b.timestamp)
// Maybe listen for `trigger` and react on SessionEnd instead (less reliable)
if lastTsGap > intervals.EVENTS_SESSION_END_TIMEOUT {
for _, p := range b.processors {
// TODO: same as above
if rm := p.Build(); rm != nil {
b.readyMsgs = append(b.readyMsgs, rm)
}
}
return true
}
return false
}