Skip to content
This repository was archived by the owner on Nov 25, 2024. It is now read-only.

Commit 60e7795

Browse files
APwhitehaterikjohnston
authored andcommitted
[roomserver] Store transaction ID and prevent reprocessing of events (#446)
1 parent ed388a3 commit 60e7795

File tree

16 files changed

+206
-49
lines changed

16 files changed

+206
-49
lines changed

src/github.com/matrix-org/dendrite/clientapi/producers/roomserver.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func NewRoomserverProducer(inputAPI api.RoomserverInputAPI) *RoomserverProducer
3737
func (c *RoomserverProducer) SendEvents(
3838
ctx context.Context, events []gomatrixserverlib.Event, sendAsServer gomatrixserverlib.ServerName,
3939
txnID *api.TransactionID,
40-
) error {
40+
) (string, error) {
4141
ires := make([]api.InputRoomEvent, len(events))
4242
for i, event := range events {
4343
ires[i] = api.InputRoomEvent{
@@ -83,20 +83,27 @@ func (c *RoomserverProducer) SendEventWithState(
8383
StateEventIDs: stateEventIDs,
8484
}
8585

86-
return c.SendInputRoomEvents(ctx, ires)
86+
_, err = c.SendInputRoomEvents(ctx, ires)
87+
return err
8788
}
8889

8990
// SendInputRoomEvents writes the given input room events to the roomserver input API.
90-
func (c *RoomserverProducer) SendInputRoomEvents(ctx context.Context, ires []api.InputRoomEvent) error {
91+
func (c *RoomserverProducer) SendInputRoomEvents(
92+
ctx context.Context, ires []api.InputRoomEvent,
93+
) (eventID string, err error) {
9194
request := api.InputRoomEventsRequest{InputRoomEvents: ires}
9295
var response api.InputRoomEventsResponse
93-
return c.InputAPI.InputRoomEvents(ctx, &request, &response)
96+
err = c.InputAPI.InputRoomEvents(ctx, &request, &response)
97+
eventID = response.EventID
98+
return
9499
}
95100

96101
// SendInvite writes the invite event to the roomserver input API.
97102
// This should only be needed for invite events that occur outside of a known room.
98103
// If we are in the room then the event should be sent using the SendEvents method.
99-
func (c *RoomserverProducer) SendInvite(ctx context.Context, inviteEvent gomatrixserverlib.Event) error {
104+
func (c *RoomserverProducer) SendInvite(
105+
ctx context.Context, inviteEvent gomatrixserverlib.Event,
106+
) error {
100107
request := api.InputRoomEventsRequest{
101108
InputInviteEvents: []api.InputInviteEvent{{Event: inviteEvent}},
102109
}

src/github.com/matrix-org/dendrite/clientapi/routing/createroom.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ func createRoom(req *http.Request, device *authtypes.Device,
261261
}
262262

263263
// send events to the room server
264-
err = producer.SendEvents(req.Context(), builtEvents, cfg.Matrix.ServerName, nil)
264+
_, err = producer.SendEvents(req.Context(), builtEvents, cfg.Matrix.ServerName, nil)
265265
if err != nil {
266266
return httputil.LogThenError(req, err)
267267
}

src/github.com/matrix-org/dendrite/clientapi/routing/joinroom.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func (r joinRoomReq) joinRoomUsingServers(
217217
var queryRes api.QueryLatestEventsAndStateResponse
218218
event, err := common.BuildEvent(r.req.Context(), &eb, r.cfg, r.queryAPI, &queryRes)
219219
if err == nil {
220-
if err = r.producer.SendEvents(r.req.Context(), []gomatrixserverlib.Event{*event}, r.cfg.Matrix.ServerName, nil); err != nil {
220+
if _, err = r.producer.SendEvents(r.req.Context(), []gomatrixserverlib.Event{*event}, r.cfg.Matrix.ServerName, nil); err != nil {
221221
return httputil.LogThenError(r.req, err)
222222
}
223223
return util.JSONResponse{

src/github.com/matrix-org/dendrite/clientapi/routing/membership.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func SendMembership(
9797
return httputil.LogThenError(req, err)
9898
}
9999

100-
if err := producer.SendEvents(
100+
if _, err := producer.SendEvents(
101101
req.Context(), []gomatrixserverlib.Event{*event}, cfg.Matrix.ServerName, nil,
102102
); err != nil {
103103
return httputil.LogThenError(req, err)

src/github.com/matrix-org/dendrite/clientapi/routing/profile.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func SetAvatarURL(
138138
return httputil.LogThenError(req, err)
139139
}
140140

141-
if err := rsProducer.SendEvents(req.Context(), events, cfg.Matrix.ServerName, nil); err != nil {
141+
if _, err := rsProducer.SendEvents(req.Context(), events, cfg.Matrix.ServerName, nil); err != nil {
142142
return httputil.LogThenError(req, err)
143143
}
144144

@@ -230,7 +230,7 @@ func SetDisplayName(
230230
return httputil.LogThenError(req, err)
231231
}
232232

233-
if err := rsProducer.SendEvents(req.Context(), events, cfg.Matrix.ServerName, nil); err != nil {
233+
if _, err := rsProducer.SendEvents(req.Context(), events, cfg.Matrix.ServerName, nil); err != nil {
234234
return httputil.LogThenError(req, err)
235235
}
236236

src/github.com/matrix-org/dendrite/clientapi/routing/sendevent.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,18 @@ func SendEvent(
107107
}
108108
}
109109

110-
// pass the new event to the roomserver
111-
if err := producer.SendEvents(
110+
// pass the new event to the roomserver and receive the correct event ID
111+
// event ID in case of duplicate transaction is discarded
112+
eventID, err := producer.SendEvents(
112113
req.Context(), []gomatrixserverlib.Event{*e}, cfg.Matrix.ServerName, txnAndDeviceID,
113-
); err != nil {
114+
)
115+
if err != nil {
114116
return httputil.LogThenError(req, err)
115117
}
116118

117119
res := util.JSONResponse{
118120
Code: http.StatusOK,
119-
JSON: sendEventResponse{e.EventID()},
121+
JSON: sendEventResponse{eventID},
120122
}
121123
// Add response to transactionsCache
122124
if txnID != nil {

src/github.com/matrix-org/dendrite/clientapi/threepid/invites.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,5 +355,6 @@ func emit3PIDInviteEvent(
355355
return err
356356
}
357357

358-
return producer.SendEvents(ctx, []gomatrixserverlib.Event{*event}, cfg.Matrix.ServerName, nil)
358+
_, err = producer.SendEvents(ctx, []gomatrixserverlib.Event{*event}, cfg.Matrix.ServerName, nil)
359+
return err
359360
}

src/github.com/matrix-org/dendrite/federationapi/routing/join.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ func SendJoin(
169169
// Send the events to the room server.
170170
// We are responsible for notifying other servers that the user has joined
171171
// the room, so set SendAsServer to cfg.Matrix.ServerName
172-
err = producer.SendEvents(ctx, []gomatrixserverlib.Event{event}, cfg.Matrix.ServerName, nil)
172+
_, err = producer.SendEvents(ctx, []gomatrixserverlib.Event{event}, cfg.Matrix.ServerName, nil)
173173
if err != nil {
174174
return httputil.LogThenError(httpReq, err)
175175
}

src/github.com/matrix-org/dendrite/federationapi/routing/send.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ func (t *txnReq) processEvent(e gomatrixserverlib.Event) error {
170170
// TODO: Check that the event is allowed by its auth_events.
171171

172172
// pass the event to the roomserver
173-
return t.producer.SendEvents(t.context, []gomatrixserverlib.Event{e}, api.DoNotSendToOtherServers, nil)
173+
_, err := t.producer.SendEvents(t.context, []gomatrixserverlib.Event{e}, api.DoNotSendToOtherServers, nil)
174+
return err
174175
}
175176

176177
func checkAllowedByState(e gomatrixserverlib.Event, stateEvents []gomatrixserverlib.Event) error {

src/github.com/matrix-org/dendrite/federationapi/routing/threepid.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func CreateInvitesFrom3PIDInvites(
8181
}
8282

8383
// Send all the events
84-
if err := producer.SendEvents(req.Context(), evs, cfg.Matrix.ServerName, nil); err != nil {
84+
if _, err := producer.SendEvents(req.Context(), evs, cfg.Matrix.ServerName, nil); err != nil {
8585
return httputil.LogThenError(req, err)
8686
}
8787

@@ -153,7 +153,7 @@ func ExchangeThirdPartyInvite(
153153
}
154154

155155
// Send the event to the roomserver
156-
if err = producer.SendEvents(
156+
if _, err = producer.SendEvents(
157157
httpReq.Context(), []gomatrixserverlib.Event{signedEvent.Event}, cfg.Matrix.ServerName, nil,
158158
); err != nil {
159159
return httputil.LogThenError(httpReq, err)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ type InputRoomEventsRequest struct {
9494
}
9595

9696
// InputRoomEventsResponse is a response to InputRoomEvents
97-
type InputRoomEventsResponse struct{}
97+
type InputRoomEventsResponse struct {
98+
EventID string `json:"event_id"`
99+
}
98100

99101
// RoomserverInputAPI is used to write events to the room server.
100102
type RoomserverInputAPI interface {

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

Lines changed: 58 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type RoomEventDatabase interface {
3232
StoreEvent(
3333
ctx context.Context,
3434
event gomatrixserverlib.Event,
35+
txnAndDeviceID *api.TransactionID,
3536
authEventNIDs []types.EventNID,
3637
) (types.RoomNID, types.StateAtEvent, error)
3738
// Look up the state entries for a list of string event IDs
@@ -61,6 +62,13 @@ type RoomEventDatabase interface {
6162
MembershipUpdater(
6263
ctx context.Context, roomID, targerUserID string,
6364
) (types.MembershipUpdater, error)
65+
// Look up event ID by transaction's info.
66+
// This is used to determine if the room event is processed/processing already.
67+
// Returns an empty string if no such event exists.
68+
GetTransactionEventID(
69+
ctx context.Context, transactionID string,
70+
deviceID string, userID string,
71+
) (string, error)
6472
}
6573

6674
// OutputRoomEventWriter has the APIs needed to write an event to the output logs.
@@ -79,52 +87,46 @@ func processRoomEvent(
7987
db RoomEventDatabase,
8088
ow OutputRoomEventWriter,
8189
input api.InputRoomEvent,
82-
) error {
90+
) (eventID string, err error) {
8391
// Parse and validate the event JSON
8492
event := input.Event
8593

8694
// Check that the event passes authentication checks and work out the numeric IDs for the auth events.
8795
authEventNIDs, err := checkAuthEvents(ctx, db, event, input.AuthEventIDs)
8896
if err != nil {
89-
return err
97+
return
98+
}
99+
100+
if input.TransactionID != nil {
101+
tdID := input.TransactionID
102+
eventID, err = db.GetTransactionEventID(
103+
ctx, tdID.TransactionID, tdID.DeviceID, input.Event.Sender(),
104+
)
105+
// On error OR event with the transaction already processed/processesing
106+
if err != nil || eventID != "" {
107+
return
108+
}
90109
}
91110

92111
// Store the event
93-
roomNID, stateAtEvent, err := db.StoreEvent(ctx, event, authEventNIDs)
112+
roomNID, stateAtEvent, err := db.StoreEvent(ctx, event, input.TransactionID, authEventNIDs)
94113
if err != nil {
95-
return err
114+
return
96115
}
97116

98117
if input.Kind == api.KindOutlier {
99118
// For outliers we can stop after we've stored the event itself as it
100119
// doesn't have any associated state to store and we don't need to
101120
// notify anyone about it.
102-
return nil
121+
return event.EventID(), nil
103122
}
104123

105124
if stateAtEvent.BeforeStateSnapshotNID == 0 {
106125
// We haven't calculated a state for this event yet.
107126
// Lets calculate one.
108-
if input.HasState {
109-
// We've been told what the state at the event is so we don't need to calculate it.
110-
// Check that those state events are in the database and store the state.
111-
var entries []types.StateEntry
112-
if entries, err = db.StateEntriesForEventIDs(ctx, input.StateEventIDs); err != nil {
113-
return err
114-
}
115-
116-
if stateAtEvent.BeforeStateSnapshotNID, err = db.AddState(ctx, roomNID, nil, entries); err != nil {
117-
return err
118-
}
119-
} else {
120-
// We haven't been told what the state at the event is so we need to calculate it from the prev_events
121-
if stateAtEvent.BeforeStateSnapshotNID, err = state.CalculateAndStoreStateBeforeEvent(ctx, db, event, roomNID); err != nil {
122-
return err
123-
}
124-
}
125-
err = db.SetState(ctx, stateAtEvent.EventNID, stateAtEvent.BeforeStateSnapshotNID)
127+
err = calculateAndSetState(ctx, db, input, roomNID, &stateAtEvent, event)
126128
if err != nil {
127-
return err
129+
return
128130
}
129131
}
130132

@@ -134,7 +136,38 @@ func processRoomEvent(
134136
}
135137

136138
// Update the extremities of the event graph for the room
137-
return updateLatestEvents(ctx, db, ow, roomNID, stateAtEvent, event, input.SendAsServer, input.TransactionID)
139+
return event.EventID(), updateLatestEvents(
140+
ctx, db, ow, roomNID, stateAtEvent, event, input.SendAsServer, input.TransactionID,
141+
)
142+
}
143+
144+
func calculateAndSetState(
145+
ctx context.Context,
146+
db RoomEventDatabase,
147+
input api.InputRoomEvent,
148+
roomNID types.RoomNID,
149+
stateAtEvent *types.StateAtEvent,
150+
event gomatrixserverlib.Event,
151+
) error {
152+
var err error
153+
if input.HasState {
154+
// We've been told what the state at the event is so we don't need to calculate it.
155+
// Check that those state events are in the database and store the state.
156+
var entries []types.StateEntry
157+
if entries, err = db.StateEntriesForEventIDs(ctx, input.StateEventIDs); err != nil {
158+
return err
159+
}
160+
161+
if stateAtEvent.BeforeStateSnapshotNID, err = db.AddState(ctx, roomNID, nil, entries); err != nil {
162+
return err
163+
}
164+
} else {
165+
// We haven't been told what the state at the event is so we need to calculate it from the prev_events
166+
if stateAtEvent.BeforeStateSnapshotNID, err = state.CalculateAndStoreStateBeforeEvent(ctx, db, event, roomNID); err != nil {
167+
return err
168+
}
169+
}
170+
return db.SetState(ctx, stateAtEvent.EventNID, stateAtEvent.BeforeStateSnapshotNID)
138171
}
139172

140173
func processInviteEvent(

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ func (r *RoomserverInputAPI) InputRoomEvents(
6060
ctx context.Context,
6161
request *api.InputRoomEventsRequest,
6262
response *api.InputRoomEventsResponse,
63-
) error {
63+
) (err error) {
6464
// We lock as processRoomEvent can only be called once at a time
6565
r.mutex.Lock()
6666
defer r.mutex.Unlock()
6767
for i := range request.InputRoomEvents {
68-
if err := processRoomEvent(ctx, r.DB, r, request.InputRoomEvents[i]); err != nil {
68+
if response.EventID, err = processRoomEvent(ctx, r.DB, r, request.InputRoomEvents[i]); err != nil {
6969
return err
7070
}
7171
}
7272
for i := range request.InputInviteEvents {
73-
if err := processInviteEvent(ctx, r.DB, r, request.InputInviteEvents[i]); err != nil {
73+
if err = processInviteEvent(ctx, r.DB, r, request.InputInviteEvents[i]); err != nil {
7474
return err
7575
}
7676
}

src/github.com/matrix-org/dendrite/roomserver/storage/sql.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type statements struct {
3030
roomAliasesStatements
3131
inviteStatements
3232
membershipStatements
33+
transactionStatements
3334
}
3435

3536
func (s *statements) prepare(db *sql.DB) error {
@@ -47,6 +48,7 @@ func (s *statements) prepare(db *sql.DB) error {
4748
s.roomAliasesStatements.prepare,
4849
s.inviteStatements.prepare,
4950
s.membershipStatements.prepare,
51+
s.transactionStatements.prepare,
5052
} {
5153
if err = prepare(db); err != nil {
5254
return err

src/github.com/matrix-org/dendrite/roomserver/storage/storage.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
// Import the postgres database driver.
2222
_ "github.com/lib/pq"
23+
"github.com/matrix-org/dendrite/roomserver/api"
2324
"github.com/matrix-org/dendrite/roomserver/types"
2425
"github.com/matrix-org/gomatrixserverlib"
2526
)
@@ -45,7 +46,8 @@ func Open(dataSourceName string) (*Database, error) {
4546

4647
// StoreEvent implements input.EventDatabase
4748
func (d *Database) StoreEvent(
48-
ctx context.Context, event gomatrixserverlib.Event, authEventNIDs []types.EventNID,
49+
ctx context.Context, event gomatrixserverlib.Event,
50+
txnAndDeviceID *api.TransactionID, authEventNIDs []types.EventNID,
4951
) (types.RoomNID, types.StateAtEvent, error) {
5052
var (
5153
roomNID types.RoomNID
@@ -56,6 +58,15 @@ func (d *Database) StoreEvent(
5658
err error
5759
)
5860

61+
if txnAndDeviceID != nil {
62+
if err = d.statements.insertTransaction(
63+
ctx, txnAndDeviceID.TransactionID,
64+
txnAndDeviceID.DeviceID, event.Sender(), event.EventID(),
65+
); err != nil {
66+
return 0, types.StateAtEvent{}, err
67+
}
68+
}
69+
5970
if roomNID, err = d.assignRoomNID(ctx, nil, event.RoomID()); err != nil {
6071
return 0, types.StateAtEvent{}, err
6172
}
@@ -308,6 +319,18 @@ func (d *Database) GetLatestEventsForUpdate(
308319
}, nil
309320
}
310321

322+
// GetTransactionEventID implements input.EventDatabase
323+
func (d *Database) GetTransactionEventID(
324+
ctx context.Context, transactionID string,
325+
deviceID string, userID string,
326+
) (string, error) {
327+
eventID, err := d.statements.selectTransactionEventID(ctx, transactionID, deviceID, userID)
328+
if err == sql.ErrNoRows {
329+
return "", nil
330+
}
331+
return eventID, err
332+
}
333+
311334
type roomRecentEventsUpdater struct {
312335
transaction
313336
d *Database

0 commit comments

Comments
 (0)