dendrite/src/github.com/matrix-org/dendrite/roomserver/input/latest_events.go

152 lines
5.5 KiB
Go
Raw Normal View History

package input
import (
"bytes"
"github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/roomserver/types"
"github.com/matrix-org/gomatrixserverlib"
)
// updateLatestEvents updates the list of latest events for this room in the database and writes the
// event to the output log.
// The latest events are the events that aren't referenced by another event in the database:
//
// Time goes down the page. 1 is the m.room.create event (root).
//
// 1 After storing 1 the latest events are {1}
// | After storing 2 the latest events are {2}
// 2 After storing 3 the latest events are {3}
// / \ After storing 4 the latest events are {3,4}
// 3 4 After storing 5 the latest events are {5,4}
// | | After storing 6 the latest events are {5,6}
// 5 6 <--- latest After storing 7 the latest events are {6,7}
// |
// 7 <----- latest
//
func updateLatestEvents(
db RoomEventDatabase, ow OutputRoomEventWriter, roomNID types.RoomNID, stateAtEvent types.StateAtEvent, event gomatrixserverlib.Event,
) (err error) {
oldLatest, lastEventIDSent, updater, err := db.GetLatestEventsForUpdate(roomNID)
if err != nil {
return
}
defer func() {
if err == nil {
// Commit if there wasn't an error.
// Set the returned err value if we encounter an error committing.
// This only works because err is a named return.
err = updater.Commit()
} else {
// Ignore any error we get rolling back since we don't want to
// clobber the current error
// TODO: log the error here.
updater.Rollback()
}
}()
err = doUpdateLatestEvents(updater, ow, oldLatest, lastEventIDSent, roomNID, stateAtEvent, event)
return
}
func doUpdateLatestEvents(
updater types.RoomRecentEventsUpdater, ow OutputRoomEventWriter, oldLatest []types.StateAtEventAndReference, lastEventIDSent string, roomNID types.RoomNID, stateAtEvent types.StateAtEvent, event gomatrixserverlib.Event,
) error {
var err error
var prevEvents []gomatrixserverlib.EventReference
prevEvents = event.PrevEvents()
if hasBeenSent, err := updater.HasEventBeenSent(stateAtEvent.EventNID); err != nil {
return err
} else if hasBeenSent {
// Already sent this event so we can stop processing
return nil
}
if err = updater.StorePreviousEvents(stateAtEvent.EventNID, prevEvents); err != nil {
return err
}
eventReference := event.EventReference()
// Check if this event is already referenced by another event in the room.
var alreadyReferenced bool
if alreadyReferenced, err = updater.IsReferenced(eventReference); err != nil {
return err
}
newLatest := calculateLatest(oldLatest, alreadyReferenced, prevEvents, types.StateAtEventAndReference{
EventReference: eventReference,
StateAtEvent: stateAtEvent,
})
// Send the event to the output logs.
// We do this inside the database transaction to ensure that we only mark an event as sent if we sent it.
// (n.b. this means that it's possible that the same event will be sent twice if the transaction fails but
// the write to the output log succeeds)
// TODO: This assumes that writing the event to the output log is synchronous. It should be possible to
// send the event asynchronously but we would need to ensure that 1) the events are written to the log in
// the correct order, 2) that pending writes are resent across restarts. In order to avoid writing all the
// necessary bookkeeping we'll keep the event sending synchronous for now.
if err = writeEvent(ow, lastEventIDSent, event, newLatest); err != nil {
return err
}
if err = updater.SetLatestEvents(roomNID, newLatest, stateAtEvent.EventNID); err != nil {
return err
}
if err = updater.MarkEventAsSent(stateAtEvent.EventNID); err != nil {
return err
}
return nil
}
func calculateLatest(oldLatest []types.StateAtEventAndReference, alreadyReferenced bool, prevEvents []gomatrixserverlib.EventReference, newEvent types.StateAtEventAndReference) []types.StateAtEventAndReference {
var alreadyInLatest bool
var newLatest []types.StateAtEventAndReference
for _, l := range oldLatest {
keep := true
for _, prevEvent := range prevEvents {
if l.EventID == prevEvent.EventID && bytes.Compare(l.EventSHA256, prevEvent.EventSHA256) == 0 {
// This event can be removed from the latest events cause we've found an event that references it.
// (If an event is referenced by another event then it can't be one of the latest events in the room
// because we have an event that comes after it)
keep = false
break
}
}
if l.EventNID == newEvent.EventNID {
alreadyInLatest = true
}
if keep {
// Keep the event in the latest events.
newLatest = append(newLatest, l)
}
}
if !alreadyReferenced && !alreadyInLatest {
// This event is not referenced by any of the events in the room
// and the event is not already in the latest events.
// Add it to the latest events
newLatest = append(newLatest, newEvent)
}
return newLatest
}
func writeEvent(ow OutputRoomEventWriter, lastEventIDSent string, event gomatrixserverlib.Event, latest []types.StateAtEventAndReference) error {
latestEventIDs := make([]string, len(latest))
for i := range latest {
latestEventIDs[i] = latest[i].EventID
}
// TODO: Fill out AddsStateEventIDs and RemovesStateEventIDs
// TODO: Fill out VisibilityStateIDs
return ow.WriteOutputRoomEvent(api.OutputRoomEvent{
Event: event.JSON(),
LastSentEventID: lastEventIDSent,
LatestEventIDs: latestEventIDs,
})
}