Skip to content

Commit 2bef3ec

Browse files
committed
fix(shwap/bitswap): Blockstore.GetSize: getting size with no compute
1 parent 08ca0ed commit 2bef3ec

File tree

6 files changed

+71
-17
lines changed

6 files changed

+71
-17
lines changed

share/shwap/p2p/bitswap/block.go

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ type Block interface {
2121
CID() cid.Cid
2222
// Height reports the Height of the Shwap container behind the Block.
2323
Height() uint64
24+
// Size reports expected size of the Block(without serialization overhead).
25+
// Must support getting size when the Block is not populated or empty and strive to
26+
// be low overhead.
27+
Size(context.Context, eds.Accessor) (int, error)
2428

2529
// Populate fills up the Block with the Shwap container getting it out of the EDS
2630
// Accessor.

share/shwap/p2p/bitswap/block_store.go

+30-17
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,28 @@ type Blockstore struct {
2828
Getter AccessorGetter
2929
}
3030

31-
func (b *Blockstore) getBlock(ctx context.Context, cid cid.Cid) (blocks.Block, error) {
31+
func (b *Blockstore) getBlockAndAccessor(ctx context.Context, cid cid.Cid) (Block, eds.AccessorStreamer, error) {
3232
blk, err := EmptyBlock(cid)
3333
if err != nil {
34-
return nil, err
34+
return nil, nil, err
3535
}
3636

3737
acc, err := b.Getter.GetByHeight(ctx, blk.Height())
3838
if errors.Is(err, store.ErrNotFound) {
3939
log.Debugf("no EDS Accessor for height %v found", blk.Height())
40-
return nil, ipld.ErrNotFound{Cid: cid}
40+
return nil, nil, ipld.ErrNotFound{Cid: cid}
4141
}
4242
if err != nil {
43-
return nil, fmt.Errorf("getting EDS Accessor for height %v: %w", blk.Height(), err)
43+
return nil, nil, fmt.Errorf("getting EDS Accessor for height %v: %w", blk.Height(), err)
44+
}
45+
46+
return blk, acc, nil
47+
}
48+
49+
func (b *Blockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) {
50+
blk, acc, err := b.getBlockAndAccessor(ctx, cid)
51+
if err != nil {
52+
return nil, err
4453
}
4554
defer func() {
4655
if err := acc.Close(); err != nil {
@@ -55,24 +64,28 @@ func (b *Blockstore) getBlock(ctx context.Context, cid cid.Cid) (blocks.Block, e
5564
return convertBitswap(blk)
5665
}
5766

58-
func (b *Blockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) {
59-
blk, err := b.getBlock(ctx, cid)
67+
func (b *Blockstore) GetSize(ctx context.Context, cid cid.Cid) (int, error) {
68+
// NOTE: Bitswap prioritizes peers based on their active/pending work and the priority that peers set for requests(work)
69+
// themselves. The prioritization happens on the Get operation of Blockstore not GetSize, while GetSize is expected
70+
// to be as lightweight as possible.
71+
//
72+
// Here is the best case we only open the Accessor and getting its size, avoiding expensive compute to get the size.
73+
blk, acc, err := b.getBlockAndAccessor(ctx, cid)
6074
if err != nil {
61-
return nil, err
75+
return 0, err
6276
}
77+
defer func() {
78+
if err := acc.Close(); err != nil {
79+
log.Warnf("failed to close EDS accessor for height %v: %s", blk.Height(), err)
80+
}
81+
}()
6382

64-
return blk, nil
65-
}
66-
67-
func (b *Blockstore) GetSize(ctx context.Context, cid cid.Cid) (int, error) {
68-
// TODO(@Wondertan): Bitswap checks the size of the data(GetSize) before serving it via Get. This means
69-
// GetSize may do an unnecessary read from disk which we can avoid by either caching on Blockstore level
70-
// or returning constant size(we know at that point that we have requested data)
71-
blk, err := b.Get(ctx, cid)
83+
size, err := blk.Size(ctx, acc)
7284
if err != nil {
73-
return 0, err
85+
return 0, fmt.Errorf("getting block size: %w", err)
7486
}
75-
return len(blk.RawData()), nil
87+
88+
return size, nil
7689
}
7790

7891
func (b *Blockstore) Has(ctx context.Context, cid cid.Cid) (bool, error) {

share/shwap/p2p/bitswap/block_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ func (t *testBlock) Height() uint64 {
9595
return 1
9696
}
9797

98+
func (t *testBlock) Size(_ context.Context, _ eds.Accessor) (int, error) {
99+
return len(t.data), nil
100+
}
101+
98102
func (t *testBlock) Populate(context.Context, eds.Accessor) error {
99103
return nil // noop
100104
}

share/shwap/p2p/bitswap/row_block.go

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/ipfs/go-cid"
88

9+
libshare "github.com/celestiaorg/go-square/v2/share"
910
"github.com/celestiaorg/rsmt2d"
1011

1112
"github.com/celestiaorg/celestia-node/share"
@@ -72,6 +73,12 @@ func (rb *RowBlock) Height() uint64 {
7273
return rb.ID.Height
7374
}
7475

76+
func (rb *RowBlock) Size(ctx context.Context, acc eds.Accessor) (int, error) {
77+
squareSize := acc.Size(ctx)
78+
rowSize := libshare.ShareSize * squareSize / 2
79+
return rowSize, nil
80+
}
81+
7582
func (rb *RowBlock) Marshal() ([]byte, error) {
7683
if rb.Container.IsEmpty() {
7784
return nil, fmt.Errorf("cannot marshal empty RowBlock")

share/shwap/p2p/bitswap/row_namespace_data_block.go

+17
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,23 @@ func (rndb *RowNamespaceDataBlock) Height() uint64 {
7878
return rndb.ID.Height
7979
}
8080

81+
func (rndb *RowNamespaceDataBlock) Size(ctx context.Context, acc eds.Accessor) (int, error) {
82+
// no way to statically learn the size of requested data, so read it out and compute
83+
// TODO(@Wondertan): Consider adding optimized RowNamespaceDataSize method to the Accessor
84+
err := rndb.Populate(ctx, acc)
85+
if err != nil {
86+
return 0, err
87+
}
88+
89+
// TODO(@Wondertan): Avoid converting in favour of getting size just by looking at container
90+
blk, err := convertBitswap(rndb)
91+
if err != nil {
92+
return 0, err
93+
}
94+
95+
return len(blk.RawData()), nil
96+
}
97+
8198
func (rndb *RowNamespaceDataBlock) Marshal() ([]byte, error) {
8299
if rndb.Container.IsEmpty() {
83100
return nil, fmt.Errorf("cannot marshal empty RowNamespaceDataBlock")

share/shwap/p2p/bitswap/sample_block.go

+9
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package bitswap
33
import (
44
"context"
55
"fmt"
6+
"math"
67

78
"github.com/ipfs/go-cid"
89

10+
libshare "github.com/celestiaorg/go-square/v2/share"
11+
912
"github.com/celestiaorg/celestia-node/share"
1013
"github.com/celestiaorg/celestia-node/share/eds"
1114
"github.com/celestiaorg/celestia-node/share/shwap"
@@ -71,6 +74,12 @@ func (sb *SampleBlock) Height() uint64 {
7174
return sb.ID.Height
7275
}
7376

77+
func (sb *SampleBlock) Size(ctx context.Context, acc eds.Accessor) (int, error) {
78+
squareSize := acc.Size(ctx)
79+
sampleSize := libshare.ShareSize + share.AxisRootSize*int(math.Log2(float64(squareSize)))
80+
return sampleSize, nil
81+
}
82+
7483
func (sb *SampleBlock) Marshal() ([]byte, error) {
7584
if sb.Container.IsEmpty() {
7685
return nil, fmt.Errorf("cannot marshal empty SampleBlock")

0 commit comments

Comments
 (0)