mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	Add global lock for migrations to make upgrade more safe with multiple replications (#33706)
This commit is contained in:
		| @@ -4,6 +4,7 @@ | |||||||
| package cmd | package cmd | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	golog "log" | 	golog "log" | ||||||
| 	"os" | 	"os" | ||||||
| @@ -130,8 +131,8 @@ func runRecreateTable(ctx *cli.Context) error { | |||||||
| 	} | 	} | ||||||
| 	recreateTables := migrate_base.RecreateTables(beans...) | 	recreateTables := migrate_base.RecreateTables(beans...) | ||||||
|  |  | ||||||
| 	return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error { | 	return db.InitEngineWithMigration(stdCtx, func(ctx context.Context, x *xorm.Engine) error { | ||||||
| 		if err := migrations.EnsureUpToDate(x); err != nil { | 		if err := migrations.EnsureUpToDate(ctx, x); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		return recreateTables(x) | 		return recreateTables(x) | ||||||
|   | |||||||
| @@ -7,9 +7,9 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/migrations" |  | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/services/versioned_migration" | ||||||
|  |  | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| ) | ) | ||||||
| @@ -36,7 +36,7 @@ func runMigrate(ctx *cli.Context) error { | |||||||
| 	log.Info("Log path: %s", setting.Log.RootPath) | 	log.Info("Log path: %s", setting.Log.RootPath) | ||||||
| 	log.Info("Configuration file: %s", setting.CustomConf) | 	log.Info("Configuration file: %s", setting.CustomConf) | ||||||
|  |  | ||||||
| 	if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { | 	if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil { | ||||||
| 		log.Fatal("Failed to initialize ORM engine: %v", err) | 		log.Fatal("Failed to initialize ORM engine: %v", err) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -13,7 +13,6 @@ import ( | |||||||
| 	actions_model "code.gitea.io/gitea/models/actions" | 	actions_model "code.gitea.io/gitea/models/actions" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
| 	"code.gitea.io/gitea/models/migrations" |  | ||||||
| 	packages_model "code.gitea.io/gitea/models/packages" | 	packages_model "code.gitea.io/gitea/models/packages" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -21,6 +20,7 @@ import ( | |||||||
| 	packages_module "code.gitea.io/gitea/modules/packages" | 	packages_module "code.gitea.io/gitea/modules/packages" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/storage" | 	"code.gitea.io/gitea/modules/storage" | ||||||
|  | 	"code.gitea.io/gitea/services/versioned_migration" | ||||||
|  |  | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| ) | ) | ||||||
| @@ -227,7 +227,7 @@ func runMigrateStorage(ctx *cli.Context) error { | |||||||
| 	log.Info("Log path: %s", setting.Log.RootPath) | 	log.Info("Log path: %s", setting.Log.RootPath) | ||||||
| 	log.Info("Configuration file: %s", setting.CustomConf) | 	log.Info("Configuration file: %s", setting.CustomConf) | ||||||
|  |  | ||||||
| 	if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil { | 	if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil { | ||||||
| 		log.Fatal("Failed to initialize ORM engine: %v", err) | 		log.Fatal("Failed to initialize ORM engine: %v", err) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -105,7 +105,7 @@ func UnsetDefaultEngine() { | |||||||
| // When called from the "doctor" command, the migration function is a version check | // When called from the "doctor" command, the migration function is a version check | ||||||
| // that prevents the doctor from fixing anything in the database if the migration level | // that prevents the doctor from fixing anything in the database if the migration level | ||||||
| // is different from the expected value. | // is different from the expected value. | ||||||
| func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) { | func InitEngineWithMigration(ctx context.Context, migrateFunc func(context.Context, *xorm.Engine) error) (err error) { | ||||||
| 	if err = InitEngine(ctx); err != nil { | 	if err = InitEngine(ctx); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -122,7 +122,7 @@ func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) | |||||||
| 	// Installation should only be being re-run if users want to recover an old database. | 	// Installation should only be being re-run if users want to recover an old database. | ||||||
| 	// However, we should think carefully about should we support re-install on an installed instance, | 	// However, we should think carefully about should we support re-install on an installed instance, | ||||||
| 	// as there may be other problems due to secret reinitialization. | 	// as there may be other problems due to secret reinitialization. | ||||||
| 	if err = migrateFunc(xormEngine); err != nil { | 	if err = migrateFunc(ctx, xormEngine); err != nil { | ||||||
| 		return fmt.Errorf("migrate: %w", err) | 		return fmt.Errorf("migrate: %w", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -413,7 +413,7 @@ func ExpectedDBVersion() int64 { | |||||||
| } | } | ||||||
|  |  | ||||||
| // EnsureUpToDate will check if the db is at the correct version | // EnsureUpToDate will check if the db is at the correct version | ||||||
| func EnsureUpToDate(x *xorm.Engine) error { | func EnsureUpToDate(ctx context.Context, x *xorm.Engine) error { | ||||||
| 	currentDB, err := GetCurrentDBVersion(x) | 	currentDB, err := GetCurrentDBVersion(x) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/setting/config" | 	"code.gitea.io/gitea/modules/setting/config" | ||||||
|  | 	"code.gitea.io/gitea/services/versioned_migration" | ||||||
|  |  | ||||||
| 	"xorm.io/xorm" | 	"xorm.io/xorm" | ||||||
| ) | ) | ||||||
| @@ -41,16 +42,16 @@ func InitDBEngine(ctx context.Context) (err error) { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func migrateWithSetting(x *xorm.Engine) error { | func migrateWithSetting(ctx context.Context, x *xorm.Engine) error { | ||||||
| 	if setting.Database.AutoMigration { | 	if setting.Database.AutoMigration { | ||||||
| 		return migrations.Migrate(x) | 		return versioned_migration.Migrate(ctx, x) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if current, err := migrations.GetCurrentDBVersion(x); err != nil { | 	if current, err := migrations.GetCurrentDBVersion(x); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if current < 0 { | 	} else if current < 0 { | ||||||
| 		// execute migrations when the database isn't initialized even if AutoMigration is false | 		// execute migrations when the database isn't initialized even if AutoMigration is false | ||||||
| 		return migrations.Migrate(x) | 		return versioned_migration.Migrate(ctx, x) | ||||||
| 	} else if expected := migrations.ExpectedDBVersion(); current != expected { | 	} else if expected := migrations.ExpectedDBVersion(); current != expected { | ||||||
| 		log.Fatal(`"database.AUTO_MIGRATION" is disabled, but current database version %d is not equal to the expected version %d.`+ | 		log.Fatal(`"database.AUTO_MIGRATION" is disabled, but current database version %d is not equal to the expected version %d.`+ | ||||||
| 			`You can set "database.AUTO_MIGRATION" to true or migrate manually by running "gitea [--config /path/to/app.ini] migrate"`, current, expected) | 			`You can set "database.AUTO_MIGRATION" to true or migrate manually by running "gitea [--config /path/to/app.ini] migrate"`, current, expected) | ||||||
|   | |||||||
| @@ -17,7 +17,6 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	db_install "code.gitea.io/gitea/models/db/install" | 	db_install "code.gitea.io/gitea/models/db/install" | ||||||
| 	"code.gitea.io/gitea/models/migrations" |  | ||||||
| 	system_model "code.gitea.io/gitea/models/system" | 	system_model "code.gitea.io/gitea/models/system" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/auth/password/hash" | 	"code.gitea.io/gitea/modules/auth/password/hash" | ||||||
| @@ -37,6 +36,7 @@ import ( | |||||||
| 	auth_service "code.gitea.io/gitea/services/auth" | 	auth_service "code.gitea.io/gitea/services/auth" | ||||||
| 	"code.gitea.io/gitea/services/context" | 	"code.gitea.io/gitea/services/context" | ||||||
| 	"code.gitea.io/gitea/services/forms" | 	"code.gitea.io/gitea/services/forms" | ||||||
|  | 	"code.gitea.io/gitea/services/versioned_migration" | ||||||
|  |  | ||||||
| 	"gitea.com/go-chi/session" | 	"gitea.com/go-chi/session" | ||||||
| ) | ) | ||||||
| @@ -359,7 +359,7 @@ func SubmitInstall(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Init the engine with migration | 	// Init the engine with migration | ||||||
| 	if err = db.InitEngineWithMigration(ctx, migrations.Migrate); err != nil { | 	if err = db.InitEngineWithMigration(ctx, versioned_migration.Migrate); err != nil { | ||||||
| 		db.UnsetDefaultEngine() | 		db.UnsetDefaultEngine() | ||||||
| 		ctx.Data["Err_DbSetting"] = true | 		ctx.Data["Err_DbSetting"] = true | ||||||
| 		ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form) | 		ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form) | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/migrations" | 	"code.gitea.io/gitea/models/migrations" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
|  | 	"code.gitea.io/gitea/services/versioned_migration" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func checkDBVersion(ctx context.Context, logger log.Logger, autofix bool) error { | func checkDBVersion(ctx context.Context, logger log.Logger, autofix bool) error { | ||||||
| @@ -21,7 +22,7 @@ func checkDBVersion(ctx context.Context, logger log.Logger, autofix bool) error | |||||||
| 		logger.Warn("Got Error: %v during ensure up to date", err) | 		logger.Warn("Got Error: %v during ensure up to date", err) | ||||||
| 		logger.Warn("Attempting to migrate to the latest DB version to fix this.") | 		logger.Warn("Attempting to migrate to the latest DB version to fix this.") | ||||||
|  |  | ||||||
| 		err = db.InitEngineWithMigration(ctx, migrations.Migrate) | 		err = db.InitEngineWithMigration(ctx, versioned_migration.Migrate) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			logger.Critical("Error: %v during migration", err) | 			logger.Critical("Error: %v during migration", err) | ||||||
| 		} | 		} | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								services/versioned_migration/migration.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								services/versioned_migration/migration.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | // Copyright 2025 The Gitea Authors. All rights reserved. | ||||||
|  | // SPDX-License-Identifier: MIT | ||||||
|  |  | ||||||
|  | package versioned_migration //nolint | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/migrations" | ||||||
|  | 	"code.gitea.io/gitea/modules/globallock" | ||||||
|  |  | ||||||
|  | 	"xorm.io/xorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func Migrate(ctx context.Context, x *xorm.Engine) error { | ||||||
|  | 	// only one instance can do the migration at the same time if there are multiple instances | ||||||
|  | 	release, err := globallock.Lock(ctx, "gitea_versioned_migration") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer release() | ||||||
|  |  | ||||||
|  | 	return migrations.Migrate(x) | ||||||
|  | } | ||||||
| @@ -5,6 +5,7 @@ package migrations | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"compress/gzip" | 	"compress/gzip" | ||||||
|  | 	"context" | ||||||
| 	"database/sql" | 	"database/sql" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| @@ -247,7 +248,7 @@ func restoreOldDB(t *testing.T, version string) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func wrappedMigrate(x *xorm.Engine) error { | func wrappedMigrate(ctx context.Context, x *xorm.Engine) error { | ||||||
| 	currentEngine = x | 	currentEngine = x | ||||||
| 	return migrations.Migrate(x) | 	return migrations.Migrate(x) | ||||||
| } | } | ||||||
| @@ -264,7 +265,7 @@ func doMigrationTest(t *testing.T, version string) { | |||||||
|  |  | ||||||
| 	beans, _ := db.NamesToBean() | 	beans, _ := db.NamesToBean() | ||||||
|  |  | ||||||
| 	err = db.InitEngineWithMigration(t.Context(), func(x *xorm.Engine) error { | 	err = db.InitEngineWithMigration(t.Context(), func(ctx context.Context, x *xorm.Engine) error { | ||||||
| 		currentEngine = x | 		currentEngine = x | ||||||
| 		return migrate_base.RecreateTables(beans...)(x) | 		return migrate_base.RecreateTables(beans...)(x) | ||||||
| 	}) | 	}) | ||||||
| @@ -272,7 +273,7 @@ func doMigrationTest(t *testing.T, version string) { | |||||||
| 	currentEngine.Close() | 	currentEngine.Close() | ||||||
|  |  | ||||||
| 	// We do this a second time to ensure that there is not a problem with retained indices | 	// We do this a second time to ensure that there is not a problem with retained indices | ||||||
| 	err = db.InitEngineWithMigration(t.Context(), func(x *xorm.Engine) error { | 	err = db.InitEngineWithMigration(t.Context(), func(ctx context.Context, x *xorm.Engine) error { | ||||||
| 		currentEngine = x | 		currentEngine = x | ||||||
| 		return migrate_base.RecreateTables(beans...)(x) | 		return migrate_base.RecreateTables(beans...)(x) | ||||||
| 	}) | 	}) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user