@@ -24,6 +24,7 @@ import (
24
24
"github.com/libp2p/go-libp2p/core/routing"
25
25
"github.com/multiformats/go-multicodec"
26
26
"github.com/multiformats/go-multihash"
27
+ "go.uber.org/multierr"
27
28
"io"
28
29
"net/http"
29
30
)
@@ -70,14 +71,15 @@ func WithBlockstore(bs blockstore.Blockstore) GraphGatewayOption {
70
71
type GraphGatewayOption func (gwOptions * gwOptions ) error
71
72
72
73
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
78
80
}
79
81
80
- func NewGraphGatewayBackend (f CarFetcher , opts ... GraphGatewayOption ) (* GraphGateway , error ) {
82
+ func NewGraphGatewayBackend (f CarFetcher , blockFetcher exchange. Fetcher , opts ... GraphGatewayOption ) (* GraphGateway , error ) {
81
83
var compiledOptions gwOptions
82
84
for _ , o := range opts {
83
85
if err := o (& compiledOptions ); err != nil {
@@ -118,10 +120,11 @@ func NewGraphGatewayBackend(f CarFetcher, opts ...GraphGatewayOption) (*GraphGat
118
120
}
119
121
120
122
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 ,
125
128
}, nil
126
129
}
127
130
@@ -141,17 +144,21 @@ func (api *GraphGateway) loadRequestIntoMemoryBlockstoreAndBlocksGateway(ctx con
141
144
return nil , err
142
145
}
143
146
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
+ }
147
154
148
155
go func () {
149
156
defer func () {
150
157
if r := recover (); r != nil {
151
158
fmt .Println ("Recovered fetcher error" , r )
152
159
}
153
160
}()
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 {
155
162
cr , err := car .NewCarReader (reader )
156
163
if err != nil {
157
164
return err
@@ -167,11 +174,16 @@ func (api *GraphGateway) loadRequestIntoMemoryBlockstoreAndBlocksGateway(ctx con
167
174
if err := bstore .Put (ctx , blk ); err != nil {
168
175
return err
169
176
}
170
- if err := exch .NotifyNewBlocks (ctx , blk ); err != nil {
177
+ if err := carFetchingExch .NotifyNewBlocks (ctx , blk ); err != nil {
171
178
return err
172
179
}
173
180
}
174
181
})
182
+ if err != nil {
183
+ goLog .Error (err )
184
+ }
185
+ doneWithFetcher <- struct {}{}
186
+ close (doneWithFetcher )
175
187
}()
176
188
177
189
bserv := blockservice .New (bstore , exch )
@@ -352,3 +364,119 @@ func (i *inboundBlockExchange) Close() error {
352
364
}
353
365
354
366
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