Don't backoff on non-200s, tight send loop, 1 event query

This commit is contained in:
Andrew Morgan 2018-06-12 14:57:16 +01:00
parent d5865fa67d
commit 3e93ac86fe
2 changed files with 36 additions and 50 deletions

View file

@ -50,13 +50,9 @@ CREATE TABLE IF NOT EXISTS appservice_events (
CREATE INDEX IF NOT EXISTS appservice_events_as_id ON appservice_events(as_id); CREATE INDEX IF NOT EXISTS appservice_events_as_id ON appservice_events(as_id);
` `
const selectPastEventsByApplicationServiceIDSQL = "" + const selectEventsByApplicationServiceIDSQL = "" +
"SELECT id, event_id, origin_server_ts, room_id, type, sender, event_content, txn_id " + "SELECT id, event_id, origin_server_ts, room_id, type, sender, event_content, txn_id " +
"FROM appservice_events WHERE as_id = $1 AND txn_id > -1 LIMIT $2" "FROM appservice_events WHERE as_id = $1 ORDER BY txn_id DESC LIMIT $2"
const selectCurrEventsByApplicationServiceIDSQL = "" +
"SELECT id, event_id, origin_server_ts, room_id, type, sender, event_content, txn_id " +
"FROM appservice_events WHERE as_id = $1 AND txn_id = -1 LIMIT $2"
const countEventsByApplicationServiceIDSQL = "" + const countEventsByApplicationServiceIDSQL = "" +
"SELECT COUNT(event_id) FROM appservice_events WHERE as_id = $1" "SELECT COUNT(event_id) FROM appservice_events WHERE as_id = $1"
@ -72,12 +68,11 @@ const deleteEventsBeforeAndIncludingIDSQL = "" +
"DELETE FROM appservice_events WHERE as_id = $1 AND id <= $2" "DELETE FROM appservice_events WHERE as_id = $1 AND id <= $2"
type eventsStatements struct { type eventsStatements struct {
selectPastEventsByApplicationServiceIDStmt *sql.Stmt selectEventsByApplicationServiceIDStmt *sql.Stmt
selectCurrEventsByApplicationServiceIDStmt *sql.Stmt countEventsByApplicationServiceIDStmt *sql.Stmt
countEventsByApplicationServiceIDStmt *sql.Stmt insertEventStmt *sql.Stmt
insertEventStmt *sql.Stmt updateTxnIDForEventsStmt *sql.Stmt
updateTxnIDForEventsStmt *sql.Stmt deleteEventsBeforeAndIncludingIDStmt *sql.Stmt
deleteEventsBeforeAndIncludingIDStmt *sql.Stmt
} }
func (s *eventsStatements) prepare(db *sql.DB) (err error) { func (s *eventsStatements) prepare(db *sql.DB) (err error) {
@ -86,10 +81,7 @@ func (s *eventsStatements) prepare(db *sql.DB) (err error) {
return return
} }
if s.selectPastEventsByApplicationServiceIDStmt, err = db.Prepare(selectPastEventsByApplicationServiceIDSQL); err != nil { if s.selectEventsByApplicationServiceIDStmt, err = db.Prepare(selectEventsByApplicationServiceIDSQL); err != nil {
return
}
if s.selectCurrEventsByApplicationServiceIDStmt, err = db.Prepare(selectCurrEventsByApplicationServiceIDSQL); err != nil {
return return
} }
if s.countEventsByApplicationServiceIDStmt, err = db.Prepare(countEventsByApplicationServiceIDSQL); err != nil { if s.countEventsByApplicationServiceIDStmt, err = db.Prepare(countEventsByApplicationServiceIDSQL); err != nil {
@ -121,28 +113,8 @@ func (s *eventsStatements) selectEventsByApplicationServiceID(
events []gomatrixserverlib.ApplicationServiceEvent, events []gomatrixserverlib.ApplicationServiceEvent,
err error, err error,
) { ) {
// First check to see if there are any events part of an old transaction // Retrieve events from the database. Unsuccessfully sent events first
eventRowsPast, err := s.selectPastEventsByApplicationServiceIDStmt.QueryContext(ctx, applicationServiceID, limit) eventRowsCurr, err := s.selectEventsByApplicationServiceIDStmt.QueryContext(ctx, applicationServiceID, limit)
if err != nil {
return 0, 0, nil, err
}
defer func() {
err = eventRowsPast.Close()
if err != nil {
log.WithError(err).Fatalf("Appservice %s unable to select past events to send",
applicationServiceID)
}
}()
events, txnID, maxID, err = retrieveEvents(eventRowsPast)
if err != nil {
return 0, 0, nil, err
}
if len(events) > 0 {
return
}
// Else, if there are old events with existing transaction IDs, grab a batch of new events
eventRowsCurr, err := s.selectCurrEventsByApplicationServiceIDStmt.QueryContext(ctx, applicationServiceID, limit)
if err != nil { if err != nil {
return 0, 0, nil, err return 0, 0, nil, err
} }
@ -163,6 +135,9 @@ func (s *eventsStatements) selectEventsByApplicationServiceID(
func retrieveEvents(eventRows *sql.Rows) (events []gomatrixserverlib.ApplicationServiceEvent, txnID, maxID int, err error) { func retrieveEvents(eventRows *sql.Rows) (events []gomatrixserverlib.ApplicationServiceEvent, txnID, maxID int, err error) {
// Iterate through each row and store event contents // Iterate through each row and store event contents
// If txn_id changes dramatically, we've switched from collecting old events to
// new ones. Send back those events first.
lastTxnID := -2 // Invalid transaction ID
for eventRows.Next() { for eventRows.Next() {
var event gomatrixserverlib.ApplicationServiceEvent var event gomatrixserverlib.ApplicationServiceEvent
var eventContent sql.NullString var eventContent sql.NullString
@ -181,6 +156,14 @@ func retrieveEvents(eventRows *sql.Rows) (events []gomatrixserverlib.Application
fmt.Println("Failed:", err.Error()) fmt.Println("Failed:", err.Error())
return nil, 0, 0, err return nil, 0, 0, err
} }
// If txnID has changed on this event from the previous event, then we've
// reached the end of a transaction's events. Return only those events.
if lastTxnID > -2 && lastTxnID != txnID {
return
}
lastTxnID = txnID
if eventContent.Valid { if eventContent.Valid {
event.Content = gomatrixserverlib.RawJSON(eventContent.String) event.Content = gomatrixserverlib.RawJSON(eventContent.String)
} }
@ -192,7 +175,8 @@ func retrieveEvents(eventRows *sql.Rows) (events []gomatrixserverlib.Application
ageMilli := time.Now().UnixNano() / int64(time.Millisecond) ageMilli := time.Now().UnixNano() / int64(time.Millisecond)
event.Age = ageMilli - event.OriginServerTimestamp event.Age = ageMilli - event.OriginServerTimestamp
// TODO: Synapse does this. Do app services really require this? :) // TODO: Synapse does this. It's unnecessary to send Sender and UserID as the
// same value. Do app services really require this? :)
event.Sender = event.UserID event.Sender = event.UserID
events = append(events, event) events = append(events, event)

View file

@ -39,7 +39,7 @@ var (
// TL;DR: Don't lower this number with any AS events still left in the database. // TL;DR: Don't lower this number with any AS events still left in the database.
transactionBatchSize = 50 transactionBatchSize = 50
// Timeout for sending a single transaction to an application service. // Timeout for sending a single transaction to an application service.
transactionTimeout = time.Second * 15 transactionTimeout = time.Second * 60
// The current transaction ID. Increments after every successful transaction. // The current transaction ID. Increments after every successful transaction.
currentTransactionID = 0 currentTransactionID = 0
) )
@ -98,7 +98,6 @@ func worker(db *storage.Database, ws types.ApplicationServiceWorkerState) {
for { for {
// Wait for more events if we've sent all the events in the database // Wait for more events if we've sent all the events in the database
if *ws.EventsReady <= 0 { if *ws.EventsReady <= 0 {
fmt.Println("Waiting")
ws.Cond.L.Lock() ws.Cond.L.Lock()
ws.Cond.Wait() ws.Cond.Wait()
ws.Cond.L.Unlock() ws.Cond.L.Unlock()
@ -114,11 +113,15 @@ func worker(db *storage.Database, ws types.ApplicationServiceWorkerState) {
} }
// Send the events off to the application service // Send the events off to the application service
err = send(client, ws.AppService, transactionID, transactionJSON) // Backoff if the application service does not respond
if err != nil { for {
// Backoff err = send(client, ws.AppService, transactionID, transactionJSON)
backoff(err, &ws) if err != nil {
continue // Backoff
backoff(&ws, err)
continue
}
break
} }
// We sent successfully, hooray! // We sent successfully, hooray!
@ -147,7 +150,7 @@ func worker(db *storage.Database, ws types.ApplicationServiceWorkerState) {
} }
// backoff pauses the calling goroutine for a 2^some backoff exponent seconds // backoff pauses the calling goroutine for a 2^some backoff exponent seconds
func backoff(err error, ws *types.ApplicationServiceWorkerState) { func backoff(ws *types.ApplicationServiceWorkerState, err error) {
// Calculate how long to backoff for // Calculate how long to backoff for
backoffDuration := time.Duration(math.Pow(2, float64(ws.Backoff))) backoffDuration := time.Duration(math.Pow(2, float64(ws.Backoff)))
backoffSeconds := time.Second * backoffDuration backoffSeconds := time.Second * backoffDuration
@ -233,9 +236,8 @@ func send(
// Check the AS received the events correctly // Check the AS received the events correctly
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return fmt.Errorf( // TODO: Handle non-200 error codes from application services
"Non-OK status code %d returned from AS", resp.StatusCode, return fmt.Errorf("Non-OK status code %d returned from AS", resp.StatusCode)
)
} }
return nil return nil