-
Notifications
You must be signed in to change notification settings - Fork 51
/
backfill.go
117 lines (108 loc) · 5.17 KB
/
backfill.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package gomatrixserverlib
import (
"context"
"fmt"
"github.com/matrix-org/gomatrixserverlib/spec"
)
// BackfillClient contains the necessary functions from the federation client to perform a backfill request
// from another homeserver.
type BackfillClient interface {
// Backfill performs a backfill request to the given server.
// https://matrix.org/docs/spec/server_server/latest#get-matrix-federation-v1-backfill-roomid
Backfill(ctx context.Context, origin, server spec.ServerName, roomID string, limit int, fromEventIDs []string) (Transaction, error)
}
// BackfillRequester contains the necessary functions to perform backfill requests from one server to another.
//
// It requires a StateProvider in order to perform PDU checks on received events, notably the step
// "Passes authorization rules based on the state at the event, otherwise it is rejected.". The BackfillRequester
// will always call functions on the StateProvider in topological order, starting with the earliest event and
// rolling forwards. This allows implementations to make optimisations for subsequent events, rather than
// constantly deferring to federation requests.
type BackfillRequester interface {
StateProvider
BackfillClient
// ServersAtEvent is called when trying to determine which server to request from.
// It returns a list of servers which can be queried for backfill requests. These servers
// will be servers that are in the room already. The entries at the beginning are preferred servers
// and will be tried first. An empty list will fail the request.
ServersAtEvent(ctx context.Context, roomID, eventID string) []spec.ServerName
ProvideEvents(roomVer RoomVersion, eventIDs []string) ([]PDU, error)
}
// RequestBackfill implements the server logic for making backfill requests to other servers.
// This handles server selection, fetching up to the request limit and verifying the received events.
// Event validation also includes authorisation checks, which may require additional state to be fetched.
//
// The returned events are safe to be inserted into a database for later retrieval. It's possible for the
// number of returned events to be less than the limit, even if there exists more events. It's also possible
// for the number of returned events to be greater than the limit, if fromEventIDs > 1 and we need to ask
// multiple servers. We don't drop events greater than the limit because we've already done all the work to
// verify them, so it's up to the caller to decide what to do with them.
//
// TODO: We should be able to make some guarantees for the caller about the returned events position in the DAG,
// but to verify it we need to know the prev_events of fromEventIDs.
//
// TODO: When does it make sense to return errors?
func RequestBackfill(ctx context.Context, origin spec.ServerName, b BackfillRequester, keyRing JSONVerifier,
roomID string, ver RoomVersion, fromEventIDs []string, limit int, userIDForSender spec.UserIDForSender) ([]PDU, error) {
if len(fromEventIDs) == 0 {
return nil, nil
}
haveEventIDs := make(map[string]bool)
var result []PDU
loader := NewEventsLoader(ver, keyRing, b, b.ProvideEvents, false)
// pick a server to backfill from
// TODO: use other event IDs and make a set out of all the returned servers?
servers := b.ServersAtEvent(ctx, roomID, fromEventIDs[0])
// loop each server asking it for `limit` events. Worst case, we ask every server for `limit`
// events before giving up. Best case, we just ask one.
var lastErr error
for _, s := range servers {
if len(result) >= limit {
break
}
if ctx.Err() != nil {
return nil, fmt.Errorf("gomatrixserverlib: RequestBackfill context cancelled %w", ctx.Err())
}
// fetch some events, and try a different server if it fails
txn, err := b.Backfill(ctx, origin, s, roomID, limit, fromEventIDs)
if err != nil {
lastErr = err
continue // try the next server
}
// topologically sort the events so implementations of 'get state at event' can do optimisations
loadResults, err := loader.LoadAndVerify(ctx, txn.PDUs, TopologicalOrderByPrevEvents, userIDForSender)
if err != nil {
lastErr = err
continue // try the next server
}
for _, res := range loadResults {
switch res.Error.(type) {
case nil, SignatureErr:
// The signature of the event might not be valid anymore, for example if
// the key ID was reused with a different signature.
case AuthChainErr, AuthRulesErr:
continue
default:
continue
}
if haveEventIDs[res.Event.EventID()] {
continue // we got this event from a different server
}
haveEventIDs[res.Event.EventID()] = true
result = append(result, res.Event)
}
}
return result, lastErr
}
/*
// BackfillResponder contains the necessary functions to handle backfill requests.
type backfillResponder interface {
// TODO, unexported for now.
}
// ReceiveBackfill implements the server logic for processing backfill requests sent by a server.
// This handles event selection via breadth-first search, as well as history visibility rules depending
// on the state of the room at that point in time.
func receiveBackfill(b backfillResponder, roomID string, fromEventIDs []string, limit int) (*Transaction, error) {
return nil, nil // TODO, unexported for now.
}
*/