dendrite/src/github.com/matrix-org/dendrite/clientapi/storage/syncserver.go

116 lines
3.8 KiB
Go
Raw Normal View History

package storage
import (
"database/sql"
// Import the postgres database driver.
_ "github.com/lib/pq"
"github.com/matrix-org/dendrite/common"
"github.com/matrix-org/gomatrixserverlib"
)
// SyncServerDatabase represents a sync server database
type SyncServerDatabase struct {
db *sql.DB
partitions common.PartitionOffsetStatements
events outputRoomEventsStatements
2017-04-05 04:30:13 -05:00
roomstate currentRoomStateStatements
}
// NewSyncServerDatabase creates a new sync server database
func NewSyncServerDatabase(dataSourceName string) (*SyncServerDatabase, error) {
var db *sql.DB
var err error
if db, err = sql.Open("postgres", dataSourceName); err != nil {
return nil, err
}
partitions := common.PartitionOffsetStatements{}
if err = partitions.Prepare(db); err != nil {
return nil, err
}
events := outputRoomEventsStatements{}
if err = events.prepare(db); err != nil {
return nil, err
}
2017-04-05 04:30:13 -05:00
state := currentRoomStateStatements{}
if err := state.prepare(db); err != nil {
return nil, err
}
return &SyncServerDatabase{db, partitions, events, state}, nil
}
// WriteEvent into the database. It is not safe to call this function from multiple goroutines, as it would create races
// when generating the stream position for this event. Returns the sync stream position for the inserted event.
// Returns an error if there was a problem inserting this event.
func (d *SyncServerDatabase) WriteEvent(ev *gomatrixserverlib.Event, addStateEventIDs, removeStateEventIDs []string) (streamPos int64, returnErr error) {
returnErr = runTransaction(d.db, func(txn *sql.Tx) error {
var err error
streamPos, err = d.events.InsertEvent(txn, ev, addStateEventIDs, removeStateEventIDs)
if err != nil {
2017-04-05 04:30:13 -05:00
return err
}
if len(addStateEventIDs) == 0 && len(removeStateEventIDs) == 0 {
// Nothing to do, the event may have just been a message event.
return nil
}
// Update the current room state based on the added/removed state event IDs.
// In the common case there is a single added event ID which is the state event itself, assuming `ev` is a state event.
// However, conflict resolution may result in there being different events being added, or even some removed.
if len(removeStateEventIDs) == 0 && len(addStateEventIDs) == 1 && addStateEventIDs[0] == ev.EventID() {
// common case
if err = d.roomstate.UpdateRoomState(txn, []gomatrixserverlib.Event{*ev}, nil); err != nil {
2017-04-05 04:30:13 -05:00
return err
}
return nil
}
// uncommon case: we need to fetch the full event for each event ID mentioned, then update room state
added, err := d.events.Events(txn, addStateEventIDs)
if err != nil {
return err
}
return d.roomstate.UpdateRoomState(txn, added, removeStateEventIDs)
})
return
}
// PartitionOffsets implements common.PartitionStorer
func (d *SyncServerDatabase) PartitionOffsets(topic string) ([]common.PartitionOffset, error) {
return d.partitions.SelectPartitionOffsets(topic)
}
// SetPartitionOffset implements common.PartitionStorer
func (d *SyncServerDatabase) SetPartitionOffset(topic string, partition int32, offset int64) error {
return d.partitions.UpsertPartitionOffset(topic, partition, offset)
}
2017-04-05 04:30:13 -05:00
// SyncStreamPosition returns the latest position in the sync stream. Returns 0 if there are no events yet.
func (d *SyncServerDatabase) SyncStreamPosition() (int64, error) {
return d.events.MaxID()
}
// EventsInRange returns all events in the given range, exclusive of oldPos, inclusive of newPos.
func (d *SyncServerDatabase) EventsInRange(oldPos, newPos int64) ([]gomatrixserverlib.Event, error) {
return d.events.InRange(oldPos, newPos)
}
2017-04-05 04:30:13 -05:00
func runTransaction(db *sql.DB, fn func(txn *sql.Tx) error) (err error) {
txn, err := db.Begin()
if err != nil {
return
}
defer func() {
if r := recover(); r != nil {
txn.Rollback()
panic(r)
} else if err != nil {
txn.Rollback()
} else {
err = txn.Commit()
}
}()
err = fn(txn)
return
}