mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-03 19:27:29 +02:00
fix: use READ_COMMITTED_SNAPSHOT on MSSQL (#39512)
MSSQL's default READ COMMITTED makes reads wait on writers, so the runner pickup deadlocks with concurrent claims, flaking `TestCreateTaskForRunnerConcurrentClaim`. - Enable `READ_COMMITTED_SNAPSHOT` on MSSQL so it reads like PostgreSQL and MySQL - Read the pickup cursor before claiming, a lost claim could skip waiting jobs - Add tests that fail without consistent READ COMMITTED Performance: Writes on MSSQL now also store the previous row version in tempdb, the same versioning cost PostgreSQL and MySQL always pay, and Azure SQL enables it by default. Reads no longer block on writers, and a 32-runner pickup stress test ran 2.5x faster with it. --------- Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Giteabot <teabot@gitea.io>
This commit is contained in:
4 files changed
+68
-4
No files matched your search
@@ -4,16 +4,20 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
actions_model "gitea.dev/models/actions"
|
||||
"gitea.dev/models/db"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/setting"
|
||||
"gitea.dev/tests"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// minimalWorkflowPayload returns the minimal YAML for a single-job workflow with no steps.
|
||||
@@ -116,3 +120,42 @@ func TestCreateTaskForRunnerConcurrentClaim(t *testing.T) {
|
||||
assert.NotZero(t, updated.TaskID)
|
||||
}
|
||||
}
|
||||
|
||||
func prepareWaitingRunJob(t *testing.T) *actions_model.ActionRunJob {
|
||||
if setting.Database.Type.IsSQLite3() {
|
||||
t.Skip("SQLite serializes write transactions")
|
||||
}
|
||||
job := &actions_model.ActionRunJob{RepoID: 1, Status: actions_model.StatusWaiting, RunsOn: []string{"ubuntu-latest"}}
|
||||
require.NoError(t, db.Insert(t.Context(), job))
|
||||
return job
|
||||
}
|
||||
|
||||
func TestCreateTaskForRunnerDuringOpenClaimDoesNotWait(t *testing.T) {
|
||||
job := prepareWaitingRunJob(t)
|
||||
|
||||
require.NoError(t, db.WithTx(t.Context(), func(ctx context.Context) error {
|
||||
_, err := db.GetEngine(ctx).ID(job.ID).Cols("name").Update(&actions_model.ActionRunJob{Name: "claiming"})
|
||||
require.NoError(t, err)
|
||||
pickupCtx, cancel := context.WithTimeout(t.Context(), 5*time.Second)
|
||||
defer cancel()
|
||||
_, _, err = actions_model.CreateTaskForRunner(pickupCtx, &actions_model.ActionRunner{})
|
||||
return err
|
||||
}))
|
||||
}
|
||||
|
||||
func TestClaimRunJobAfterConcurrentCancelUpdatesNothing(t *testing.T) {
|
||||
job := prepareWaitingRunJob(t)
|
||||
|
||||
require.NoError(t, db.WithTx(t.Context(), func(ctx context.Context) error {
|
||||
claimed, err := actions_model.GetRunJobByRepoAndID(ctx, job.RepoID, job.ID)
|
||||
require.NoError(t, err)
|
||||
_, err = db.GetEngine(t.Context()).ID(job.ID).Cols("status").Update(&actions_model.ActionRunJob{Status: actions_model.StatusCancelled})
|
||||
require.NoError(t, err)
|
||||
|
||||
claimed.TaskID, claimed.Status = 1, actions_model.StatusRunning
|
||||
affected, err := actions_model.UpdateRunJob(ctx, claimed, builder.Eq{"task_id": 0, "status": actions_model.StatusWaiting}, "task_id", "status")
|
||||
require.NoError(t, err)
|
||||
assert.Zero(t, affected)
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
Reference in new issue
Block a user