refactor: use modernc sqlite driver as default (#37562)

The mattn driver is still kept, can be enabled by
TAGS="sqlite_mattn sqlite_unlock_notify"

---------

Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
This commit is contained in:
wxiaoguang
2026-05-07 02:57:59 +08:00
committed by GitHub
parent b093c2c43c
commit a39af1a829
34 changed files with 185 additions and 149 deletions
+17 -3
View File
@@ -10,6 +10,7 @@ import (
"net/url"
"os"
"path/filepath"
"slices"
"strings"
"code.gitea.io/gitea/modules/setting"
@@ -31,7 +32,9 @@ type ConnOptions struct {
}
type SQLiteConnStrOptions struct {
FilePath string
FilePath string
// how long a concurrent query can wait for others (milliseconds),
// if timeout is reached, the error is something like "database is locked (SQLITE_BUSY)"
BusyTimeout int
JournalMode string
}
@@ -52,10 +55,21 @@ func GlobalConnOptions() ConnOptions {
}
}
const sqlDriverPostgresSchema = "postgresschema"
const (
sqlDriverPostgresSchema = "postgresschema"
sqlDriverSQLite3 = "sqlite3" // although database type also has "sqlite3", they are different, for different purposes
)
var makeSQLiteConnStr = func(opts SQLiteConnStrOptions) (string, string, error) {
return "", "", errors.New(`this Gitea binary was not built with SQLite3 support, get an official release or rebuild with: -tags sqlite,sqlite_unlock_notify`)
return "", "", errors.New(`this Gitea binary was not built with SQLite3 support, get an official release or rebuild with correct "-tags"`)
}
func registerSQLiteConnStrMaker(fn func(opts SQLiteConnStrOptions) (string, string, error)) {
if slices.Contains(setting.SupportedDatabaseTypes, setting.DatabaseTypeSQLite3) {
panic("another sqlite3 driver has been registered")
}
setting.SupportedDatabaseTypes = append(setting.SupportedDatabaseTypes, setting.DatabaseTypeSQLite3)
makeSQLiteConnStr = fn
}
func ConnStrDefaultDatabase(opts ConnOptions) (string, string, error) {
+3 -6
View File
@@ -1,4 +1,4 @@
//go:build sqlite
//go:build sqlite_mattn && sqlite_unlock_notify
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
@@ -10,14 +10,11 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/modules/setting"
_ "github.com/mattn/go-sqlite3"
)
func init() {
setting.SupportedDatabaseTypes = append(setting.SupportedDatabaseTypes, "sqlite3")
makeSQLiteConnStr = makeSQLiteConnStrMattnCGO
registerSQLiteConnStrMaker(makeSQLiteConnStrMattnCGO)
}
func makeSQLiteConnStrMattnCGO(opts SQLiteConnStrOptions) (string, string, error) {
@@ -30,5 +27,5 @@ func makeSQLiteConnStrMattnCGO(opts SQLiteConnStrOptions) (string, string, error
params = append(params, "_journal_mode="+opts.JournalMode)
}
connStr := fmt.Sprintf("file:%s?%s", opts.FilePath, strings.Join(params, "&"))
return "sqlite3", connStr, nil
return sqlDriverSQLite3, connStr, nil
}
+41
View File
@@ -0,0 +1,41 @@
//go:build !sqlite_mattn
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// modernc driver is chosen as the default one (compared to mattn, ncruces)
// * mattn was used as default, but it requires CGO
// * the CI times are almost the same for these three (race detector must be disabled)
// * modernc increases the binary size about 2MB, ncruces increases about 7MB
// * compiling time: modernc is slightly slower than mattn, ncruces is the slowest
package db
import (
"database/sql"
"fmt"
"strings"
"modernc.org/sqlite"
)
func init() {
// this driver contains huge amount of Golang code, so it is much slower when "-race" check is enabled.
registerSQLiteConnStrMaker(makeSQLiteConnStrModerncCCGO)
sql.Register(sqlDriverSQLite3, &sqlite.Driver{})
}
func makeSQLiteConnStrModerncCCGO(opts SQLiteConnStrOptions) (string, string, error) {
var params []string
// TODO: there is a changed behavior from mattn driver:
// * mattn driver can wait for pretty long time for concurrent accesses (not limited by the busy timeout)
// * but other drivers will report something like "database is locked (5) (SQLITE_BUSY)" if the timeout is reached
// Maybe we need to relax the busy timeout to a reasonable long time in the future
params = append(params, fmt.Sprintf("_pragma=busy_timeout(%d)", opts.BusyTimeout))
params = append(params, "_txlock=immediate")
if opts.JournalMode != "" {
params = append(params, fmt.Sprintf("_pragma=journal_mode(%s)", opts.JournalMode))
}
connStr := fmt.Sprintf("file:%s?%s", opts.FilePath, strings.Join(params, "&"))
return sqlDriverSQLite3, connStr, nil
}
+4 -6
View File
@@ -35,12 +35,10 @@ const (
FilterModeYourRepositories
)
const (
// MaxQueryParameters represents the max query parameters
// When queries are broken down in parts because of the number
// of parameters, attempt to break by this amount
MaxQueryParameters = 300
)
// MaxQueryParameters represents the max query parameters
// When queries are broken down in parts because of the number
// of parameters, attempt to break by this amount
var MaxQueryParameters = 300
// CountIssuesByRepo map from repoID to number of issues matching the options
func CountIssuesByRepo(ctx context.Context, opts *IssuesOptions) (map[int64]int64, error) {
+6 -14
View File
@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
"xorm.io/builder"
@@ -297,14 +298,11 @@ func TestIssue_ResolveMentions(t *testing.T) {
func TestResourceIndex(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
var wg sync.WaitGroup
for i := range 100 {
wg.Add(1)
go func(i int) {
for i := range 25 {
wg.Go(func() {
testInsertIssue(t, fmt.Sprintf("issue %d", i+1), "my issue", 0)
wg.Done()
}(i)
})
}
wg.Wait()
}
@@ -317,18 +315,12 @@ func TestCorrectIssueStats(t *testing.T) {
// maxQueryParameters + 10 issues into the testDatabase.
// Each new issues will have a constant description "Bugs are nasty"
// Which will be used later on.
defer test.MockVariableValue(&issues_model.MaxQueryParameters, 25)()
issueAmount := issues_model.MaxQueryParameters + 10
var wg sync.WaitGroup
for i := range issueAmount {
wg.Add(1)
go func(i int) {
testInsertIssue(t, fmt.Sprintf("Issue %d", i+1), "Bugs are nasty", 0)
wg.Done()
}(i)
testInsertIssue(t, fmt.Sprintf("Issue %d", i+1), "Bugs are nasty", 0)
}
wg.Wait()
// Now we will get all issueID's that match the "Bugs are nasty" query.
issues, err := issues_model.Issues(t.Context(), &issues_model.IssuesOptions{
+2 -2
View File
@@ -70,7 +70,7 @@ func prepareTestFixturesLoaders(t testing.TB) unittest.FixturesOptions {
opts := unittest.FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: []string{
"user.yml",
}}
require.NoError(t, unittest.CreateTestEngine(opts))
require.NoError(t, unittest.CreateTestEngine(filepath.Join(t.TempDir(), "sqlite-test.db"), opts))
return opts
}
@@ -95,7 +95,7 @@ func TestFixturesLoader(t *testing.T) {
func BenchmarkFixturesLoader(b *testing.B) {
opts := prepareTestFixturesLoaders(b)
require.NoError(b, unittest.CreateTestEngine(opts))
require.NoError(b, unittest.CreateTestEngine(filepath.Join(b.TempDir(), "sqlite-test.db"), opts))
loaderInternal, err := unittest.NewFixturesLoader(unittest.GetXORMEngine(), opts)
require.NoError(b, err)
loaderVendor, err := NewFixturesLoaderVendor(unittest.GetXORMEngine(), opts)
+4 -4
View File
@@ -56,7 +56,7 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int {
giteaRoot := setting.GetGiteaTestSourceRoot()
fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles}
if err := CreateTestEngine(fixturesOpts); err != nil {
if err := CreateTestEngine(filepath.Join(tempWorkPath, "sqlite-test.db"), fixturesOpts); err != nil {
return testlogger.MainErrorf("Error creating test database engine: %v", err)
}
@@ -205,9 +205,9 @@ type FixturesOptions struct {
Files []string
}
// CreateTestEngine creates a memory database and loads the fixture data from fixturesDir
func CreateTestEngine(opts FixturesOptions) error {
driver, connStr, err := db.ConnStr(db.ConnOptions{Type: "sqlite3", SQLitePath: ":memory:"})
// CreateTestEngine creates a test database and loads the fixture data from fixturesDir
func CreateTestEngine(testSQLiteFile string, opts FixturesOptions) error {
driver, connStr, err := db.ConnStr(db.ConnOptions{Type: setting.DatabaseTypeSQLite3, SQLitePath: testSQLiteFile, SQLiteBusyTimeout: 5000})
if err != nil {
return err
}