This is an automated email from the ASF dual-hosted git repository.

zhangliang2022 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake-website.git


The following commit(s) were added to refs/heads/main by this push:
     new abaa31441 docs: add roll back for migration (#218)
abaa31441 is described below

commit abaa314410c92922e9666d7fcf60ecbd80a7f637
Author: mappjzc <[email protected]>
AuthorDate: Thu Sep 22 23:48:52 2022 +0800

    docs: add roll back for migration (#218)
    
    Add RollBack for DB Migration.
    
    Nddtfjiang <[email protected]>
---
 docs/DeveloperManuals/DBMigration.md | 112 +++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/docs/DeveloperManuals/DBMigration.md 
b/docs/DeveloperManuals/DBMigration.md
index 2cb3000b7..b165644b5 100644
--- a/docs/DeveloperManuals/DBMigration.md
+++ b/docs/DeveloperManuals/DBMigration.md
@@ -56,3 +56,115 @@ for the framework-only migrations defined under the 
`models` package.
 2. Sort scripts by Version in ascending order.
 3. Execute scripts.
 4. Save results in the `migration_history` table.
+
+
+## Best Practices
+When you write a new migration script, please pay attention to the fault 
tolerance and the side effect. It would be better if the failed script could be 
safely retry, in case of something goes wrong during the migration. For this 
purpose, the migration scripts should be well-designed. For example, if you 
created a temporary table in the Up method, it should be dropped before 
exiting, regardless of success or failure. Using the defer statement to do some 
cleanup is a good idea. Let's demo [...]
+
+Suppose we want to recalculate the column `name` of the table `user`
+
+1. rename `user` to `user_bak` (stop if error, define `defer` to rename back 
on error)
+2. create new `user` (stop if error, define `defer` to drop TABLE on error)
+3. convert data from `user_bak` to `user` (stop if error)
+4. drop `user_bak`
+
+```golang
+
+type User struct {
+       name string `gorm:"type:varchar(255)"`
+}
+
+func (User) TableName() string {
+       return "user"
+}
+
+type NewUser struct {
+       name string `gorm:"type:text"`
+}
+
+func (NewUser) TableName() string {
+       return "user"
+}
+
+type UserBak struct {
+       name string `gorm:"type:varchar(255)"`
+}
+
+func (UserBak) TableName() string {
+       return "user_bak"
+}
+
+func (*exampleScript) Up(ctx context.Context, db *gorm.DB) (errs errors.Error) 
{
+       var err error
+
+       // rename the user_bak to cache old table
+       err = db.Migrator().RenameTable(&User{}, &UserBak{})
+       if err != nil {
+               return errors.Default.Wrap(err, "error no rename user to 
user_bak")
+       }
+
+       // rollback for rename back
+       defer func() {
+               if errs != nil {
+                       err = db.Migrator().RenameTable(&UserBak{}, &User{})
+                       if err != nil {
+                               errs = errors.Default.Wrap(err, 
fmt.Sprintf("fail to rollback table user_bak , you must to rollback by 
yourself. %s", err.Error()))
+                       }
+               }
+       }()
+
+       // create new user table
+       err = db.Migrator().AutoMigrate(&NewUser{})
+
+       if err != nil {
+               return errors.Default.Wrap(err, "error on auto migrate user")
+       }
+
+       // rollback for create new table
+       defer func() {
+               if errs != nil {
+                       err = db.Migrator().DropTable(&User{})
+                       if err != nil {
+                               errs = errors.Default.Wrap(err, 
fmt.Sprintf("fail to rollback table OldTable , you must to rollback by 
yourself. %s", err.Error()))
+                       }
+               }
+       }()
+
+       // update old id to new id and write to the new table
+       cursor, err := db.Model(&UserBak{}).Rows()
+       if err != nil {
+               return errors.Default.Wrap(err, "error on select NewTable")
+       }
+       defer cursor.Close()
+
+       // caculate and save the data to new table
+       batch, err := helper.NewBatchSave(api.BasicRes, 
reflect.TypeOf(&NewUser{}), 200)
+       if err != nil {
+               return errors.Default.Wrap(err, "error getting batch from table 
user")
+       }
+       defer batch.Close()
+       for cursor.Next() {
+               ot := UserBak{}
+               err = db.ScanRows(cursor, &ot)
+               if err != nil {
+                       return errors.Default.Wrap(err, "error scan rows from 
table user_bak")
+               }
+               nt := NewUser(ot)
+
+               nt.name = nt.name + "new"
+
+               err = batch.Add(&nt)
+               if err != nil {
+                       return errors.Default.Wrap(err, "error on user batch 
add")
+               }
+       }
+
+       // drop the old table
+       err = db.Migrator().DropTable(&UserBak{})
+       if err != nil {
+               return errors.Default.Wrap(err, "error no drop user_bak")
+       }
+}
+
+```
+

Reply via email to