Skip to content

Commit c0099f2

Browse files
committed
integrate Select
1 parent 462911d commit c0099f2

File tree

2 files changed

+18
-29
lines changed

2 files changed

+18
-29
lines changed

fibre/client_download.go

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"math/rand/v2"
8-
"slices"
97
"sync/atomic"
108

119
"github.com/celestiaorg/celestia-app/v6/fibre/validator"
@@ -26,14 +24,14 @@ var (
2624

2725
// Download retrieves and reconstructs [Blob] by [Commitment] from the [Server]s.
2826
//
29-
// The algorithm is simple: take a randomized 2/3 subset of the latest validators (instead of the complete set)
30-
// and request blob shards from them. In the happy path, this is sufficient to reconstruct blobs.
31-
// For the unhappy path, we add recovery requests to additional validators if any of the initial requests fail,
32-
// so that in the worst case, we cover all validators.
27+
// The algorithm selects minimal required number of validators,
28+
// shuffled by stake weight for load balancing and requests them for shards.
29+
// If any of the requests fails, more validators are requested until enough shards are retrieved or
30+
// the maximum number of validators is reached. In the happy case, the operation succeeds in a single roundtrip.
3331
//
3432
// Errors:
3533
// - [ErrNotFound]: no shard was retrieved for the blob
36-
// - [ErrNotEnoughShards]: not enough rows were retrieved to reconstruct the original data
34+
// - [ErrNotEnoughShards]: not enough shards were retrieved to reconstruct the original data
3735
// - [ErrInvalidCommitment]: the commitment doesn't match the reconstructed blob
3836
func (c *Client) Download(ctx context.Context, commitment Commitment) (*Blob, error) {
3937
if c.closed.Load() {
@@ -192,19 +190,9 @@ func (c *Client) downloadBlob(
192190
downloadedCh = make(chan struct{}) // closes when downloadTarget amount of responses complete
193191
)
194192

195-
var (
196-
// limit to download minimum required amount of shards in the best case, instead of everything
197-
downloadTarget = max(valSet.Size()*int(c.cfg.UploadTargetSignaturesCount.Numerator)/
198-
int(c.cfg.UploadTargetSignaturesCount.Denominator), 1)
199-
downloadLimitCh = make(chan struct{}, downloadTarget)
200-
)
201-
202-
// shuffle validators for random prioritization
203-
// TODO(@Wondertan): Order validators based on their performance over time using EWMA
204-
validators := slices.Clone(valSet.Validators)
205-
rand.Shuffle(len(validators), func(i, j int) {
206-
validators[i], validators[j] = validators[j], validators[i]
207-
})
193+
// select validators shuffled by stake for load balancing
194+
validators, downloadTarget := valSet.Select(blob.Config().OriginalRows, c.cfg.MinRowsPerValidator, c.cfg.LivenessThreshold)
195+
downloadLimitCh := make(chan struct{}, downloadTarget)
208196

209197
loop:
210198
for _, val := range validators {

fibre/client_download_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func testClientDownloadClosedClient(t *testing.T) {
9797

9898
func testClientDownloadExactTargetCount(t *testing.T) {
9999
// test that we download from exactly downloadTarget validators (no more)
100-
// with 10 validators and 2/3 target, downloadTarget = 6
100+
// with 10 equal-stake validators and livenessThreshold=1/3, downloadTarget = 4
101101
const numValidators = 10
102102

103103
blob := makeTestBlobV0(t, 256*1024)
@@ -112,13 +112,14 @@ func testClientDownloadExactTargetCount(t *testing.T) {
112112
require.NoError(t, err)
113113
require.Equal(t, blob.Data(), downloaded.Data())
114114

115-
// downloadTarget = 10 * 2/3 = 6
116-
// we should have exactly 6 successful downloads (no over-fetching in happy path)
117-
require.Equal(t, int64(6), counter.Load(), "should download from exactly downloadTarget validators")
115+
// Select returns minRequired = 4 for 10 equal-stake validators with livenessThreshold=1/3
116+
// we should have exactly 4 successful downloads (no over-fetching in happy path)
117+
require.Equal(t, int64(4), counter.Load(), "should download from exactly downloadTarget validators")
118118
}
119119

120120
func testClientDownloadFaultTolerance(t *testing.T) {
121-
// test failure tolerance boundaries with 10 validators and 2/3 target
121+
// test failure tolerance boundaries with 10 validators
122+
// Select uses livenessThreshold (1/3), so downloadTarget = 4 for 10 equal-stake validators
122123
const numValidators = 10
123124
blob := makeTestBlobV0(t, 256*1024)
124125

@@ -127,10 +128,10 @@ func testClientDownloadFaultTolerance(t *testing.T) {
127128
expectErr error
128129
}{
129130
{10, fibre.ErrNotFound},
130-
{6, fibre.ErrNotEnoughShards}, // 4 successes, need 6
131-
{5, fibre.ErrNotEnoughShards}, // 5 successes, need 6
132-
{4, nil}, // 6 successes, exactly enough
133-
{3, nil}, // 7 successes, more than enough
131+
{7, fibre.ErrNotEnoughShards}, // 3 successes, need 4
132+
{6, nil}, // 4 successes, exactly enough
133+
{5, nil}, // 5 successes, more than enough
134+
{4, nil}, // 6 successes, more than enough
134135
}
135136

136137
for _, tc := range tests {

0 commit comments

Comments
 (0)