Skip to content

Commit

Permalink
rollback repo on error after session closed
Browse files Browse the repository at this point in the history
  • Loading branch information
6543 committed Jan 18, 2021
1 parent a570749 commit 0f52aa8
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 44 deletions.
70 changes: 32 additions & 38 deletions models/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -1511,74 +1511,62 @@ func UpdateRepositoryUnits(repo *Repository, units []RepoUnit, deleteUnitTypes [
}

// DeleteRepository deletes a repository for a user or organization.
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
func DeleteRepository(doer *User, uid, repoID int64) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}

if err := deleteRepository(sess, doer, uid, repoID); err != nil {
return err
}

return sess.Commit()
}

// DeleteRepositoryWithContext deletes a repository for a user or organization.
func DeleteRepositoryWithContext(ctx DBContext, doer *User, uid, repoID int64) error {
return deleteRepository(ctx.e, doer, uid, repoID)
}

func deleteRepository(e Engine, doer *User, uid, repoID int64) error {
// In case is a organization.
org, err := getUserByID(e, uid)
org, err := getUserByID(sess, uid)
if err != nil {
return err
}
if org.IsOrganization() {
if err = org.getTeams(e); err != nil {
if err = org.getTeams(sess); err != nil {
return err
}
}

repo := &Repository{OwnerID: uid}
has, err := e.ID(repoID).Get(repo)
has, err := sess.ID(repoID).Get(repo)
if err != nil {
return err
} else if !has {
return ErrRepoNotExist{repoID, uid, "", ""}
}

// Delete Deploy Keys
deployKeys, err := listDeployKeys(e, repo.ID, ListOptions{})
deployKeys, err := listDeployKeys(sess, repo.ID, ListOptions{})
if err != nil {
return fmt.Errorf("listDeployKeys: %v", err)
}
for _, dKey := range deployKeys {
if err := deleteDeployKey(e, doer, dKey.ID); err != nil {
if err := deleteDeployKey(sess, doer, dKey.ID); err != nil {
return fmt.Errorf("deleteDeployKeys: %v", err)
}
}

if cnt, err := e.ID(repoID).Delete(&Repository{}); err != nil {
if cnt, err := sess.ID(repoID).Delete(&Repository{}); err != nil {
return err
} else if cnt != 1 {
return ErrRepoNotExist{repoID, uid, "", ""}
}

if org.IsOrganization() {
for _, t := range org.Teams {
if !t.hasRepository(e, repoID) {
if !t.hasRepository(sess, repoID) {
continue
} else if err = t.removeRepository(e, repo, false); err != nil {
} else if err = t.removeRepository(sess, repo, false); err != nil {
return err
}
}
}

attachments := make([]*Attachment, 0, 20)
if err = e.Join("INNER", "`release`", "`release`.id = `attachment`.release_id").
if err = sess.Join("INNER", "`release`", "`release`.id = `attachment`.release_id").
Where("`release`.repo_id = ?", repoID).
Find(&attachments); err != nil {
return err
Expand All @@ -1588,11 +1576,11 @@ func deleteRepository(e Engine, doer *User, uid, repoID int64) error {
releaseAttachments = append(releaseAttachments, attachments[i].RelativePath())
}

if _, err = e.Exec("UPDATE `user` SET num_stars=num_stars-1 WHERE id IN (SELECT `uid` FROM `star` WHERE repo_id = ?)", repo.ID); err != nil {
if _, err = sess.Exec("UPDATE `user` SET num_stars=num_stars-1 WHERE id IN (SELECT `uid` FROM `star` WHERE repo_id = ?)", repo.ID); err != nil {
return err
}

if err = deleteBeans(e,
if err = deleteBeans(sess,
&Access{RepoID: repo.ID},
&Action{RepoID: repo.ID},
&Watch{RepoID: repoID},
Expand All @@ -1618,79 +1606,85 @@ func deleteRepository(e Engine, doer *User, uid, repoID int64) error {

// Delete Issues and related objects
var attachmentPaths []string
if attachmentPaths, err = deleteIssuesByRepoID(e, repoID); err != nil {
if attachmentPaths, err = deleteIssuesByRepoID(sess, repoID); err != nil {
return err
}

if _, err = e.Where("repo_id = ?", repoID).Delete(new(RepoUnit)); err != nil {
if _, err = sess.Where("repo_id = ?", repoID).Delete(new(RepoUnit)); err != nil {
return err
}

if repo.IsFork {
if _, err = e.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkID); err != nil {
if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkID); err != nil {
return fmt.Errorf("decrease fork count: %v", err)
}
}

if _, err = e.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", uid); err != nil {
if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", uid); err != nil {
return err
}

if len(repo.Topics) > 0 {
if err = removeTopicsFromRepo(e, repo.ID); err != nil {
if err = removeTopicsFromRepo(sess, repo.ID); err != nil {
return err
}
}

projects, _, err := getProjects(e, ProjectSearchOptions{
projects, _, err := getProjects(sess, ProjectSearchOptions{
RepoID: repoID,
})
if err != nil {
return fmt.Errorf("get projects: %v", err)
}
for i := range projects {
if err := deleteProjectByID(e, projects[i].ID); err != nil {
if err := deleteProjectByID(sess, projects[i].ID); err != nil {
return fmt.Errorf("delete project [%d]: %v", projects[i].ID, err)
}
}

// FIXME: Remove repository files should be executed after transaction succeed.
repoPath := repo.RepoPath()
removeAllWithNotice(e, "Delete repository files", repoPath)
removeAllWithNotice(sess, "Delete repository files", repoPath)

err = repo.deleteWiki(e)
err = repo.deleteWiki(sess)
if err != nil {
return err
}

// Remove LFS objects
var lfsObjects []*LFSMetaObject
if err = e.Where("repository_id=?", repoID).Find(&lfsObjects); err != nil {
if err = sess.Where("repository_id=?", repoID).Find(&lfsObjects); err != nil {
return err
}

for _, v := range lfsObjects {
count, err := e.Count(&LFSMetaObject{Oid: v.Oid})
count, err := sess.Count(&LFSMetaObject{Oid: v.Oid})
if err != nil {
return err
}
if count > 1 {
continue
}

removeStorageWithNotice(e, storage.LFS, "Delete orphaned LFS file", v.RelativePath())
removeStorageWithNotice(sess, storage.LFS, "Delete orphaned LFS file", v.RelativePath())
}

if _, err := e.Delete(&LFSMetaObject{RepositoryID: repoID}); err != nil {
if _, err := sess.Delete(&LFSMetaObject{RepositoryID: repoID}); err != nil {
return err
}

if repo.NumForks > 0 {
if _, err = e.Exec("UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?", false, repo.ID); err != nil {
if _, err = sess.Exec("UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?", false, repo.ID); err != nil {
log.Error("reset 'fork_id' and 'is_fork': %v", err)
}
}

if err = sess.Commit(); err != nil {
return err
}

sess.Close()

// We should always delete the files after the database transaction succeed. If
// we delete the file but the database rollback, the repository will be borken.

Expand Down
18 changes: 12 additions & 6 deletions modules/repository/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (*mod
TrustModel: opts.TrustModel,
}

var rollbackRepo *models.Repository

if err := models.WithTx(func(ctx models.DBContext) error {
if err := models.CreateRepository(ctx, doer, u, repo, false); err != nil {
return err
Expand Down Expand Up @@ -95,9 +97,8 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (*mod
// Initialize Issue Labels if selected
if len(opts.IssueLabels) > 0 {
if err = models.InitializeLabels(ctx, repo.ID, opts.IssueLabels, false); err != nil {
if errDelete := models.DeleteRepositoryWithContext(ctx, doer, u.ID, repo.ID); errDelete != nil {
log.Error("Rollback deleteRepository: %v", errDelete)
}
rollbackRepo = repo
rollbackRepo.OwnerID = u.ID
return fmt.Errorf("InitializeLabels: %v", err)
}
}
Expand All @@ -106,13 +107,18 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (*mod
SetDescription(fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath)).
RunInDir(repoPath); err != nil {
log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err)
if errDelete := models.DeleteRepositoryWithContext(ctx, doer, u.ID, repo.ID); errDelete != nil {
log.Error("Rollback deleteRepository: %v", errDelete)
}
rollbackRepo = repo
rollbackRepo.OwnerID = u.ID
return fmt.Errorf("CreateRepository(git update-server-info): %v", err)
}
return nil
}); err != nil {
if rollbackRepo != nil {
if errDelete := models.DeleteRepository(doer, rollbackRepo.OwnerID, rollbackRepo.ID); errDelete != nil {
log.Error("Rollback deleteRepository: %v", errDelete)
}
}

return nil, err
}

Expand Down

0 comments on commit 0f52aa8

Please sign in to comment.