Retry create issue to cope with duplicate keys (#7898)
* Retry create issue to cope with duplicate keys * Use .SetExpr().Where().Insert()
This commit is contained in:

committed by
techknowlogick

parent
541fab196f
commit
5fe2ec264f
@ -5,7 +5,6 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
@ -74,6 +73,7 @@ var (
|
||||
|
||||
const issueTasksRegexpStr = `(^\s*[-*]\s\[[\sx]\]\s.)|(\n\s*[-*]\s\[[\sx]\]\s.)`
|
||||
const issueTasksDoneRegexpStr = `(^\s*[-*]\s\[[x]\]\s.)|(\n\s*[-*]\s\[[x]\]\s.)`
|
||||
const issueMaxDupIndexAttempts = 3
|
||||
|
||||
func init() {
|
||||
issueTasksPat = regexp.MustCompile(issueTasksRegexpStr)
|
||||
@ -1031,36 +1031,9 @@ type NewIssueOptions struct {
|
||||
IsPull bool
|
||||
}
|
||||
|
||||
// GetMaxIndexOfIssue returns the max index on issue
|
||||
func GetMaxIndexOfIssue(repoID int64) (int64, error) {
|
||||
return getMaxIndexOfIssue(x, repoID)
|
||||
}
|
||||
|
||||
func getMaxIndexOfIssue(e Engine, repoID int64) (int64, error) {
|
||||
var (
|
||||
maxIndex int64
|
||||
has bool
|
||||
err error
|
||||
)
|
||||
|
||||
has, err = e.SQL("SELECT COALESCE((SELECT MAX(`index`) FROM issue WHERE repo_id = ?),0)", repoID).Get(&maxIndex)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else if !has {
|
||||
return 0, errors.New("Retrieve Max index from issue failed")
|
||||
}
|
||||
return maxIndex, nil
|
||||
}
|
||||
|
||||
func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
|
||||
opts.Issue.Title = strings.TrimSpace(opts.Issue.Title)
|
||||
|
||||
maxIndex, err := getMaxIndexOfIssue(e, opts.Issue.RepoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.Issue.Index = maxIndex + 1
|
||||
|
||||
if opts.Issue.MilestoneID > 0 {
|
||||
milestone, err := getMilestoneByRepoID(e, opts.Issue.RepoID, opts.Issue.MilestoneID)
|
||||
if err != nil && !IsErrMilestoneNotExist(err) {
|
||||
@ -1109,10 +1082,31 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
|
||||
}
|
||||
|
||||
// Milestone and assignee validation should happen before insert actual object.
|
||||
if _, err = e.Insert(opts.Issue); err != nil {
|
||||
|
||||
// There's no good way to identify a duplicate key error in database/sql; brute force some retries
|
||||
dupIndexAttempts := issueMaxDupIndexAttempts
|
||||
for {
|
||||
_, err := e.SetExpr("`index`", "coalesce(MAX(`index`),0)+1").
|
||||
Where("repo_id=?", opts.Issue.RepoID).
|
||||
Insert(opts.Issue)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
dupIndexAttempts--
|
||||
if dupIndexAttempts <= 0 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
inserted, err := getIssueByID(e, opts.Issue.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch Index with the value calculated by the database
|
||||
opts.Issue.Index = inserted.Index
|
||||
|
||||
if opts.Issue.MilestoneID > 0 {
|
||||
if err = changeMilestoneAssign(e, doer, opts.Issue, -1); err != nil {
|
||||
return err
|
||||
|
Reference in New Issue
Block a user