openreplay/backend/pkg/messages/session-iterator.go
Alexander f7744a0c2c
Mob split (#2015)
* feat(backend): try to split mob files by ts

* feat(backend): removed debug code and used worker pool
2024-04-03 17:10:44 +02:00

142 lines
3.4 KiB
Go

package messages
import (
"bytes"
"fmt"
"io"
"sort"
)
type msgInfo struct {
index uint64
start int64
end int64
body Message
msgType uint64
timestamp uint64
}
func (m *msgInfo) Print() string {
return fmt.Sprintf("index: %d, start: %d, end: %d, type: %d, body: %s", m.index, m.start, m.end, m.msgType, m.body)
}
func SplitMessages(data []byte) ([]*msgInfo, error) {
messages := make([]*msgInfo, 0)
indexes := make(map[uint64]bool)
hadDuplicates := false
var lastTimestamp uint64
reader := NewBytesReader(data)
var err error = nil
for {
// Get message start
msgStart := reader.Pointer()
if int(msgStart) >= len(data) {
return messages, err
}
// Read message index
msgIndex, err := reader.ReadIndex()
if err != nil {
if err != io.EOF {
return messages, fmt.Errorf("can't read message's index, msgStart: %d, pointer: %d, err: %s",
msgStart, reader.Pointer(), err)
}
return messages, err
}
// Read message type
msgType, err := reader.ReadUint()
if err != nil {
return messages, fmt.Errorf("read message type err: %s", err)
}
// Read message body
body, err := ReadMessage(msgType, reader)
if err != nil {
return messages, fmt.Errorf("read message body err: %s", err)
}
if _, ok := indexes[msgIndex]; ok && !hadDuplicates {
hadDuplicates = true
continue
}
indexes[msgIndex] = true
// Update current timestamp
if msgType == MsgTimestamp {
msgBody := body.(*Timestamp)
lastTimestamp = msgBody.Timestamp
err = fmt.Errorf("session has duplicate messages")
}
// Add new message info to messages slice
messages = append(messages, &msgInfo{
index: msgIndex,
start: msgStart + 8, // start pointer without index (that's why we use +8)
end: reader.Pointer(),
body: body,
msgType: msgType,
timestamp: lastTimestamp,
})
}
}
func SortMessages(messages []*msgInfo) []*msgInfo {
// Sort messages by index
sort.SliceStable(messages, func(i, j int) bool {
if messages[i].timestamp < messages[j].timestamp {
return true
}
if messages[i].timestamp == messages[j].timestamp {
return messages[i].index < messages[j].index
}
return false
})
return messages
}
func MergeMessages(data []byte, messages []*msgInfo, doSplit bool, splitDuration uint64) ([]byte, int) {
sortedSession := bytes.NewBuffer(make([]byte, 0, len(data)))
// Add maximum possible index value to the start of the session to inform player about new version of mob file
sortedSession.Write([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})
var (
firstTimestamp uint64 = 0
lastTsIndex = -1
splitIndex = -1
)
if splitDuration == 0 {
doSplit = false
}
for i, info := range messages {
if info.msgType == MsgTimestamp {
if firstTimestamp == 0 {
firstTimestamp = info.timestamp
}
// Save index of last timestamp message and continue to read next message
lastTsIndex = i
continue
}
if lastTsIndex != -1 {
// Try to split mob file just before timestamp message
if splitIndex < 0 && info.timestamp-firstTimestamp > splitDuration {
splitIndex = sortedSession.Len()
}
// Write last timestamp message to mob file
tsInfo := messages[lastTsIndex]
sortedSession.Write(data[tsInfo.start:tsInfo.end])
lastTsIndex = -1
}
// Write current message
sortedSession.Write(data[info.start:info.end])
}
if !doSplit {
splitIndex = -1
}
return sortedSession.Bytes(), splitIndex
}