Skip to content

Commit d37ffdf

Browse files
fix: database locks and busy
1 parent 577c481 commit d37ffdf

File tree

8 files changed

+101
-15
lines changed

8 files changed

+101
-15
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ db-migrate:
5050
# Generation
5151

5252
gen-jet:
53-
jet -source=sqlite -dsn="$(DB_PATH)" -path=./internal/jet -ignore-tables goose_db_version
53+
jet -source=sqlite -dsn="$(DB_PATH)" -path=./internal/jet -ignore-tables goose_db_version,_dummy
5454
rm -rf ./internal/jet/model
5555

5656
gen-templ:

cmd/stress-database/main.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"log"
8+
"os"
9+
"os/signal"
10+
"time"
11+
12+
"github.com/ItsNotGoodName/smtpbridge/internal/database"
13+
"github.com/ItsNotGoodName/smtpbridge/internal/models"
14+
"github.com/ItsNotGoodName/smtpbridge/internal/repo"
15+
)
16+
17+
func main() {
18+
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
19+
defer cancel()
20+
21+
db, err := database.New("./smtpbridge_data/smtpbridge.db", false)
22+
if err != nil {
23+
log.Fatalln(err)
24+
}
25+
26+
go func() {
27+
msg := models.Message{}
28+
for {
29+
id, err := repo.EnvelopeCreate(ctx, db, msg, []models.Attachment{})
30+
if err != nil {
31+
log.Fatalln(err)
32+
}
33+
34+
err = repo.MailmanEnqueue(ctx, db, id)
35+
if err != nil {
36+
log.Fatalln(err)
37+
}
38+
}
39+
}()
40+
41+
for {
42+
id, err := repo.MailmanDequeue(ctx, db)
43+
if err != nil {
44+
if !errors.Is(err, repo.ErrNoRows) {
45+
log.Fatalln(err)
46+
}
47+
48+
time.Sleep(1 * time.Second)
49+
}
50+
51+
fmt.Println(id)
52+
}
53+
}

internal/database/database.go

+13-9
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ type Querier interface {
1212
Conn() *sql.DB
1313
ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
1414
QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
15-
BeginTx(ctx context.Context, opts *sql.TxOptions) (QuerierTx, error)
15+
BeginTx(ctx context.Context, write bool) (QuerierTx, error)
1616
}
1717

1818
type QuerierTx interface {
@@ -34,6 +34,10 @@ func New(dbPath string, debug bool) (Querier, error) {
3434
return nil, err
3535
}
3636

37+
if err := dummyCreate(db); err != nil {
38+
return nil, err
39+
}
40+
3741
if debug {
3842
return DebugDB{DB: db}, nil
3943
}
@@ -45,8 +49,8 @@ type DB struct {
4549
*sql.DB
4650
}
4751

48-
func (db DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (QuerierTx, error) {
49-
return db.DB.BeginTx(ctx, opts)
52+
func (db DB) BeginTx(ctx context.Context, write bool) (QuerierTx, error) {
53+
return dummyBeginTx(ctx, db.DB, write)
5054
}
5155

5256
func (db DB) Conn() *sql.DB {
@@ -97,20 +101,16 @@ func (tx DebugTx) QueryContext(ctx context.Context, query string, args ...any) (
97101
return tx.Tx.QueryContext(ctx, query, args...)
98102
}
99103

100-
func (db DebugDB) BeginTx(ctx context.Context, opts *sql.TxOptions) (QuerierTx, error) {
104+
func (db DebugDB) BeginTx(ctx context.Context, write bool) (QuerierTx, error) {
101105
log.Debug().
102106
Msg("BeginTx (Tx)")
103-
tx, err := db.DB.BeginTx(ctx, opts)
107+
tx, err := dummyBeginTx(ctx, db.DB, write)
104108
if err != nil {
105109
return DebugTx{}, err
106110
}
107111
return DebugTx{Tx: tx}, nil
108112
}
109113

110-
type DebugTx struct {
111-
*sql.Tx
112-
}
113-
114114
func (tx DebugTx) Commit() error {
115115
log.Debug().
116116
Str("func", "Commit (Tx)").
@@ -124,3 +124,7 @@ func (tx DebugTx) Rollback() error {
124124
Msg("")
125125
return tx.Tx.Rollback()
126126
}
127+
128+
type DebugTx struct {
129+
*sql.Tx
130+
}

internal/database/dummy.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package database
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
)
7+
8+
func dummyCreate(db *sql.DB) error {
9+
_, err := db.Exec("CREATE TABLE IF NOT EXISTS _dummy(_dummy integer);")
10+
return err
11+
}
12+
13+
func dummyBeginTx(ctx context.Context, db *sql.DB, write bool) (*sql.Tx, error) {
14+
tx, err := db.BeginTx(ctx, nil)
15+
if err != nil {
16+
return nil, err
17+
}
18+
19+
if write {
20+
// This prevents SQLITE_BUSY (5) and database locked (517) when doing write transactions
21+
// because we tell sqlite that we are going to do a write transaction through the dummy DELETE query.
22+
_, err = tx.ExecContext(ctx, "DELETE FROM _dummy WHERE 0 = 1;")
23+
if err != nil {
24+
return nil, err
25+
}
26+
}
27+
28+
return tx, nil
29+
}

internal/repo/envelope.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var attachmentPJ ProjectionList = ProjectionList{
3131
}
3232

3333
func EnvelopeCreate(ctx context.Context, db database.Querier, msg models.Message, atts []models.Attachment) (int64, error) {
34-
tx, err := db.BeginTx(ctx, nil)
34+
tx, err := db.BeginTx(ctx, true)
3535
if err != nil {
3636
return 0, err
3737
}

internal/repo/internal.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func InternalSync(
2020
rules []models.Rule,
2121
ruleToEndpoints map[string][]string,
2222
) error {
23-
tx, err := db.BeginTx(ctx, nil)
23+
tx, err := db.BeginTx(ctx, true)
2424
if err != nil {
2525
return err
2626
}

internal/repo/mailman.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func MailmanEnqueue(ctx context.Context, db database.Querier, envelopeID int64)
2525
}
2626

2727
func MailmanDequeue(ctx context.Context, db database.Querier) (int64, error) {
28-
tx, err := db.BeginTx(ctx, nil)
28+
tx, err := db.BeginTx(ctx, true)
2929
if err != nil {
3030
return 0, err
3131
}

internal/repo/rule.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ var rulePJ ProjectionList = ProjectionList{
2222
var ruleCreatePJ ProjectionList = rulePJ.Except(Rules.ID)
2323

2424
func RuleCreate(ctx context.Context, db database.Querier, rule models.Rule, endpoints []int64) (int64, error) {
25-
tx, err := db.BeginTx(ctx, nil)
25+
tx, err := db.BeginTx(ctx, true)
2626
if err != nil {
2727
return 0, err
2828
}
@@ -129,7 +129,7 @@ func RuleEndpointsList(ctx context.Context, db database.Querier) ([]models.RuleE
129129
}
130130

131131
func RuleEndpointsSet(ctx context.Context, db database.Querier, ruleID int64, endpointIDs []int64) error {
132-
tx, err := db.BeginTx(ctx, nil)
132+
tx, err := db.BeginTx(ctx, true)
133133
if err != nil {
134134
return err
135135
}

0 commit comments

Comments
 (0)