Skip to content

Commit b68646c

Browse files
committed
Use transaction for PutMany and cursor Query
1 parent 130c4a4 commit b68646c

File tree

3 files changed

+66
-27
lines changed

3 files changed

+66
-27
lines changed

Diff for: base/database/storage/sqlite/sqlite.go

+58-16
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,15 @@ func (db *SQLite) GetMeta(key string) (*record.Meta, error) {
9999

100100
// Put stores a record in the database.
101101
func (db *SQLite) Put(r record.Record) (record.Record, error) {
102-
r.Lock()
103-
defer r.Unlock()
102+
return db.putRecord(r, nil)
103+
}
104+
105+
func (db *SQLite) putRecord(r record.Record, tx *bob.Tx) (record.Record, error) {
106+
// Lock record if in a transaction.
107+
if tx != nil {
108+
r.Lock()
109+
defer r.Unlock()
110+
}
104111

105112
// Serialize to JSON.
106113
data, err := r.MarshalDataOnly(r, dsd.JSON)
@@ -127,12 +134,19 @@ func (db *SQLite) Put(r record.Record) (record.Record, error) {
127134
defer db.lock.Unlock()
128135

129136
// Simulate upsert with custom selection on conflict.
130-
_, err = models.Records.Insert(
137+
dbQuery := models.Records.Insert(
131138
&setter,
132139
im.OnConflict("key").DoUpdate(
133140
im.SetExcluded("format", "value", "created", "modified", "expires", "deleted", "secret", "crownjewel"),
134141
),
135-
).Exec(db.ctx, db.bob)
142+
)
143+
144+
// Execute in transaction or directly.
145+
if tx != nil {
146+
_, err = dbQuery.Exec(db.ctx, tx)
147+
} else {
148+
_, err = dbQuery.Exec(db.ctx, db.bob)
149+
}
136150
if err != nil {
137151
return nil, err
138152
}
@@ -150,16 +164,39 @@ func (db *SQLite) PutMany(shadowDelete bool) (chan<- record.Record, <-chan error
150164
batch := make(chan record.Record, 100)
151165
errs := make(chan error, 1)
152166

167+
tx, err := db.bob.BeginTx(db.ctx, nil)
168+
if err != nil {
169+
errs <- err
170+
return batch, errs
171+
}
172+
153173
// start handler
154174
go func() {
155-
for r := range batch {
156-
_, err := db.Put(r)
157-
if err != nil {
158-
errs <- err
159-
return
175+
// Read all put records.
176+
writeBatch:
177+
for {
178+
select {
179+
case r := <-batch:
180+
if r != nil {
181+
// Write record.
182+
_, err := db.putRecord(r, &tx)
183+
if err != nil {
184+
errs <- err
185+
break writeBatch
186+
}
187+
} else {
188+
// Finalize transcation.
189+
errs <- tx.Commit()
190+
return
191+
}
192+
193+
case <-db.ctx.Done():
194+
break writeBatch
160195
}
161196
}
162-
errs <- nil
197+
198+
// Rollback transaction.
199+
errs <- tx.Rollback()
163200
}()
164201

165202
return batch, errs
@@ -199,20 +236,25 @@ func (db *SQLite) queryExecutor(queryIter *iterator.Iterator, q *query.Query, lo
199236
recordQuery = models.Records.View.Query()
200237
}
201238

202-
// Get all records from query.
203-
// TODO: This will load all records into memory. While this is efficient and
204-
// will not block others from using the datbase, this might be quite a strain
205-
// on the system memory. Monitor and see if this is an issue.
239+
// Get cursor to go over all records in the query.
206240
db.lock.RLock()
207-
records, err := models.RecordsQuery.All(recordQuery, db.ctx, db.bob)
241+
cursor, err := models.RecordsQuery.Cursor(recordQuery, db.ctx, db.bob)
208242
db.lock.RUnlock()
209243
if err != nil {
210244
queryIter.Finish(err)
211245
return
212246
}
247+
defer cursor.Close()
213248

214249
recordsLoop:
215-
for _, r := range records {
250+
for cursor.Next() {
251+
// Get next record
252+
r, cErr := cursor.Get()
253+
if cErr != nil {
254+
err = fmt.Errorf("cursor error: %w", cErr)
255+
break recordsLoop
256+
}
257+
216258
// Check if key matches.
217259
if !q.MatchesKey(r.Key) {
218260
continue recordsLoop

Diff for: base/database/storage/sqlite/sqlite_test.go

+7-11
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,13 @@ func TestSQLite(t *testing.T) {
115115
qZ := &TestRecord{}
116116
qZ.SetKey("test:z")
117117
qZ.CreateMeta()
118-
// put
119-
_, err = db.Put(qA)
120-
if err == nil {
121-
_, err = db.Put(qB)
122-
}
123-
if err == nil {
124-
_, err = db.Put(qC)
125-
}
126-
if err == nil {
127-
_, err = db.Put(qZ)
128-
}
118+
put, errs := db.PutMany(false)
119+
put <- qA
120+
put <- qB
121+
put <- qC
122+
put <- qZ
123+
close(put)
124+
err = <-errs
129125
if err != nil {
130126
t.Fatal(err)
131127
}

Diff for: service/core/base/databases.go

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/safing/portmaster/base/database"
77
_ "github.com/safing/portmaster/base/database/storage/bbolt"
8+
_ "github.com/safing/portmaster/base/database/storage/sqlite"
89
"github.com/safing/portmaster/base/dataroot"
910
"github.com/safing/portmaster/base/utils"
1011
)

0 commit comments

Comments
 (0)