Don't retrieve same state events over and over again (#1737)

This commit is contained in:
Neil Alexander 2021-01-26 09:12:17 +00:00 committed by GitHub
parent ef9d5ad4fe
commit 64fb6de6d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -33,19 +33,21 @@ import (
type StateResolution struct { type StateResolution struct {
db storage.Database db storage.Database
roomInfo types.RoomInfo roomInfo types.RoomInfo
events map[types.EventNID]*gomatrixserverlib.Event
} }
func NewStateResolution(db storage.Database, roomInfo types.RoomInfo) StateResolution { func NewStateResolution(db storage.Database, roomInfo types.RoomInfo) StateResolution {
return StateResolution{ return StateResolution{
db: db, db: db,
roomInfo: roomInfo, roomInfo: roomInfo,
events: make(map[types.EventNID]*gomatrixserverlib.Event),
} }
} }
// LoadStateAtSnapshot loads the full state of a room at a particular snapshot. // LoadStateAtSnapshot loads the full state of a room at a particular snapshot.
// This is typically the state before an event or the current state of a room. // This is typically the state before an event or the current state of a room.
// Returns a sorted list of state entries or an error if there was a problem talking to the database. // Returns a sorted list of state entries or an error if there was a problem talking to the database.
func (v StateResolution) LoadStateAtSnapshot( func (v *StateResolution) LoadStateAtSnapshot(
ctx context.Context, stateNID types.StateSnapshotNID, ctx context.Context, stateNID types.StateSnapshotNID,
) ([]types.StateEntry, error) { ) ([]types.StateEntry, error) {
stateBlockNIDLists, err := v.db.StateBlockNIDs(ctx, []types.StateSnapshotNID{stateNID}) stateBlockNIDLists, err := v.db.StateBlockNIDs(ctx, []types.StateSnapshotNID{stateNID})
@ -83,7 +85,7 @@ func (v StateResolution) LoadStateAtSnapshot(
} }
// LoadStateAtEvent loads the full state of a room before a particular event. // LoadStateAtEvent loads the full state of a room before a particular event.
func (v StateResolution) LoadStateAtEvent( func (v *StateResolution) LoadStateAtEvent(
ctx context.Context, eventID string, ctx context.Context, eventID string,
) ([]types.StateEntry, error) { ) ([]types.StateEntry, error) {
snapshotNID, err := v.db.SnapshotNIDFromEventID(ctx, eventID) snapshotNID, err := v.db.SnapshotNIDFromEventID(ctx, eventID)
@ -105,7 +107,7 @@ func (v StateResolution) LoadStateAtEvent(
// LoadCombinedStateAfterEvents loads a snapshot of the state after each of the events // LoadCombinedStateAfterEvents loads a snapshot of the state after each of the events
// and combines those snapshots together into a single list. At this point it is // and combines those snapshots together into a single list. At this point it is
// possible to run into duplicate (type, state key) tuples. // possible to run into duplicate (type, state key) tuples.
func (v StateResolution) LoadCombinedStateAfterEvents( func (v *StateResolution) LoadCombinedStateAfterEvents(
ctx context.Context, prevStates []types.StateAtEvent, ctx context.Context, prevStates []types.StateAtEvent,
) ([]types.StateEntry, error) { ) ([]types.StateEntry, error) {
stateNIDs := make([]types.StateSnapshotNID, len(prevStates)) stateNIDs := make([]types.StateSnapshotNID, len(prevStates))
@ -177,7 +179,7 @@ func (v StateResolution) LoadCombinedStateAfterEvents(
} }
// DifferenceBetweeenStateSnapshots works out which state entries have been added and removed between two snapshots. // DifferenceBetweeenStateSnapshots works out which state entries have been added and removed between two snapshots.
func (v StateResolution) DifferenceBetweeenStateSnapshots( func (v *StateResolution) DifferenceBetweeenStateSnapshots(
ctx context.Context, oldStateNID, newStateNID types.StateSnapshotNID, ctx context.Context, oldStateNID, newStateNID types.StateSnapshotNID,
) (removed, added []types.StateEntry, err error) { ) (removed, added []types.StateEntry, err error) {
if oldStateNID == newStateNID { if oldStateNID == newStateNID {
@ -236,7 +238,7 @@ func (v StateResolution) DifferenceBetweeenStateSnapshots(
// If there is no entry for a given event type and state key pair then it will be discarded. // If there is no entry for a given event type and state key pair then it will be discarded.
// This is typically the state before an event or the current state of a room. // This is typically the state before an event or the current state of a room.
// Returns a sorted list of state entries or an error if there was a problem talking to the database. // Returns a sorted list of state entries or an error if there was a problem talking to the database.
func (v StateResolution) LoadStateAtSnapshotForStringTuples( func (v *StateResolution) LoadStateAtSnapshotForStringTuples(
ctx context.Context, ctx context.Context,
stateNID types.StateSnapshotNID, stateNID types.StateSnapshotNID,
stateKeyTuples []gomatrixserverlib.StateKeyTuple, stateKeyTuples []gomatrixserverlib.StateKeyTuple,
@ -251,7 +253,7 @@ func (v StateResolution) LoadStateAtSnapshotForStringTuples(
// stringTuplesToNumericTuples converts the string state key tuples into numeric IDs // stringTuplesToNumericTuples converts the string state key tuples into numeric IDs
// If there isn't a numeric ID for either the event type or the event state key then the tuple is discarded. // If there isn't a numeric ID for either the event type or the event state key then the tuple is discarded.
// Returns an error if there was a problem talking to the database. // Returns an error if there was a problem talking to the database.
func (v StateResolution) stringTuplesToNumericTuples( func (v *StateResolution) stringTuplesToNumericTuples(
ctx context.Context, ctx context.Context,
stringTuples []gomatrixserverlib.StateKeyTuple, stringTuples []gomatrixserverlib.StateKeyTuple,
) ([]types.StateKeyTuple, error) { ) ([]types.StateKeyTuple, error) {
@ -292,7 +294,7 @@ func (v StateResolution) stringTuplesToNumericTuples(
// If there is no entry for a given event type and state key pair then it will be discarded. // If there is no entry for a given event type and state key pair then it will be discarded.
// This is typically the state before an event or the current state of a room. // This is typically the state before an event or the current state of a room.
// Returns a sorted list of state entries or an error if there was a problem talking to the database. // Returns a sorted list of state entries or an error if there was a problem talking to the database.
func (v StateResolution) loadStateAtSnapshotForNumericTuples( func (v *StateResolution) loadStateAtSnapshotForNumericTuples(
ctx context.Context, ctx context.Context,
stateNID types.StateSnapshotNID, stateNID types.StateSnapshotNID,
stateKeyTuples []types.StateKeyTuple, stateKeyTuples []types.StateKeyTuple,
@ -340,7 +342,7 @@ func (v StateResolution) loadStateAtSnapshotForNumericTuples(
// If there is no entry for a given event type and state key pair then it will be discarded. // If there is no entry for a given event type and state key pair then it will be discarded.
// This is typically the state before an event. // This is typically the state before an event.
// Returns a sorted list of state entries or an error if there was a problem talking to the database. // Returns a sorted list of state entries or an error if there was a problem talking to the database.
func (v StateResolution) LoadStateAfterEventsForStringTuples( func (v *StateResolution) LoadStateAfterEventsForStringTuples(
ctx context.Context, ctx context.Context,
prevStates []types.StateAtEvent, prevStates []types.StateAtEvent,
stateKeyTuples []gomatrixserverlib.StateKeyTuple, stateKeyTuples []gomatrixserverlib.StateKeyTuple,
@ -352,7 +354,7 @@ func (v StateResolution) LoadStateAfterEventsForStringTuples(
return v.loadStateAfterEventsForNumericTuples(ctx, prevStates, numericTuples) return v.loadStateAfterEventsForNumericTuples(ctx, prevStates, numericTuples)
} }
func (v StateResolution) loadStateAfterEventsForNumericTuples( func (v *StateResolution) loadStateAfterEventsForNumericTuples(
ctx context.Context, ctx context.Context,
prevStates []types.StateAtEvent, prevStates []types.StateAtEvent,
stateKeyTuples []types.StateKeyTuple, stateKeyTuples []types.StateKeyTuple,
@ -520,7 +522,7 @@ func init() {
// CalculateAndStoreStateBeforeEvent calculates a snapshot of the state of a room before an event. // CalculateAndStoreStateBeforeEvent calculates a snapshot of the state of a room before an event.
// Stores the snapshot of the state in the database. // Stores the snapshot of the state in the database.
// Returns a numeric ID for the snapshot of the state before the event. // Returns a numeric ID for the snapshot of the state before the event.
func (v StateResolution) CalculateAndStoreStateBeforeEvent( func (v *StateResolution) CalculateAndStoreStateBeforeEvent(
ctx context.Context, ctx context.Context,
event *gomatrixserverlib.Event, event *gomatrixserverlib.Event,
isRejected bool, isRejected bool,
@ -537,7 +539,7 @@ func (v StateResolution) CalculateAndStoreStateBeforeEvent(
// CalculateAndStoreStateAfterEvents finds the room state after the given events. // CalculateAndStoreStateAfterEvents finds the room state after the given events.
// Stores the resulting state in the database and returns a numeric ID for that snapshot. // Stores the resulting state in the database and returns a numeric ID for that snapshot.
func (v StateResolution) CalculateAndStoreStateAfterEvents( func (v *StateResolution) CalculateAndStoreStateAfterEvents(
ctx context.Context, ctx context.Context,
prevStates []types.StateAtEvent, prevStates []types.StateAtEvent,
) (types.StateSnapshotNID, error) { ) (types.StateSnapshotNID, error) {
@ -607,7 +609,7 @@ const maxStateBlockNIDs = 64
// calculateAndStoreStateAfterManyEvents finds the room state after the given events. // calculateAndStoreStateAfterManyEvents finds the room state after the given events.
// This handles the slow path of calculateAndStoreStateAfterEvents for when there is more than one event. // This handles the slow path of calculateAndStoreStateAfterEvents for when there is more than one event.
// Stores the resulting state and returns a numeric ID for the snapshot. // Stores the resulting state and returns a numeric ID for the snapshot.
func (v StateResolution) calculateAndStoreStateAfterManyEvents( func (v *StateResolution) calculateAndStoreStateAfterManyEvents(
ctx context.Context, ctx context.Context,
roomNID types.RoomNID, roomNID types.RoomNID,
prevStates []types.StateAtEvent, prevStates []types.StateAtEvent,
@ -627,7 +629,7 @@ func (v StateResolution) calculateAndStoreStateAfterManyEvents(
return metrics.stop(v.db.AddState(ctx, roomNID, nil, state)) return metrics.stop(v.db.AddState(ctx, roomNID, nil, state))
} }
func (v StateResolution) calculateStateAfterManyEvents( func (v *StateResolution) calculateStateAfterManyEvents(
ctx context.Context, roomVersion gomatrixserverlib.RoomVersion, ctx context.Context, roomVersion gomatrixserverlib.RoomVersion,
prevStates []types.StateAtEvent, prevStates []types.StateAtEvent,
) (state []types.StateEntry, algorithm string, conflictLength int, err error) { ) (state []types.StateEntry, algorithm string, conflictLength int, err error) {
@ -754,7 +756,7 @@ func ResolveConflictsAdhoc(
return resolved, nil return resolved, nil
} }
func (v StateResolution) resolveConflicts( func (v *StateResolution) resolveConflicts(
ctx context.Context, version gomatrixserverlib.RoomVersion, ctx context.Context, version gomatrixserverlib.RoomVersion,
notConflicted, conflicted []types.StateEntry, notConflicted, conflicted []types.StateEntry,
) ([]types.StateEntry, error) { ) ([]types.StateEntry, error) {
@ -778,7 +780,7 @@ func (v StateResolution) resolveConflicts(
// Returns a list that combines the entries without conflicts with the result of state resolution for the entries with conflicts. // Returns a list that combines the entries without conflicts with the result of state resolution for the entries with conflicts.
// The returned list is sorted by state key tuple. // The returned list is sorted by state key tuple.
// Returns an error if there was a problem talking to the database. // Returns an error if there was a problem talking to the database.
func (v StateResolution) resolveConflictsV1( func (v *StateResolution) resolveConflictsV1(
ctx context.Context, ctx context.Context,
notConflicted, conflicted []types.StateEntry, notConflicted, conflicted []types.StateEntry,
) ([]types.StateEntry, error) { ) ([]types.StateEntry, error) {
@ -842,7 +844,7 @@ func (v StateResolution) resolveConflictsV1(
// The returned list is sorted by state key tuple. // The returned list is sorted by state key tuple.
// Returns an error if there was a problem talking to the database. // Returns an error if there was a problem talking to the database.
// nolint:gocyclo // nolint:gocyclo
func (v StateResolution) resolveConflictsV2( func (v *StateResolution) resolveConflictsV2(
ctx context.Context, ctx context.Context,
notConflicted, conflicted []types.StateEntry, notConflicted, conflicted []types.StateEntry,
) ([]types.StateEntry, error) { ) ([]types.StateEntry, error) {
@ -959,7 +961,7 @@ func (v StateResolution) resolveConflictsV2(
} }
// stateKeyTuplesNeeded works out which numeric state key tuples we need to authenticate some events. // stateKeyTuplesNeeded works out which numeric state key tuples we need to authenticate some events.
func (v StateResolution) stateKeyTuplesNeeded(stateKeyNIDMap map[string]types.EventStateKeyNID, stateNeeded gomatrixserverlib.StateNeeded) []types.StateKeyTuple { func (v *StateResolution) stateKeyTuplesNeeded(stateKeyNIDMap map[string]types.EventStateKeyNID, stateNeeded gomatrixserverlib.StateNeeded) []types.StateKeyTuple {
var keyTuples []types.StateKeyTuple var keyTuples []types.StateKeyTuple
if stateNeeded.Create { if stateNeeded.Create {
keyTuples = append(keyTuples, types.StateKeyTuple{ keyTuples = append(keyTuples, types.StateKeyTuple{
@ -1004,26 +1006,33 @@ func (v StateResolution) stateKeyTuplesNeeded(stateKeyNIDMap map[string]types.Ev
// Returns a list of state events in no particular order and a map from string event ID back to state entry. // Returns a list of state events in no particular order and a map from string event ID back to state entry.
// The map can be used to recover which numeric state entry a given event is for. // The map can be used to recover which numeric state entry a given event is for.
// Returns an error if there was a problem talking to the database. // Returns an error if there was a problem talking to the database.
func (v StateResolution) loadStateEvents( func (v *StateResolution) loadStateEvents(
ctx context.Context, entries []types.StateEntry, ctx context.Context, entries []types.StateEntry,
) ([]*gomatrixserverlib.Event, map[string]types.StateEntry, error) { ) ([]*gomatrixserverlib.Event, map[string]types.StateEntry, error) {
eventNIDs := make([]types.EventNID, len(entries)) result := make([]*gomatrixserverlib.Event, 0, len(entries))
for i := range entries { eventEntries := make([]types.StateEntry, 0, len(entries))
eventNIDs[i] = entries[i].EventNID eventNIDs := make([]types.EventNID, 0, len(entries))
for _, entry := range entries {
if e, ok := v.events[entry.EventNID]; ok {
result = append(result, e)
} else {
eventEntries = append(eventEntries, entry)
eventNIDs = append(eventNIDs, entry.EventNID)
}
} }
events, err := v.db.Events(ctx, eventNIDs) events, err := v.db.Events(ctx, eventNIDs)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
eventIDMap := map[string]types.StateEntry{} eventIDMap := map[string]types.StateEntry{}
result := make([]*gomatrixserverlib.Event, len(entries)) for _, entry := range eventEntries {
for i := range entries { event, ok := eventMap(events).lookup(entry.EventNID)
event, ok := eventMap(events).lookup(entries[i].EventNID)
if !ok { if !ok {
panic(fmt.Errorf("Corrupt DB: Missing event numeric ID %d", entries[i].EventNID)) panic(fmt.Errorf("Corrupt DB: Missing event numeric ID %d", entry.EventNID))
} }
result[i] = event.Event result = append(result, event.Event)
eventIDMap[event.Event.EventID()] = entries[i] eventIDMap[event.Event.EventID()] = entry
v.events[entry.EventNID] = event.Event
} }
return result, eventIDMap, nil return result, eventIDMap, nil
} }