Skip to content

Commit c668526

Browse files
authored
test ComputeBoostrapDelay, remove post-bootstrap alignment (#1028)
* refactor bootstrap delay computation into a standalone function The computation of the bootstrap delay is now in a standalone function, which allows us to test it in isolation. Signed-off-by: Jakub Sztandera <[email protected]> * add tests for bootstrap delay Signed-off-by: Jakub Sztandera <[email protected]> * fix: don't align bootstrap time after bootstrap Aligning bootstrap time after bootstrap epoch shouldn't be required and as it stands it could delay F3 startup Signed-off-by: Jakub Sztandera <[email protected]> --------- Signed-off-by: Jakub Sztandera <[email protected]>
1 parent 00ff781 commit c668526

File tree

3 files changed

+104
-23
lines changed

3 files changed

+104
-23
lines changed

bootstrap_delay_test.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package f3
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/filecoin-project/go-f3/ec"
8+
"github.com/filecoin-project/go-f3/internal/clock"
9+
"github.com/filecoin-project/go-f3/manifest"
10+
)
11+
12+
var _ ec.TipSet = (*tipset)(nil)
13+
14+
func TestComputeBootstrapDelay(t *testing.T) {
15+
period := 30 * time.Second
16+
bootstrapEpoch := 1000
17+
m := manifest.Manifest{
18+
BootstrapEpoch: int64(bootstrapEpoch),
19+
EC: manifest.EcConfig{
20+
Period: period,
21+
},
22+
}
23+
24+
clock := clock.NewMock()
25+
genesis := time.Date(2020, time.January, 12, 01, 01, 01, 00, time.UTC)
26+
27+
tt := []struct {
28+
name string
29+
time time.Time
30+
ts tipset
31+
want time.Duration
32+
}{
33+
{
34+
name: "in sync - right before bootstrap",
35+
time: genesis.Add(time.Duration(bootstrapEpoch-1) * period),
36+
ts: tipset{genesis: genesis, epoch: int64(bootstrapEpoch - 1), period: period},
37+
want: period,
38+
},
39+
{
40+
name: "in sync - right at bootstrap",
41+
time: genesis.Add(time.Duration(bootstrapEpoch) * period),
42+
ts: tipset{genesis: genesis, epoch: int64(bootstrapEpoch), period: period},
43+
want: 0,
44+
},
45+
{
46+
name: "in sync - right after bootstrap",
47+
time: genesis.Add(time.Duration(bootstrapEpoch+1) * period),
48+
ts: tipset{genesis: genesis, epoch: int64(bootstrapEpoch + 1), period: period},
49+
want: 0,
50+
},
51+
{
52+
name: "in sync - right before bootstrap (offset)",
53+
time: genesis.Add(time.Duration(bootstrapEpoch-1)*period + 15*time.Second),
54+
ts: tipset{genesis: genesis, epoch: int64(bootstrapEpoch - 1), period: period},
55+
want: 15 * time.Second,
56+
},
57+
{
58+
name: "in sync - right after bootstrap (offset)",
59+
time: genesis.Add(time.Duration(bootstrapEpoch)*period + 1*time.Second),
60+
ts: tipset{genesis: genesis, epoch: int64(bootstrapEpoch), period: period},
61+
want: 0 * time.Second,
62+
},
63+
{
64+
name: "out of sync - way after bootstrap",
65+
time: genesis.Add(time.Duration(bootstrapEpoch+100)*period + 1*time.Second),
66+
ts: tipset{genesis: genesis, epoch: int64(bootstrapEpoch - 100), period: period},
67+
want: 0 * time.Second,
68+
},
69+
{
70+
name: "out of sync - way before bootstrap",
71+
time: genesis.Add(time.Duration(bootstrapEpoch-30)*period + 1*time.Second),
72+
ts: tipset{genesis: genesis, epoch: int64(bootstrapEpoch - 100), period: period},
73+
want: 30*period - 1*time.Second,
74+
},
75+
}
76+
77+
for _, tc := range tt {
78+
t.Run(tc.name, func(t *testing.T) {
79+
clock.Set(tc.time)
80+
got := computeBootstrapDelay(&tc.ts, clock, m)
81+
if got != tc.want {
82+
t.Errorf("computeBootstrapDelay(%s, %v, %v) = %v, want %v", &tc.ts, tc.time, period, got, tc.want)
83+
}
84+
})
85+
}
86+
}

