mirror of
https://github.com/go-gitea/gitea.git
synced 2026-06-14 03:29:55 +00:00
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:
+17
-3
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user