Эх сурвалжийг харах

Improve errors in state store migration edge cases

Tulir Asokan 5 жил өмнө
parent
commit
3be9aa2319

+ 1 - 1
config/config.go

@@ -43,7 +43,7 @@ type Config struct {
 			MaxIdleConns int `yaml:"max_idle_conns"`
 		} `yaml:"database"`
 
-		StateStore string `yaml:"state_store_path"`
+		StateStore string `yaml:"state_store_path,omitempty"`
 
 		ID  string `yaml:"id"`
 		Bot struct {

+ 2 - 2
database/upgrades/2018-09-01-initial-schema.go

@@ -6,9 +6,9 @@ import (
 )
 
 func init() {
-	upgrades[0] = upgrade{"Initial schema", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
+	upgrades[0] = upgrade{"Initial schema", func(tx *sql.Tx, ctx context) error {
 		var byteType string
-		if dialect == SQLite {
+		if ctx.dialect == SQLite {
 			byteType = "BLOB"
 		} else {
 			byteType = "bytea"

+ 2 - 2
database/upgrades/2019-05-16-message-delete-cascade.go

@@ -5,8 +5,8 @@ import (
 )
 
 func init() {
-	upgrades[1] = upgrade{"Add ON DELETE CASCADE to message table", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
-		if dialect == SQLite {
+	upgrades[1] = upgrade{"Add ON DELETE CASCADE to message table", func(tx *sql.Tx, ctx context) error {
+		if ctx.dialect == SQLite {
 			// SQLite doesn't support constraint updates, but it isn't that careful with constraints anyway.
 			return nil
 		}

+ 1 - 1
database/upgrades/2019-05-21-message-timestamp-column.go

@@ -5,7 +5,7 @@ import (
 )
 
 func init() {
-	upgrades[2] = upgrade{"Add timestamp column to messages", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
+	upgrades[2] = upgrade{"Add timestamp column to messages", func(tx *sql.Tx, ctx context) error {
 		_, err := tx.Exec("ALTER TABLE message ADD COLUMN timestamp BIGINT NOT NULL DEFAULT 0")
 		if err != nil {
 			return err

+ 1 - 1
database/upgrades/2019-05-22-user-last-connection-column.go

@@ -5,7 +5,7 @@ import (
 )
 
 func init() {
-	upgrades[3] = upgrade{"Add last_connection column to users", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
+	upgrades[3] = upgrade{"Add last_connection column to users", func(tx *sql.Tx, ctx context) error {
 		_, err := tx.Exec(`ALTER TABLE "user" ADD COLUMN last_connection BIGINT NOT NULL DEFAULT 0`)
 		if err != nil {
 			return err

+ 2 - 2
database/upgrades/2019-05-23-protoupgrade.go

@@ -8,8 +8,8 @@ import (
 
 func init() {
 	var keys = []string{"imageMessage", "contactMessage", "locationMessage", "extendedTextMessage", "documentMessage", "audioMessage", "videoMessage"}
-	upgrades[4] = upgrade{"Update message content to new protocol version. This may take a while.", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
-		rows, err := db.Query("SELECT mxid, content FROM message")
+	upgrades[4] = upgrade{"Update message content to new protocol version. This may take a while.", func(tx *sql.Tx, ctx context) error {
+		rows, err := ctx.db.Query("SELECT mxid, content FROM message")
 		if err != nil {
 			return err
 		}

+ 1 - 1
database/upgrades/2019-05-23-puppet-custom-mxid-columns.go

@@ -5,7 +5,7 @@ import (
 )
 
 func init() {
-	upgrades[5] = upgrade{"Add columns to store custom puppet info", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
+	upgrades[5] = upgrade{"Add columns to store custom puppet info", func(tx *sql.Tx, ctx context) error {
 		_, err := tx.Exec(`ALTER TABLE puppet ADD COLUMN custom_mxid VARCHAR(255)`)
 		if err != nil {
 			return err

+ 1 - 1
database/upgrades/2019-05-28-user-portal-table.go

@@ -5,7 +5,7 @@ import (
 )
 
 func init() {
-	upgrades[6] = upgrade{"Add user-portal mapping table", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
+	upgrades[6] = upgrade{"Add user-portal mapping table", func(tx *sql.Tx, ctx context) error {
 		_, err := tx.Exec(`CREATE TABLE user_portal (
 			user_jid        VARCHAR(255),
 			portal_jid      VARCHAR(255),

+ 1 - 1
database/upgrades/2019-06-01-avatar-url-fields.go

@@ -5,7 +5,7 @@ import (
 )
 
 func init() {
-	upgrades[7] = upgrade{"Add columns to store avatar MXC URIs", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
+	upgrades[7] = upgrade{"Add columns to store avatar MXC URIs", func(tx *sql.Tx, ctx context) error {
 		_, err := tx.Exec(`ALTER TABLE puppet ADD COLUMN avatar_url VARCHAR(255)`)
 		if err != nil {
 			return err

+ 1 - 1
database/upgrades/2019-08-10-portal-in-community-field.go

@@ -5,7 +5,7 @@ import (
 )
 
 func init() {
-	upgrades[8] = upgrade{"Add columns to store portal in filtering community meta", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
+	upgrades[8] = upgrade{"Add columns to store portal in filtering community meta", func(tx *sql.Tx, ctx context) error {
 		_, err := tx.Exec(`ALTER TABLE user_portal ADD COLUMN in_community BOOLEAN NOT NULL DEFAULT FALSE`)
 		return err
 	}}

+ 7 - 3
database/upgrades/2019-08-25-move-state-store-to-db.go

@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
+	"os"
 	"strings"
 
 	"maunium.net/go/mautrix"
@@ -78,15 +79,16 @@ func init() {
 		user_id VARCHAR(255) PRIMARY KEY
 	)`
 
-	upgrades[9] = upgrade{"Move state store to main DB", func(dialect Dialect, tx *sql.Tx, db *sql.DB) error {
+	upgrades[9] = upgrade{"Move state store to main DB", func(tx *sql.Tx, ctx context) error {
 		store := appservice.NewBasicStateStore().(*appservice.BasicStateStore)
 
-		if dialect == Postgres {
+		if ctx.dialect == Postgres {
 			roomStateTable = strings.Replace(roomStateTable, "TEXT", "JSONB", 1)
 		}
 
 		if data, err := ioutil.ReadFile("mx-state.json"); err != nil {
-			return err
+			ctx.log.Debugln("mx-state.json not found, not migrating state store")
+			return nil
 		} else if err = json.Unmarshal(data, &store); err != nil {
 			return err
 		} else if _, err := tx.Exec(userProfileTable); err != nil {
@@ -101,6 +103,8 @@ func init() {
 			return err
 		} else if err = migratePowerLevels(tx, store.PowerLevels); err != nil {
 			return err
+		} else if err = os.Rename("mx-state.json", "mx-state.json.bak"); err != nil {
+			return err
 		}
 		return nil
 	}}

+ 13 - 7
database/upgrades/upgrades.go

@@ -15,11 +15,17 @@ const (
 	SQLite
 )
 
-type upgradeFunc func(Dialect, *sql.Tx, *sql.DB) error
+type upgradeFunc func(*sql.Tx, context) error
+
+type context struct {
+	dialect Dialect
+	db      *sql.DB
+	log     log.Logger
+}
 
 type upgrade struct {
 	message string
-	fn upgradeFunc
+	fn      upgradeFunc
 }
 
 const NumberOfUpgrades = 10
@@ -28,7 +34,7 @@ var upgrades [NumberOfUpgrades]upgrade
 
 var UnsupportedDatabaseVersion = fmt.Errorf("unsupported database version")
 
-func getVersion(dialect Dialect, db *sql.DB) (int, error) {
+func GetVersion(db *sql.DB) (int, error) {
 	_, err := db.Exec("CREATE TABLE IF NOT EXISTS version (version INTEGER)")
 	if err != nil {
 		return -1, err
@@ -42,7 +48,7 @@ func getVersion(dialect Dialect, db *sql.DB) (int, error) {
 	return version, nil
 }
 
-func setVersion(dialect Dialect, tx *sql.Tx, version int) error {
+func SetVersion(tx *sql.Tx, version int) error {
 	_, err := tx.Exec("DELETE FROM version")
 	if err != nil {
 		return err
@@ -62,7 +68,7 @@ func Run(log log.Logger, dialectName string, db *sql.DB) error {
 		return fmt.Errorf("unknown dialect %s", dialectName)
 	}
 
-	version, err := getVersion(dialect, db)
+	version, err := GetVersion(db)
 	if err != nil {
 		return err
 	}
@@ -78,11 +84,11 @@ func Run(log log.Logger, dialectName string, db *sql.DB) error {
 		if err != nil {
 			return err
 		}
-		err = upgrade.fn(dialect, tx, db)
+		err = upgrade.fn(tx, context{dialect, db, log})
 		if err != nil {
 			return err
 		}
-		err = setVersion(dialect, tx, version+i+1)
+		err = SetVersion(tx, version+i+1)
 		if err != nil {
 			return err
 		}

+ 9 - 0
main.go

@@ -164,6 +164,15 @@ func (bridge *Bridge) Init() {
 		os.Exit(14)
 	}
 
+	if len(bridge.Config.AppService.StateStore) > 0 && bridge.Config.AppService.StateStore != "./mx-state.json" {
+		version, err := upgrades.GetVersion(bridge.DB.DB)
+		if version < 0 && err == nil {
+			bridge.Log.Fatalln("Non-standard state store path. Please move the state store to ./mx-state.json " +
+				"and update the config. The state store will be migrated into the db on the next launch.")
+			os.Exit(18)
+		}
+	}
+
 	bridge.Log.Debugln("Initializing state store")
 	bridge.StateStore = database.NewSQLStateStore(bridge.DB)
 	bridge.AS.StateStore = bridge.StateStore