2017-04-20 22:40:52 +00:00
|
|
|
// Copyright 2017 Vector Creations Ltd
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2017-02-21 14:50:30 +00:00
|
|
|
package input
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-02-27 11:25:35 +00:00
|
|
|
"github.com/matrix-org/dendrite/roomserver/api"
|
2017-03-08 15:10:26 +00:00
|
|
|
"github.com/matrix-org/dendrite/roomserver/state"
|
2017-02-21 14:50:30 +00:00
|
|
|
"github.com/matrix-org/dendrite/roomserver/types"
|
|
|
|
"github.com/matrix-org/gomatrixserverlib"
|
2017-06-27 12:20:04 +00:00
|
|
|
"github.com/matrix-org/util"
|
2017-02-21 14:50:30 +00:00
|
|
|
)
|
|
|
|
|
2017-02-27 11:25:35 +00:00
|
|
|
// updateLatestEvents updates the list of latest events for this room in the database and writes the
|
|
|
|
// event to the output log.
|
2017-02-21 14:50:30 +00:00
|
|
|
// 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(
|
2017-02-27 11:25:35 +00:00
|
|
|
db RoomEventDatabase, ow OutputRoomEventWriter, roomNID types.RoomNID, stateAtEvent types.StateAtEvent, event gomatrixserverlib.Event,
|
2017-02-21 14:50:30 +00:00
|
|
|
) (err error) {
|
2017-03-07 10:25:01 +00:00
|
|
|
updater, err := db.GetLatestEventsForUpdate(roomNID)
|
2017-02-21 14:50:30 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2017-03-07 10:25:01 +00:00
|
|
|
err = doUpdateLatestEvents(db, updater, ow, roomNID, stateAtEvent, event)
|
2017-02-21 14:50:30 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func doUpdateLatestEvents(
|
2017-03-07 10:25:01 +00:00
|
|
|
db RoomEventDatabase, updater types.RoomRecentEventsUpdater, ow OutputRoomEventWriter, roomNID types.RoomNID, stateAtEvent types.StateAtEvent, event gomatrixserverlib.Event,
|
2017-02-21 14:50:30 +00:00
|
|
|
) error {
|
|
|
|
var err error
|
|
|
|
var prevEvents []gomatrixserverlib.EventReference
|
|
|
|
prevEvents = event.PrevEvents()
|
2017-03-07 10:25:01 +00:00
|
|
|
oldLatest := updater.LatestEvents()
|
|
|
|
lastEventIDSent := updater.LastEventIDSent()
|
|
|
|
oldStateNID := updater.CurrentStateSnapshotNID()
|
2017-02-21 14:50:30 +00:00
|
|
|
|
2017-02-27 11:25:35 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-02-21 14:50:30 +00:00
|
|
|
if err = updater.StorePreviousEvents(stateAtEvent.EventNID, prevEvents); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-27 11:25:35 +00:00
|
|
|
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,
|
|
|
|
})
|
|
|
|
|
2017-03-07 10:25:01 +00:00
|
|
|
latestStateAtEvents := make([]types.StateAtEvent, len(newLatest))
|
|
|
|
for i := range newLatest {
|
|
|
|
latestStateAtEvents[i] = newLatest[i].StateAtEvent
|
|
|
|
}
|
2017-05-26 12:51:54 +00:00
|
|
|
newStateNID, err := state.CalculateAndStoreStateAfterEvents(db, roomNID, latestStateAtEvents)
|
2017-03-07 10:25:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-03-08 15:10:26 +00:00
|
|
|
removed, added, err := state.DifferenceBetweeenStateSnapshots(db, oldStateNID, newStateNID)
|
2017-03-07 10:25:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-27 12:20:04 +00:00
|
|
|
stateBeforeEventRemoves, stateBeforeEventAdds, err := state.DifferenceBetweeenStateSnapshots(
|
|
|
|
db, newStateNID, stateAtEvent.BeforeStateSnapshotNID,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-27 11:25:35 +00:00
|
|
|
// 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.
|
2017-06-27 12:20:04 +00:00
|
|
|
if err = writeEvent(
|
|
|
|
db, ow, lastEventIDSent, event, newLatest, removed, added,
|
|
|
|
stateBeforeEventRemoves, stateBeforeEventAdds,
|
|
|
|
); err != nil {
|
2017-02-27 11:25:35 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-03-07 10:25:01 +00:00
|
|
|
if err = updater.SetLatestEvents(roomNID, newLatest, stateAtEvent.EventNID, newStateNID); err != nil {
|
2017-02-27 11:25:35 +00:00
|
|
|
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 {
|
2017-02-22 16:51:10 +00:00
|
|
|
var alreadyInLatest bool
|
2017-02-21 14:50:30 +00:00
|
|
|
var newLatest []types.StateAtEventAndReference
|
|
|
|
for _, l := range oldLatest {
|
2017-02-22 16:51:10 +00:00
|
|
|
keep := true
|
2017-02-21 14:50:30 +00:00
|
|
|
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)
|
2017-02-22 16:51:10 +00:00
|
|
|
keep = false
|
|
|
|
break
|
2017-02-21 14:50:30 +00:00
|
|
|
}
|
2017-02-22 16:51:10 +00:00
|
|
|
}
|
2017-02-27 11:25:35 +00:00
|
|
|
if l.EventNID == newEvent.EventNID {
|
2017-02-22 16:51:10 +00:00
|
|
|
alreadyInLatest = true
|
|
|
|
}
|
|
|
|
if keep {
|
2017-02-21 14:50:30 +00:00
|
|
|
// Keep the event in the latest events.
|
|
|
|
newLatest = append(newLatest, l)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-22 16:51:10 +00:00
|
|
|
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.
|
2017-02-21 14:50:30 +00:00
|
|
|
// Add it to the latest events
|
2017-02-27 11:25:35 +00:00
|
|
|
newLatest = append(newLatest, newEvent)
|
2017-02-21 14:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-02-27 11:25:35 +00:00
|
|
|
return newLatest
|
|
|
|
}
|
|
|
|
|
2017-03-07 10:25:01 +00:00
|
|
|
func writeEvent(
|
|
|
|
db RoomEventDatabase, ow OutputRoomEventWriter, lastEventIDSent string,
|
|
|
|
event gomatrixserverlib.Event, latest []types.StateAtEventAndReference,
|
|
|
|
removed, added []types.StateEntry,
|
2017-06-27 12:20:04 +00:00
|
|
|
stateBeforeEventRemoves, stateBeforeEventAdds []types.StateEntry,
|
2017-03-07 10:25:01 +00:00
|
|
|
) error {
|
2017-02-27 11:25:35 +00:00
|
|
|
|
|
|
|
latestEventIDs := make([]string, len(latest))
|
|
|
|
for i := range latest {
|
|
|
|
latestEventIDs[i] = latest[i].EventID
|
2017-02-21 14:50:30 +00:00
|
|
|
}
|
|
|
|
|
2017-03-07 10:25:01 +00:00
|
|
|
ore := api.OutputRoomEvent{
|
2017-02-27 11:25:35 +00:00
|
|
|
Event: event.JSON(),
|
|
|
|
LastSentEventID: lastEventIDSent,
|
|
|
|
LatestEventIDs: latestEventIDs,
|
2017-03-07 10:25:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var stateEventNIDs []types.EventNID
|
|
|
|
for _, entry := range added {
|
|
|
|
stateEventNIDs = append(stateEventNIDs, entry.EventNID)
|
|
|
|
}
|
|
|
|
for _, entry := range removed {
|
|
|
|
stateEventNIDs = append(stateEventNIDs, entry.EventNID)
|
|
|
|
}
|
2017-06-27 12:20:04 +00:00
|
|
|
for _, entry := range stateBeforeEventRemoves {
|
|
|
|
stateEventNIDs = append(stateEventNIDs, entry.EventNID)
|
|
|
|
}
|
|
|
|
for _, entry := range stateBeforeEventAdds {
|
|
|
|
stateEventNIDs = append(stateEventNIDs, entry.EventNID)
|
|
|
|
}
|
|
|
|
stateEventNIDs = stateEventNIDs[:util.SortAndUnique(eventNIDSorter(stateEventNIDs))]
|
2017-03-07 10:25:01 +00:00
|
|
|
eventIDMap, err := db.EventIDs(stateEventNIDs)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, entry := range added {
|
|
|
|
ore.AddsStateEventIDs = append(ore.AddsStateEventIDs, eventIDMap[entry.EventNID])
|
|
|
|
}
|
|
|
|
for _, entry := range removed {
|
|
|
|
ore.RemovesStateEventIDs = append(ore.RemovesStateEventIDs, eventIDMap[entry.EventNID])
|
|
|
|
}
|
2017-06-27 12:20:04 +00:00
|
|
|
for _, entry := range stateBeforeEventRemoves {
|
|
|
|
ore.StateBeforeRemovesEventIDs = append(ore.StateBeforeRemovesEventIDs, eventIDMap[entry.EventNID])
|
|
|
|
}
|
|
|
|
for _, entry := range stateBeforeEventAdds {
|
|
|
|
ore.StateBeforeAddsEventIDs = append(ore.StateBeforeAddsEventIDs, eventIDMap[entry.EventNID])
|
|
|
|
}
|
2017-03-07 10:25:01 +00:00
|
|
|
return ow.WriteOutputRoomEvent(ore)
|
2017-02-21 14:50:30 +00:00
|
|
|
}
|
2017-06-27 12:20:04 +00:00
|
|
|
|
|
|
|
type eventNIDSorter []types.EventNID
|
|
|
|
|
|
|
|
func (s eventNIDSorter) Len() int { return len(s) }
|
|
|
|
func (s eventNIDSorter) Less(i, j int) bool { return s[i] < s[j] }
|
|
|
|
func (s eventNIDSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|