-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwrapper.go
More file actions
144 lines (121 loc) · 2.66 KB
/
wrapper.go
File metadata and controls
144 lines (121 loc) · 2.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package sqlitecx
import (
"context"
"time"
sqlite "github.com/go-llsqlite/crawshaw"
"github.com/go-llsqlite/crawshaw/sqlitex"
)
type Wrapper struct {
db *sqlitex.Pool
closed chan struct{}
housekeepingContext context.Context
stop func()
closeErr error
}
type Builder struct {
name string
flags sqlite.OpenFlags
poolSize int
migrateDatabase func(*sqlite.Conn) error
}
func NewBuilder(name string) *Builder {
return &Builder{
name: name,
flags: sqlite.OpenFlagsDefault,
poolSize: 3,
}
}
func (b *Builder) WithFlags(flags sqlite.OpenFlags) *Builder {
b.flags = flags
return b
}
func (b *Builder) WithPoolSize(poolSize int) *Builder {
b.poolSize = poolSize
return b
}
func (b *Builder) WithMigrateDatabase(migrateDatabase func(*sqlite.Conn) error) *Builder {
b.migrateDatabase = migrateDatabase
return b
}
func (b *Builder) Build(ctx context.Context) (*Wrapper, error) {
db, err := sqlitex.Open(b.name, b.flags, b.poolSize)
if err != nil {
return nil, err
}
if b.migrateDatabase != nil {
conn := db.Get(ctx)
if conn == nil {
db.Close()
return nil, context.Canceled
}
if err := b.migrateDatabase(conn); err != nil {
db.Put(conn)
db.Close()
return nil, err
}
db.Put(conn)
}
hc, stop := context.WithCancel(context.Background())
r := &Wrapper{
db: db,
closed: make(chan struct{}, 1),
housekeepingContext: hc,
stop: stop,
}
go r.run()
return r, nil
}
func (s *Wrapper) Close() error {
s.stop()
<-s.closed
return s.closeErr
}
func (s *Wrapper) housekeeping() {
ctx, cancel := context.WithTimeout(s.housekeepingContext, time.Minute)
defer cancel()
conn := s.db.Get(ctx)
if conn == nil {
return
}
defer s.db.Put(conn)
sqlitex.ExecuteTransient(conn, "PRAGMA optimize", nil)
sqlitex.ExecuteTransient(conn, "VACUUM", nil)
}
func (s *Wrapper) run() {
defer func() {
defer close(s.closed)
s.closeErr = s.db.Close()
}()
for {
select {
case <-s.housekeepingContext.Done():
s.housekeeping()
return
case <-time.After(time.Hour):
s.housekeeping()
}
}
}
func (s *Wrapper) DB() *sqlitex.Pool {
return s.db
}
func (s *Wrapper) RunInTransaction(ctx context.Context, fn func(*sqlite.Conn) error) (err error) {
conn := s.db.Get(ctx)
if conn == nil {
return ctx.Err()
}
defer s.db.Put(conn)
defer sqlitex.Save(conn)(&err)
err = fn(conn)
return err
}
func RunInTransaction[T any](ctx context.Context, db *sqlitex.Pool, fn func(*sqlite.Conn) (T, error)) (result T, err error) {
conn := db.Get(ctx)
if conn == nil {
var zero T
return zero, ctx.Err()
}
defer db.Put(conn)
defer sqlitex.Save(conn)(&err)
return fn(conn)
}