Skip to content

Commit c8752c5

Browse files
committed
sync2: eliminate unneeded DB accesses when peers are in sync (#6576)
## Motivation `FPTree`, and thus `DBSet` hold enough information in memory for peers to decide whether they are in sync or not. In fully synced state, the peers only exchange probe requests that cover the whole set, and we only need to know fingerprint and count of the set. For that, no database queries are necessary.
1 parent 865d114 commit c8752c5

File tree

16 files changed

+548
-347
lines changed

16 files changed

+548
-347
lines changed

sync2/dbset/dbset.go

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dbset
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"maps"
78
"sync"
@@ -118,16 +119,12 @@ func (d *DBSet) Receive(k rangesync.KeyBytes) error {
118119
return nil
119120
}
120121

121-
func (d *DBSet) firstItem() (rangesync.KeyBytes, error) {
122-
if err := d.EnsureLoaded(); err != nil {
123-
return nil, err
124-
}
125-
return d.ft.All().First()
126-
}
127-
128-
// GetRangeInfo returns information about the range of items in the DBSet.
122+
// RangeInfo returns information about the range of items in the DBSet.
129123
// Implements rangesync.OrderedSet.
130-
func (d *DBSet) GetRangeInfo(x, y rangesync.KeyBytes) (rangesync.RangeInfo, error) {
124+
func (d *DBSet) RangeInfo(x, y rangesync.KeyBytes) (rangesync.RangeInfo, error) {
125+
if x == nil || y == nil {
126+
return rangesync.RangeInfo{}, errors.New("bad range")
127+
}
131128
if err := d.EnsureLoaded(); err != nil {
132129
return rangesync.RangeInfo{}, err
133130
}
@@ -136,17 +133,6 @@ func (d *DBSet) GetRangeInfo(x, y rangesync.KeyBytes) (rangesync.RangeInfo, erro
136133
Items: rangesync.EmptySeqResult(),
137134
}, nil
138135
}
139-
if x == nil || y == nil {
140-
if x != nil || y != nil {
141-
panic("BUG: GetRangeInfo called with one of x/y nil but not both")
142-
}
143-
var err error
144-
x, err = d.firstItem()
145-
if err != nil {
146-
return rangesync.RangeInfo{}, fmt.Errorf("getting first item: %w", err)
147-
}
148-
y = x
149-
}
150136
fpr, err := d.ft.FingerprintInterval(x, y, -1)
151137
if err != nil {
152138
return rangesync.RangeInfo{}, err
@@ -192,6 +178,20 @@ func (d *DBSet) SplitRange(x, y rangesync.KeyBytes, count int) (rangesync.SplitI
192178
}, nil
193179
}
194180

181+
// SetInfo returns RangeInfo for the whole DBSet.
182+
// Implements rangesync.OrderedSet.
183+
func (d *DBSet) SetInfo() (rangesync.RangeInfo, error) {
184+
if err := d.EnsureLoaded(); err != nil {
185+
return rangesync.RangeInfo{}, err
186+
}
187+
fpr := d.ft.FingerprintAll()
188+
return rangesync.RangeInfo{
189+
Fingerprint: fpr.FP,
190+
Count: int(fpr.Count),
191+
Items: fpr.Items,
192+
}, nil
193+
}
194+
195195
// Items returns a sequence of all items in the DBSet.
196196
// Implements rangesync.OrderedSet.
197197
func (d *DBSet) Items() rangesync.SeqResult {

sync2/dbset/dbset_test.go

Lines changed: 123 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,21 @@ func TestDBSet_Empty(t *testing.T) {
4444
requireEmpty(t, s.Items())
4545
requireEmpty(t, s.Received())
4646

47-
info, err := s.GetRangeInfo(nil, nil)
47+
info, err := s.SetInfo()
4848
require.NoError(t, err)
4949
require.Equal(t, 0, info.Count)
5050
require.Equal(t, "000000000000000000000000", info.Fingerprint.String())
5151
requireEmpty(t, info.Items)
5252

53-
info, err = s.GetRangeInfo(
53+
info, err = s.RangeInfo(
5454
rangesync.MustParseHexKeyBytes("0000000000000000000000000000000000000000000000000000000000000000"),
5555
rangesync.MustParseHexKeyBytes("0000000000000000000000000000000000000000000000000000000000000000"))
5656
require.NoError(t, err)
5757
require.Equal(t, 0, info.Count)
5858
require.Equal(t, "000000000000000000000000", info.Fingerprint.String())
5959
requireEmpty(t, info.Items)
6060

61-
info, err = s.GetRangeInfo(
61+
info, err = s.RangeInfo(
6262
rangesync.MustParseHexKeyBytes("0000000000000000000000000000000000000000000000000000000000000000"),
6363
rangesync.MustParseHexKeyBytes("9999000000000000000000000000000000000000000000000000000000000000"))
6464
require.NoError(t, err)
@@ -69,7 +69,7 @@ func TestDBSet_Empty(t *testing.T) {
6969

7070
func TestDBSet(t *testing.T) {
7171
ids := []rangesync.KeyBytes{
72-
rangesync.MustParseHexKeyBytes("0000000000000000000000000000000000000000000000000000000000000000"),
72+
rangesync.MustParseHexKeyBytes("1111111111111111111111111111111111111111111111111111111111111111"),
7373
rangesync.MustParseHexKeyBytes("123456789abcdef0000000000000000000000000000000000000000000000000"),
7474
rangesync.MustParseHexKeyBytes("5555555555555555555555555555555555555555555555555555555555555555"),
7575
rangesync.MustParseHexKeyBytes("8888888888888888888888888888888888888888888888888888888888888888"),
@@ -81,94 +81,126 @@ func TestDBSet(t *testing.T) {
8181
IDColumn: "id",
8282
}
8383
s := dbset.NewDBSet(db, st, testKeyLen, testDepth)
84-
require.Equal(t, "0000000000000000000000000000000000000000000000000000000000000000",
84+
require.Equal(t, "1111111111111111111111111111111111111111111111111111111111111111",
8585
firstKey(t, s.Items()).String())
8686
has, err := s.Has(
8787
rangesync.MustParseHexKeyBytes("9876000000000000000000000000000000000000000000000000000000000000"))
8888
require.NoError(t, err)
8989
require.False(t, has)
9090

91-
for _, tc := range []struct {
92-
xIdx, yIdx int
93-
limit int
94-
fp string
95-
count int
96-
startIdx, endIdx int
97-
}{
98-
{
99-
xIdx: 1,
100-
yIdx: 1,
101-
limit: -1,
102-
fp: "642464b773377bbddddddddd",
103-
count: 5,
104-
startIdx: 1,
105-
endIdx: 1,
106-
},
107-
{
108-
xIdx: -1,
109-
yIdx: -1,
110-
limit: -1,
111-
fp: "642464b773377bbddddddddd",
112-
count: 5,
113-
startIdx: 0,
114-
endIdx: 0,
115-
},
116-
{
117-
xIdx: 0,
118-
yIdx: 3,
119-
limit: -1,
120-
fp: "4761032dcfe98ba555555555",
121-
count: 3,
122-
startIdx: 0,
123-
endIdx: 3,
124-
},
125-
{
126-
xIdx: 2,
127-
yIdx: 0,
128-
limit: -1,
129-
fp: "761032cfe98ba54ddddddddd",
130-
count: 3,
131-
startIdx: 2,
132-
endIdx: 0,
133-
},
134-
{
135-
xIdx: 3,
136-
yIdx: 2,
137-
limit: 3,
138-
fp: "2345679abcdef01888888888",
139-
count: 3,
140-
startIdx: 3,
141-
endIdx: 1,
142-
},
143-
} {
144-
name := fmt.Sprintf("%d-%d_%d", tc.xIdx, tc.yIdx, tc.limit)
145-
t.Run(name, func(t *testing.T) {
146-
var x, y rangesync.KeyBytes
147-
if tc.xIdx >= 0 {
91+
t.Run("RangeInfo", func(t *testing.T) {
92+
for _, tc := range []struct {
93+
xIdx, yIdx int
94+
fp string
95+
count int
96+
startIdx, endIdx int
97+
}{
98+
{
99+
xIdx: 1,
100+
yIdx: 1,
101+
fp: "753575a662266aaccccccccc",
102+
count: 5,
103+
startIdx: 1,
104+
endIdx: 1,
105+
},
106+
{
107+
xIdx: 0,
108+
yIdx: 3,
109+
fp: "5670123cdef89ab444444444",
110+
count: 3,
111+
startIdx: 0,
112+
endIdx: 3,
113+
},
114+
{
115+
xIdx: 2,
116+
yIdx: 0,
117+
fp: "761032cfe98ba54ddddddddd",
118+
count: 3,
119+
startIdx: 2,
120+
endIdx: 0,
121+
},
122+
} {
123+
name := fmt.Sprintf("%d-%d", tc.xIdx, tc.yIdx)
124+
t.Run(name, func(t *testing.T) {
125+
var x, y rangesync.KeyBytes
148126
x = ids[tc.xIdx]
149127
y = ids[tc.yIdx]
150-
}
151-
t.Logf("x %v y %v limit %d", x, y, tc.limit)
152-
var info rangesync.RangeInfo
153-
if tc.limit < 0 {
154-
info, err = s.GetRangeInfo(x, y)
128+
t.Logf("x %v y %v", x, y)
129+
var info rangesync.RangeInfo
130+
info, err = s.RangeInfo(x, y)
131+
require.NoError(t, err)
132+
require.Equal(t, tc.count, info.Count)
133+
require.Equal(t, tc.fp, info.Fingerprint.String())
134+
require.Equal(t, ids[tc.startIdx], firstKey(t, info.Items))
135+
has, err := s.Has(ids[tc.startIdx])
155136
require.NoError(t, err)
156-
} else {
137+
require.True(t, has)
138+
has, err = s.Has(ids[tc.endIdx])
139+
require.NoError(t, err)
140+
require.True(t, has)
141+
})
142+
}
143+
})
144+
145+
t.Run("SplitRange", func(t *testing.T) {
146+
for _, tc := range []struct {
147+
xIdx, yIdx int
148+
limit int
149+
fp string
150+
count int
151+
startIdx, endIdx int
152+
}{
153+
{
154+
xIdx: 3,
155+
yIdx: 2,
156+
limit: 3,
157+
fp: "3254768badcfe10999999999",
158+
count: 3,
159+
startIdx: 3,
160+
endIdx: 1,
161+
},
162+
{
163+
xIdx: 3,
164+
yIdx: 3,
165+
limit: 2,
166+
fp: "2345679abcdef01888888888",
167+
count: 2,
168+
startIdx: 3,
169+
endIdx: 3,
170+
},
171+
} {
172+
name := fmt.Sprintf("%d-%d_%d", tc.xIdx, tc.yIdx, tc.limit)
173+
t.Run(name, func(t *testing.T) {
174+
var x, y rangesync.KeyBytes
175+
x = ids[tc.xIdx]
176+
y = ids[tc.yIdx]
177+
t.Logf("x %v y %v limit %d", x, y, tc.limit)
178+
var info rangesync.RangeInfo
157179
sr, err := s.SplitRange(x, y, tc.limit)
158180
require.NoError(t, err)
159181
info = sr.Parts[0]
160-
}
161-
require.Equal(t, tc.count, info.Count)
162-
require.Equal(t, tc.fp, info.Fingerprint.String())
163-
require.Equal(t, ids[tc.startIdx], firstKey(t, info.Items))
164-
has, err := s.Has(ids[tc.startIdx])
165-
require.NoError(t, err)
166-
require.True(t, has)
167-
has, err = s.Has(ids[tc.endIdx])
168-
require.NoError(t, err)
169-
require.True(t, has)
170-
})
171-
}
182+
require.Equal(t, tc.count, info.Count)
183+
require.Equal(t, tc.fp, info.Fingerprint.String())
184+
require.Equal(t, ids[tc.startIdx], firstKey(t, info.Items))
185+
has, err := s.Has(ids[tc.startIdx])
186+
require.NoError(t, err)
187+
require.True(t, has)
188+
has, err = s.Has(ids[tc.endIdx])
189+
require.NoError(t, err)
190+
require.True(t, has)
191+
})
192+
}
193+
})
194+
195+
t.Run("SetInfo", func(t *testing.T) {
196+
info, err := s.SetInfo()
197+
require.NoError(t, err)
198+
require.Equal(t, 5, info.Count)
199+
require.Equal(t, "753575a662266aaccccccccc", info.Fingerprint.String())
200+
items, err := info.Items.Collect()
201+
require.NoError(t, err)
202+
require.Equal(t, ids, items)
203+
})
172204
}
173205

174206
func TestDBSet_Receive(t *testing.T) {
@@ -195,7 +227,7 @@ func TestDBSet_Receive(t *testing.T) {
195227
require.NoError(t, err)
196228
require.Equal(t, []rangesync.KeyBytes{newID}, items)
197229

198-
info, err := s.GetRangeInfo(ids[2], ids[0])
230+
info, err := s.RangeInfo(ids[2], ids[0])
199231
require.NoError(t, err)
200232
require.Equal(t, 2, info.Count)
201233
require.Equal(t, "dddddddddddddddddddddddd", info.Fingerprint.String())
@@ -218,7 +250,7 @@ func TestDBSet_Copy(t *testing.T) {
218250
firstKey(t, s.Items()).String())
219251

220252
require.NoError(t, s.WithCopy(context.Background(), func(copy rangesync.OrderedSet) error {
221-
info, err := copy.GetRangeInfo(ids[2], ids[0])
253+
info, err := copy.RangeInfo(ids[2], ids[0])
222254
require.NoError(t, err)
223255
require.Equal(t, 2, info.Count)
224256
require.Equal(t, "dddddddddddddddddddddddd", info.Fingerprint.String())
@@ -228,15 +260,15 @@ func TestDBSet_Copy(t *testing.T) {
228260
"abcdef1234567890000000000000000000000000000000000000000000000000")
229261
require.NoError(t, copy.Receive(newID))
230262

231-
info, err = s.GetRangeInfo(ids[2], ids[0])
263+
info, err = s.RangeInfo(ids[2], ids[0])
232264
require.NoError(t, err)
233265
require.Equal(t, 2, info.Count)
234266
require.Equal(t, "dddddddddddddddddddddddd", info.Fingerprint.String())
235267
require.Equal(t, ids[2], firstKey(t, info.Items))
236268

237269
requireEmpty(t, s.Received())
238270

239-
info, err = s.GetRangeInfo(ids[2], ids[0])
271+
info, err = s.RangeInfo(ids[2], ids[0])
240272
require.NoError(t, err)
241273
require.Equal(t, 2, info.Count)
242274
require.Equal(t, "dddddddddddddddddddddddd", info.Fingerprint.String())
@@ -267,13 +299,13 @@ func TestDBItemStore_Advance(t *testing.T) {
267299
require.NoError(t, os.EnsureLoaded())
268300

269301
require.NoError(t, os.WithCopy(context.Background(), func(copy rangesync.OrderedSet) error {
270-
info, err := os.GetRangeInfo(ids[0], ids[0])
302+
info, err := os.RangeInfo(ids[0], ids[0])
271303
require.NoError(t, err)
272304
require.Equal(t, 4, info.Count)
273305
require.Equal(t, "cfe98ba54761032ddddddddd", info.Fingerprint.String())
274306
require.Equal(t, ids[0], firstKey(t, info.Items))
275307

276-
info, err = copy.GetRangeInfo(ids[0], ids[0])
308+
info, err = copy.RangeInfo(ids[0], ids[0])
277309
require.NoError(t, err)
278310
require.Equal(t, 4, info.Count)
279311
require.Equal(t, "cfe98ba54761032ddddddddd", info.Fingerprint.String())
@@ -284,27 +316,27 @@ func TestDBItemStore_Advance(t *testing.T) {
284316
"abcdef1234567890000000000000000000000000000000000000000000000000"),
285317
})
286318

287-
info, err = os.GetRangeInfo(ids[0], ids[0])
319+
info, err = os.RangeInfo(ids[0], ids[0])
288320
require.NoError(t, err)
289321
require.Equal(t, 4, info.Count)
290322
require.Equal(t, "cfe98ba54761032ddddddddd", info.Fingerprint.String())
291323
require.Equal(t, ids[0], firstKey(t, info.Items))
292324

293-
info, err = copy.GetRangeInfo(ids[0], ids[0])
325+
info, err = copy.RangeInfo(ids[0], ids[0])
294326
require.NoError(t, err)
295327
require.Equal(t, 4, info.Count)
296328
require.Equal(t, "cfe98ba54761032ddddddddd", info.Fingerprint.String())
297329
require.Equal(t, ids[0], firstKey(t, info.Items))
298330

299331
require.NoError(t, os.Advance())
300332

301-
info, err = os.GetRangeInfo(ids[0], ids[0])
333+
info, err = os.RangeInfo(ids[0], ids[0])
302334
require.NoError(t, err)
303335
require.Equal(t, 5, info.Count)
304336
require.Equal(t, "642464b773377bbddddddddd", info.Fingerprint.String())
305337
require.Equal(t, ids[0], firstKey(t, info.Items))
306338

307-
info, err = copy.GetRangeInfo(ids[0], ids[0])
339+
info, err = copy.RangeInfo(ids[0], ids[0])
308340
require.NoError(t, err)
309341
require.Equal(t, 4, info.Count)
310342
require.Equal(t, "cfe98ba54761032ddddddddd", info.Fingerprint.String())
@@ -314,7 +346,7 @@ func TestDBItemStore_Advance(t *testing.T) {
314346
}))
315347

316348
require.NoError(t, os.WithCopy(context.Background(), func(copy rangesync.OrderedSet) error {
317-
info, err := copy.GetRangeInfo(ids[0], ids[0])
349+
info, err := copy.RangeInfo(ids[0], ids[0])
318350
require.NoError(t, err)
319351
require.Equal(t, 5, info.Count)
320352
require.Equal(t, "642464b773377bbddddddddd", info.Fingerprint.String())

0 commit comments

Comments
 (0)