f3.go

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -136,34 +136,26 @@ func (m *F3) GetPowerTableByInstance(ctx context.Context, instance uint64) (gpbf
136136

137137
// computeBootstrapDelay returns the time at which the F3 instance specified by
138138
// the passed manifest should be started.
139-
func (m *F3) computeBootstrapDelay() (time.Duration, error) {
140-
ts, err := m.ec.GetHead(m.runningCtx)
141-
if err != nil {
142-
return 0, fmt.Errorf("failed to get the EC chain head: %w", err)
143-
}
144-
139+
func computeBootstrapDelay(ts ec.TipSet, clock clock.Clock, mfst manifest.Manifest) time.Duration {
145140
currentEpoch := ts.Epoch()
146-
if currentEpoch >= m.mfst.BootstrapEpoch {
147-
return 0, nil
148-
}
149-
epochDelay := m.mfst.BootstrapEpoch - currentEpoch
150-
start := ts.Timestamp().Add(time.Duration(epochDelay) * m.mfst.EC.Period)
151-
delay := m.clock.Until(start)
152-
// Add additional delay to skip over null epochs. That way we wait the full 900 epochs.
153-
if delay <= 0 {
154-
delay = m.mfst.EC.Period + delay%m.mfst.EC.Period
155-
}
156-
return delay, nil
141+
if currentEpoch >= mfst.BootstrapEpoch {
142+
return 0
143+
}
144+
epochDelay := mfst.BootstrapEpoch - currentEpoch
145+
start := ts.Timestamp().Add(time.Duration(epochDelay) * mfst.EC.Period)
146+
delay := clock.Until(start)
147+
delay = max(delay, 0)
148+
return delay
157149
}
158150

159151
// Start the module, call Stop to exit. Canceling the past context will cancel the request to start
160152
// F3, it won't stop the service after it has started.
161153
func (m *F3) Start(startCtx context.Context) (_err error) {
162-
163-
initialDelay, err := m.computeBootstrapDelay()
154+
ts, err := m.ec.GetHead(m.runningCtx)
164155
if err != nil {
165-
return fmt.Errorf("failed to compute bootstrap delay: %w", err)
156+
return fmt.Errorf("failed to get the EC chain head: %w", err)
166157
}
158+
initialDelay := computeBootstrapDelay(ts, m.clock, m.mfst)
167159

168160
// Try to start immediately if there's no initial delay and pass on any start
169161
// errors directly.
@@ -184,11 +176,13 @@ func (m *F3) Start(startCtx context.Context) (_err error) {
184176
log.Debugw("F3 start disrupted", "cause", m.runningCtx.Err())
185177
return
186178
case startTime := <-startTimer.C:
187-
delay, err := m.computeBootstrapDelay()
179+
ts, err := m.ec.GetHead(m.runningCtx)
188180
if err != nil {
189-
log.Errorw("failed to compute bootstrap delay", "err", err)
181+
log.Errorw("failed to get the EC chain head during startup", "err", err)
190182
return
191183
}
184+
185+
delay := computeBootstrapDelay(ts, m.clock, m.mfst)
192186
if delay > 0 {
193187
log.Infow("waiting for bootstrap epoch", "duration", delay.String())
194188
startTimer.Reset(delay)

tipsettimestamp_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package f3
22

33
import (
4+
"fmt"
45
"testing"
56
"time"
67

@@ -15,7 +16,7 @@ type tipset struct {
1516
}
1617

1718
func (ts *tipset) String() string {
18-
panic("not implemented")
19+
return fmt.Sprintf("epoch %d, timestamp %s", ts.epoch, ts.Timestamp())
1920
}
2021

2122
func (ts *tipset) Key() gpbft.TipSetKey {

0 commit comments

Comments
 (0)