Skip to content

Commit b0bf458

Browse files
committed
fix(f3): throttle delayed bootstrap retries
When wall-clock time has passed bootstrap but the observed EC head is still behind, retry bootstrap less frequently while the head remains far from the bootstrap epoch. Fixes #1079 Signed-off-by: Jakub Sztandera <oss@kubuxu.com>
1 parent 1f93d0d commit b0bf458

2 files changed

Lines changed: 26 additions & 10 deletions

File tree

bootstrap_delay_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,13 @@ func TestComputeBootstrapDelay(t *testing.T) {
6464
name: "out of sync - way after bootstrap",
6565
time: genesis.Add(time.Duration(bootstrapEpoch+100)*period + 1*time.Second),
6666
ts: tipset{genesis: genesis, epoch: int64(bootstrapEpoch - 100), period: period},
67-
want: 1 * time.Nanosecond, // we don't start immediately as the tipset we need is not available yet
67+
want: 5 * period, // poll twice over the 10-epoch short-range window
68+
},
69+
{
70+
name: "out of sync - right after bootstrap",
71+
time: genesis.Add(time.Duration(bootstrapEpoch)*period + 1*time.Second),
72+
ts: tipset{genesis: genesis, epoch: int64(bootstrapEpoch - 1), period: period},
73+
want: time.Nanosecond, // don't start immediately as the tipset we need is not available yet
6874
},
6975
{
7076
name: "out of sync - way before bootstrap",

f3.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,23 +144,33 @@ func (m *F3) GetPowerTableByInstance(ctx context.Context, instance uint64) (gpbf
144144
return cs.GetPowerTable(ctx, instance)
145145
}
146146

147-
// computeBootstrapDelay returns the time at which the F3 instance specified by
148-
// the passed manifest should be started.
149-
// It will return 0 if the manifest bootstrap epoch is greater than the current epoch.
150-
// It will also return 1ns if the manifest bootstrap epoch is equal to the current epoch but by
151-
// the time calculation, we should have already received the bootstrap tipset.
147+
// computeBootstrapDelay returns how long F3 should wait before attempting to start.
148+
// It returns 0 once the observed chain head has reached the manifest bootstrap epoch.
149+
// If the bootstrap epoch has not been observed but is estimated to have already
150+
// happened, it returns a retry delay based on how far the observed head is from bootstrap.
152151
func computeBootstrapDelay(ts ec.TipSet, clock clock.Clock, mfst manifest.Manifest) time.Duration {
153152
currentEpoch := ts.Epoch()
154153
if currentEpoch >= mfst.BootstrapEpoch {
155154
return 0
156155
}
156+
157+
// We did not observe the bootstrap epoch yet.
157158
epochDelay := mfst.BootstrapEpoch - currentEpoch
158159
start := ts.Timestamp().Add(time.Duration(epochDelay) * mfst.EC.Period)
159160
delay := clock.Until(start)
160-
// ensure that we don't start immediately
161-
// to trigger waiting for the bootstrap tipset to exist
162-
delay = max(delay, 1*time.Nanosecond)
163-
return delay
161+
if delay > 0 {
162+
return delay
163+
}
164+
165+
const shortRange = 10
166+
// But by our estimate the bootstrap epoch should have already happened.
167+
if mfst.BootstrapEpoch-currentEpoch < shortRange {
168+
// Within the shortRange, start checking more frequently. Start applies
169+
// a minimum 20ms retry delay after the initial timer fires.
170+
return time.Nanosecond
171+
}
172+
// Otherwise, poll twice over the short-range window.
173+
return shortRange * mfst.EC.Period / 2
164174
}
165175

166176
// Start the module, call Stop to exit. Canceling the past context will cancel the request to start

0 commit comments

Comments
 (0)