Skip to content
This repository was archived by the owner on Jun 20, 2024. It is now read-only.

Commit d8b4d4e

Browse files
committed
feat(graph-gateway): add some (hacky) support for fetching blocks as well as CARs
1 parent ce1ffd4 commit d8b4d4e

File tree

3 files changed

+151
-17
lines changed

3 files changed

+151
-17
lines changed

blockstore_proxy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ package main
33
import (
44
"context"
55
"fmt"
6-
"github.com/ipfs/bifrost-gateway/lib"
76
"io"
87
"log"
98
"math/rand"
109
"net/http"
1110
"net/url"
1211
"time"
1312

13+
"github.com/ipfs/bifrost-gateway/lib"
1414
"github.com/ipfs/go-cid"
1515
blockstore "github.com/ipfs/go-ipfs-blockstore"
1616
"github.com/ipfs/go-libipfs/blocks"

handlers.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,13 @@ func makeGatewayHandler(bs bstore.Blockstore, kuboRPC []string, port int, blockC
7272
return nil, err
7373
}
7474
} else {
75-
gwAPI, err = lib.NewGraphGatewayBackend(bs.(lib.CarFetcher), lib.WithValueStore(routing))
75+
// Sets up an exchange based on the given Block Store
76+
exch, err := newExchange(bs)
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
gwAPI, err = lib.NewGraphGatewayBackend(bs.(lib.CarFetcher), exch, lib.WithValueStore(routing))
7682
if err != nil {
7783
return nil, err
7884
}

lib/graph_gateway.go

Lines changed: 143 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/libp2p/go-libp2p/core/routing"
2525
"github.com/multiformats/go-multicodec"
2626
"github.com/multiformats/go-multihash"
27+
"go.uber.org/multierr"
2728
"io"
2829
"net/http"
2930
)
@@ -70,14 +71,15 @@ func WithBlockstore(bs blockstore.Blockstore) GraphGatewayOption {
7071
type GraphGatewayOption func(gwOptions *gwOptions) error
7172

7273
type GraphGateway struct {
73-
fetcher CarFetcher
74-
routing routing.ValueStore
75-
namesys namesys.NameSystem
76-
bstore blockstore.Blockstore
77-
bsrv blockservice.BlockService
74+
fetcher CarFetcher
75+
blockFetcher exchange.Fetcher
76+
routing routing.ValueStore
77+
namesys namesys.NameSystem
78+
bstore blockstore.Blockstore
79+
bsrv blockservice.BlockService
7880
}
7981

80-
func NewGraphGatewayBackend(f CarFetcher, opts ...GraphGatewayOption) (*GraphGateway, error) {
82+
func NewGraphGatewayBackend(f CarFetcher, blockFetcher exchange.Fetcher, opts ...GraphGatewayOption) (*GraphGateway, error) {
8183
var compiledOptions gwOptions
8284
for _, o := range opts {
8385
if err := o(&compiledOptions); err != nil {
@@ -118,10 +120,11 @@ func NewGraphGatewayBackend(f CarFetcher, opts ...GraphGatewayOption) (*GraphGat
118120
}
119121

120122
return &GraphGateway{
121-
fetcher: f,
122-
routing: vs,
123-
namesys: ns,
124-
bstore: bs,
123+
fetcher: f,
124+
blockFetcher: blockFetcher,
125+
routing: vs,
126+
namesys: ns,
127+
bstore: bs,
125128
}, nil
126129
}
127130

@@ -141,17 +144,21 @@ func (api *GraphGateway) loadRequestIntoMemoryBlockstoreAndBlocksGateway(ctx con
141144
return nil, err
142145
}
143146
bstore := blockstore.NewBlockstore(ds)
144-
exch := newInboundBlockExchange()
145-
146-
doneWithFetcher := make(chan error, 1)
147+
carFetchingExch := newInboundBlockExchange()
148+
doneWithFetcher := make(chan struct{}, 1)
149+
exch := &handoffExchange{
150+
startingExchange: carFetchingExch,
151+
followupExchange: &blockFetcherExchWrapper{api.blockFetcher},
152+
handoffCh: doneWithFetcher,
153+
}
147154

148155
go func() {
149156
defer func() {
150157
if r := recover(); r != nil {
151158
fmt.Println("Recovered fetcher error", r)
152159
}
153160
}()
154-
doneWithFetcher <- api.fetcher.Fetch(ctx, path, func(resource string, reader io.Reader) error {
161+
err := api.fetcher.Fetch(ctx, path, func(resource string, reader io.Reader) error {
155162
cr, err := car.NewCarReader(reader)
156163
if err != nil {
157164
return err
@@ -167,11 +174,16 @@ func (api *GraphGateway) loadRequestIntoMemoryBlockstoreAndBlocksGateway(ctx con
167174
if err := bstore.Put(ctx, blk); err != nil {
168175
return err
169176
}
170-
if err := exch.NotifyNewBlocks(ctx, blk); err != nil {
177+
if err := carFetchingExch.NotifyNewBlocks(ctx, blk); err != nil {
171178
return err
172179
}
173180
}
174181
})
182+
if err != nil {
183+
goLog.Error(err)
184+
}
185+
doneWithFetcher <- struct{}{}
186+
close(doneWithFetcher)
175187
}()
176188

177189
bserv := blockservice.New(bstore, exch)
@@ -352,3 +364,119 @@ func (i *inboundBlockExchange) Close() error {
352364
}
353365

354366
var _ exchange.Interface = (*inboundBlockExchange)(nil)
367+
368+
type handoffExchange struct {
369+
startingExchange, followupExchange exchange.Interface
370+
handoffCh <-chan struct{}
371+
}
372+
373+
func (f *handoffExchange) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) {
374+
blkCh, err := f.startingExchange.GetBlocks(ctx, []cid.Cid{c})
375+
if err != nil {
376+
return nil, err
377+
}
378+
blk, ok := <-blkCh
379+
if ok {
380+
return blk, nil
381+
}
382+
383+
select {
384+
case <-f.handoffCh:
385+
goLog.Infof("needed to use use a backup fetcher for cid %s", c)
386+
return f.followupExchange.GetBlock(ctx, c)
387+
case <-ctx.Done():
388+
return nil, ctx.Err()
389+
}
390+
}
391+
392+
func (f *handoffExchange) GetBlocks(ctx context.Context, cids []cid.Cid) (<-chan blocks.Block, error) {
393+
blkCh, err := f.startingExchange.GetBlocks(ctx, cids)
394+
if err != nil {
395+
return nil, err
396+
}
397+
398+
retCh := make(chan blocks.Block)
399+
400+
go func() {
401+
cs := cid.NewSet()
402+
for cs.Len() < len(cids) {
403+
blk, ok := <-blkCh
404+
if !ok {
405+
break
406+
}
407+
select {
408+
case retCh <- blk:
409+
cs.Add(blk.Cid())
410+
case <-ctx.Done():
411+
}
412+
}
413+
414+
for cs.Len() < len(cids) {
415+
select {
416+
case <-ctx.Done():
417+
return
418+
case <-f.handoffCh:
419+
var newCidArr []cid.Cid
420+
for _, c := range cids {
421+
if !cs.Has(c) {
422+
newCidArr = append(newCidArr, c)
423+
}
424+
}
425+
goLog.Infof("needed to use use a backup fetcher for cids %v", newCidArr)
426+
fch, err := f.followupExchange.GetBlocks(ctx, newCidArr)
427+
if err != nil {
428+
goLog.Error(fmt.Errorf("error getting blocks from followup exchange %w", err))
429+
return
430+
}
431+
for cs.Len() < len(cids) {
432+
select {
433+
case blk := <-fch:
434+
select {
435+
case retCh <- blk:
436+
cs.Add(blk.Cid())
437+
case <-ctx.Done():
438+
return
439+
}
440+
}
441+
}
442+
}
443+
}
444+
}()
445+
return retCh, nil
446+
}
447+
448+
func (f *handoffExchange) NotifyNewBlocks(ctx context.Context, blocks ...blocks.Block) error {
449+
err1 := f.startingExchange.NotifyNewBlocks(ctx, blocks...)
450+
err2 := f.followupExchange.NotifyNewBlocks(ctx, blocks...)
451+
return multierr.Combine(err1, err2)
452+
}
453+
454+
func (f *handoffExchange) Close() error {
455+
err1 := f.startingExchange.Close()
456+
err2 := f.followupExchange.Close()
457+
return multierr.Combine(err1, err2)
458+
}
459+
460+
var _ exchange.Interface = (*handoffExchange)(nil)
461+
462+
type blockFetcherExchWrapper struct {
463+
f exchange.Fetcher
464+
}
465+
466+
func (b *blockFetcherExchWrapper) GetBlock(ctx context.Context, c cid.Cid) (blocks.Block, error) {
467+
return b.f.GetBlock(ctx, c)
468+
}
469+
470+
func (b *blockFetcherExchWrapper) GetBlocks(ctx context.Context, cids []cid.Cid) (<-chan blocks.Block, error) {
471+
return b.f.GetBlocks(ctx, cids)
472+
}
473+
474+
func (b *blockFetcherExchWrapper) NotifyNewBlocks(ctx context.Context, blocks ...blocks.Block) error {
475+
return nil
476+
}
477+
478+
func (b *blockFetcherExchWrapper) Close() error {
479+
return nil
480+
}
481+
482+
var _ exchange.Interface = (*blockFetcherExchWrapper)(nil)

0 commit comments

Comments
 (0)