Skip to content

Commit 8c1008c

Browse files
committed
feat: remove peer.ID from asyncloader & loadattempter
1 parent fde604d commit 8c1008c

File tree

10 files changed

+94
-119
lines changed

10 files changed

+94
-119
lines changed

requestmanager/asyncloader/asyncloader.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99

1010
blocks "github.com/ipfs/go-block-format"
1111
"github.com/ipld/go-ipld-prime"
12-
peer "github.com/libp2p/go-libp2p-core/peer"
1312
"go.opentelemetry.io/otel"
1413
"go.opentelemetry.io/otel/attribute"
1514
"go.opentelemetry.io/otel/trace"
@@ -140,9 +139,9 @@ func (al *AsyncLoader) ProcessResponse(
140139

141140
// AsyncLoad asynchronously loads the given link for the given request ID. It returns a channel for data and a channel
142141
// for errors -- only one message will be sent over either.
143-
func (al *AsyncLoader) AsyncLoad(p peer.ID, requestID graphsync.RequestID, link ipld.Link, linkContext ipld.LinkContext) <-chan types.AsyncLoadResult {
142+
func (al *AsyncLoader) AsyncLoad(requestID graphsync.RequestID, link ipld.Link, linkContext ipld.LinkContext) <-chan types.AsyncLoadResult {
144143
resultChan := make(chan types.AsyncLoadResult, 1)
145-
lr := loadattemptqueue.NewLoadRequest(p, requestID, link, linkContext, resultChan)
144+
lr := loadattemptqueue.NewLoadRequest(requestID, link, linkContext, resultChan)
146145
al.stateLk.Lock()
147146
defer al.stateLk.Unlock()
148147
_, retry := al.activeRequests[requestID]
@@ -165,7 +164,7 @@ func (al *AsyncLoader) CompleteResponsesFor(requestID graphsync.RequestID) {
165164
// CleanupRequest indicates the given request is complete on the client side,
166165
// and no further attempts will be made to load links for this request,
167166
// so any cached response data is invalid can be cleaned
168-
func (al *AsyncLoader) CleanupRequest(p peer.ID, requestID graphsync.RequestID) {
167+
func (al *AsyncLoader) CleanupRequest(requestID graphsync.RequestID) {
169168
al.stateLk.Lock()
170169
defer al.stateLk.Unlock()
171170
responseCache := al.responseCache
@@ -194,7 +193,7 @@ func (al *AsyncLoader) getResponseCache(queue string) *responsecache.ResponseCac
194193
func setupAttemptQueue(lsys ipld.LinkSystem) (*responsecache.ResponseCache, *loadattemptqueue.LoadAttemptQueue) {
195194
unverifiedBlockStore := unverifiedblockstore.New(lsys.StorageWriteOpener)
196195
responseCache := responsecache.New(unverifiedBlockStore)
197-
loadAttemptQueue := loadattemptqueue.New(func(p peer.ID, requestID graphsync.RequestID, link ipld.Link, linkContext ipld.LinkContext) types.AsyncLoadResult {
196+
loadAttemptQueue := loadattemptqueue.New(func(requestID graphsync.RequestID, link ipld.Link, linkContext ipld.LinkContext) types.AsyncLoadResult {
198197
// load from response cache
199198
data, err := responseCache.AttemptLoad(requestID, link, linkContext)
200199
if err != nil {

requestmanager/asyncloader/asyncloader_test.go

+17-29
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ func TestAsyncLoadInitialLoadSucceedsLocallyPresent(t *testing.T) {
2323
link := st.Store(t, block)
2424
withLoader(st, func(ctx context.Context, asyncLoader *AsyncLoader) {
2525
requestID := graphsync.NewRequestID()
26-
p := testutil.GeneratePeers(1)[0]
27-
resultChan := asyncLoader.AsyncLoad(p, requestID, link, ipld.LinkContext{})
26+
resultChan := asyncLoader.AsyncLoad(requestID, link, ipld.LinkContext{})
2827
assertSuccessResponse(ctx, t, resultChan)
2928
st.AssertLocalLoads(t, 1)
3029
})
@@ -45,9 +44,8 @@ func TestAsyncLoadInitialLoadSucceedsResponsePresent(t *testing.T) {
4544
Action: graphsync.LinkActionPresent,
4645
}}),
4746
}
48-
p := testutil.GeneratePeers(1)[0]
4947
asyncLoader.ProcessResponse(context.Background(), responses, blocks)
50-
resultChan := asyncLoader.AsyncLoad(p, requestID, link, ipld.LinkContext{})
48+
resultChan := asyncLoader.AsyncLoad(requestID, link, ipld.LinkContext{})
5149

5250
assertSuccessResponse(ctx, t, resultChan)
5351
st.AssertLocalLoads(t, 0)
@@ -68,10 +66,9 @@ func TestAsyncLoadInitialLoadFails(t *testing.T) {
6866
Action: graphsync.LinkActionMissing,
6967
}}),
7068
}
71-
p := testutil.GeneratePeers(1)[0]
7269
asyncLoader.ProcessResponse(context.Background(), responses, nil)
7370

74-
resultChan := asyncLoader.AsyncLoad(p, requestID, link, ipld.LinkContext{})
71+
resultChan := asyncLoader.AsyncLoad(requestID, link, ipld.LinkContext{})
7572
assertFailResponse(ctx, t, resultChan)
7673
st.AssertLocalLoads(t, 0)
7774
})
@@ -82,8 +79,7 @@ func TestAsyncLoadInitialLoadIndeterminateWhenRequestNotInProgress(t *testing.T)
8279
withLoader(st, func(ctx context.Context, asyncLoader *AsyncLoader) {
8380
link := testutil.NewTestLink()
8481
requestID := graphsync.NewRequestID()
85-
p := testutil.GeneratePeers(1)[0]
86-
resultChan := asyncLoader.AsyncLoad(p, requestID, link, ipld.LinkContext{})
82+
resultChan := asyncLoader.AsyncLoad(requestID, link, ipld.LinkContext{})
8783
assertFailResponse(ctx, t, resultChan)
8884
st.AssertLocalLoads(t, 1)
8985
})
@@ -100,8 +96,7 @@ func TestAsyncLoadInitialLoadIndeterminateThenSucceeds(t *testing.T) {
10096
requestID := graphsync.NewRequestID()
10197
err := asyncLoader.StartRequest(requestID, "")
10298
require.NoError(t, err)
103-
p := testutil.GeneratePeers(1)[0]
104-
resultChan := asyncLoader.AsyncLoad(p, requestID, link, ipld.LinkContext{})
99+
resultChan := asyncLoader.AsyncLoad(requestID, link, ipld.LinkContext{})
105100

106101
st.AssertAttemptLoadWithoutResult(ctx, t, resultChan)
107102

@@ -127,8 +122,7 @@ func TestAsyncLoadInitialLoadIndeterminateThenFails(t *testing.T) {
127122
requestID := graphsync.NewRequestID()
128123
err := asyncLoader.StartRequest(requestID, "")
129124
require.NoError(t, err)
130-
p := testutil.GeneratePeers(1)[0]
131-
resultChan := asyncLoader.AsyncLoad(p, requestID, link, ipld.LinkContext{})
125+
resultChan := asyncLoader.AsyncLoad(requestID, link, ipld.LinkContext{})
132126

133127
st.AssertAttemptLoadWithoutResult(ctx, t, resultChan)
134128

@@ -152,8 +146,7 @@ func TestAsyncLoadInitialLoadIndeterminateThenRequestFinishes(t *testing.T) {
152146
requestID := graphsync.NewRequestID()
153147
err := asyncLoader.StartRequest(requestID, "")
154148
require.NoError(t, err)
155-
p := testutil.GeneratePeers(1)[0]
156-
resultChan := asyncLoader.AsyncLoad(p, requestID, link, ipld.LinkContext{})
149+
resultChan := asyncLoader.AsyncLoad(requestID, link, ipld.LinkContext{})
157150
st.AssertAttemptLoadWithoutResult(ctx, t, resultChan)
158151
asyncLoader.CompleteResponsesFor(requestID)
159152
assertFailResponse(ctx, t, resultChan)
@@ -175,14 +168,13 @@ func TestAsyncLoadTwiceLoadsLocallySecondTime(t *testing.T) {
175168
Action: graphsync.LinkActionPresent,
176169
}}),
177170
}
178-
p := testutil.GeneratePeers(1)[0]
179171
asyncLoader.ProcessResponse(context.Background(), responses, blocks)
180-
resultChan := asyncLoader.AsyncLoad(p, requestID, link, ipld.LinkContext{})
172+
resultChan := asyncLoader.AsyncLoad(requestID, link, ipld.LinkContext{})
181173

182174
assertSuccessResponse(ctx, t, resultChan)
183175
st.AssertLocalLoads(t, 0)
184176

185-
resultChan = asyncLoader.AsyncLoad(p, requestID, link, ipld.LinkContext{})
177+
resultChan = asyncLoader.AsyncLoad(requestID, link, ipld.LinkContext{})
186178
assertSuccessResponse(ctx, t, resultChan)
187179
st.AssertLocalLoads(t, 1)
188180

@@ -206,13 +198,12 @@ func TestRegisterUnregister(t *testing.T) {
206198
requestID2 := graphsync.NewRequestID()
207199
err = asyncLoader.StartRequest(requestID2, "other")
208200
require.NoError(t, err)
209-
p := testutil.GeneratePeers(1)[0]
210-
resultChan1 := asyncLoader.AsyncLoad(p, requestID2, link1, ipld.LinkContext{})
201+
resultChan1 := asyncLoader.AsyncLoad(requestID2, link1, ipld.LinkContext{})
211202
assertSuccessResponse(ctx, t, resultChan1)
212203
err = asyncLoader.UnregisterPersistenceOption("other")
213204
require.EqualError(t, err, "cannot unregister while requests are in progress")
214205
asyncLoader.CompleteResponsesFor(requestID2)
215-
asyncLoader.CleanupRequest(p, requestID2)
206+
asyncLoader.CleanupRequest(requestID2)
216207
err = asyncLoader.UnregisterPersistenceOption("other")
217208
require.NoError(t, err)
218209

@@ -230,13 +221,12 @@ func TestRequestSplittingLoadLocallyFromBlockstore(t *testing.T) {
230221
err := asyncLoader.RegisterPersistenceOption("other", otherSt.lsys)
231222
require.NoError(t, err)
232223
requestID1 := graphsync.NewRequestID()
233-
p := testutil.GeneratePeers(1)[0]
234224

235-
resultChan1 := asyncLoader.AsyncLoad(p, requestID1, link, ipld.LinkContext{})
225+
resultChan1 := asyncLoader.AsyncLoad(requestID1, link, ipld.LinkContext{})
236226
requestID2 := graphsync.NewRequestID()
237227
err = asyncLoader.StartRequest(requestID2, "other")
238228
require.NoError(t, err)
239-
resultChan2 := asyncLoader.AsyncLoad(p, requestID2, link, ipld.LinkContext{})
229+
resultChan2 := asyncLoader.AsyncLoad(requestID2, link, ipld.LinkContext{})
240230

241231
assertFailResponse(ctx, t, resultChan1)
242232
assertSuccessResponse(ctx, t, resultChan2)
@@ -259,9 +249,8 @@ func TestRequestSplittingSameBlockTwoStores(t *testing.T) {
259249
require.NoError(t, err)
260250
err = asyncLoader.StartRequest(requestID2, "other")
261251
require.NoError(t, err)
262-
p := testutil.GeneratePeers(1)[0]
263-
resultChan1 := asyncLoader.AsyncLoad(p, requestID1, link, ipld.LinkContext{})
264-
resultChan2 := asyncLoader.AsyncLoad(p, requestID2, link, ipld.LinkContext{})
252+
resultChan1 := asyncLoader.AsyncLoad(requestID1, link, ipld.LinkContext{})
253+
resultChan2 := asyncLoader.AsyncLoad(requestID2, link, ipld.LinkContext{})
265254
responses := map[graphsync.RequestID]graphsync.LinkMetadata{
266255
requestID1: message.NewLinkMetadata(
267256
[]message.GraphSyncLinkMetadatum{{
@@ -298,9 +287,8 @@ func TestRequestSplittingSameBlockOnlyOneResponse(t *testing.T) {
298287
require.NoError(t, err)
299288
err = asyncLoader.StartRequest(requestID2, "other")
300289
require.NoError(t, err)
301-
p := testutil.GeneratePeers(1)[0]
302-
resultChan1 := asyncLoader.AsyncLoad(p, requestID1, link, ipld.LinkContext{})
303-
resultChan2 := asyncLoader.AsyncLoad(p, requestID2, link, ipld.LinkContext{})
290+
resultChan1 := asyncLoader.AsyncLoad(requestID1, link, ipld.LinkContext{})
291+
resultChan2 := asyncLoader.AsyncLoad(requestID2, link, ipld.LinkContext{})
304292
responses := map[graphsync.RequestID]graphsync.LinkMetadata{
305293
requestID2: message.NewLinkMetadata(
306294
[]message.GraphSyncLinkMetadatum{{

requestmanager/asyncloader/loadattemptqueue/loadattemptqueue.go

+3-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"errors"
55

66
"github.com/ipld/go-ipld-prime"
7-
"github.com/libp2p/go-libp2p-core/peer"
87

98
"github.com/ipfs/go-graphsync"
109
"github.com/ipfs/go-graphsync/requestmanager/types"
@@ -13,7 +12,6 @@ import (
1312
// LoadRequest is a request to load the given link for the given request id,
1413
// with results returned to the given channel
1514
type LoadRequest struct {
16-
p peer.ID
1715
requestID graphsync.RequestID
1816
link ipld.Link
1917
linkContext ipld.LinkContext
@@ -23,17 +21,16 @@ type LoadRequest struct {
2321
// NewLoadRequest returns a new LoadRequest for the given request id, link,
2422
// and results channel
2523
func NewLoadRequest(
26-
p peer.ID,
2724
requestID graphsync.RequestID,
2825
link ipld.Link,
2926
linkContext ipld.LinkContext,
3027
resultChan chan types.AsyncLoadResult) LoadRequest {
31-
return LoadRequest{p, requestID, link, linkContext, resultChan}
28+
return LoadRequest{requestID, link, linkContext, resultChan}
3229
}
3330

3431
// LoadAttempter attempts to load a link to an array of bytes
3532
// and returns an async load result
36-
type LoadAttempter func(peer.ID, graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult
33+
type LoadAttempter func(graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult
3734

3835
// LoadAttemptQueue attempts to load using the load attempter, and then can
3936
// place requests on a retry queue
@@ -52,7 +49,7 @@ func New(loadAttempter LoadAttempter) *LoadAttemptQueue {
5249
// AttemptLoad attempts to loads the given load request, and if retry is true
5350
// it saves the loadrequest for retrying later
5451
func (laq *LoadAttemptQueue) AttemptLoad(lr LoadRequest, retry bool) {
55-
response := laq.loadAttempter(lr.p, lr.requestID, lr.link, lr.linkContext)
52+
response := laq.loadAttempter(lr.requestID, lr.link, lr.linkContext)
5653
if response.Err != nil || response.Data != nil {
5754
lr.resultChan <- response
5855
close(lr.resultChan)

requestmanager/asyncloader/loadattemptqueue/loadattemptqueue_test.go

+10-16
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"time"
88

99
ipld "github.com/ipld/go-ipld-prime"
10-
"github.com/libp2p/go-libp2p-core/peer"
1110
"github.com/stretchr/testify/require"
1211

1312
"github.com/ipfs/go-graphsync"
@@ -20,7 +19,7 @@ func TestAsyncLoadInitialLoadSucceeds(t *testing.T) {
2019
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
2120
defer cancel()
2221
callCount := 0
23-
loadAttempter := func(peer.ID, graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
22+
loadAttempter := func(graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
2423
callCount++
2524
return types.AsyncLoadResult{
2625
Data: testutil.RandomBytes(100),
@@ -31,10 +30,9 @@ func TestAsyncLoadInitialLoadSucceeds(t *testing.T) {
3130
link := testutil.NewTestLink()
3231
linkContext := ipld.LinkContext{}
3332
requestID := graphsync.NewRequestID()
34-
p := testutil.GeneratePeers(1)[0]
3533

3634
resultChan := make(chan types.AsyncLoadResult, 1)
37-
lr := NewLoadRequest(p, requestID, link, linkContext, resultChan)
35+
lr := NewLoadRequest(requestID, link, linkContext, resultChan)
3836
loadAttemptQueue.AttemptLoad(lr, false)
3937

4038
var result types.AsyncLoadResult
@@ -50,7 +48,7 @@ func TestAsyncLoadInitialLoadFails(t *testing.T) {
5048
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
5149
defer cancel()
5250
callCount := 0
53-
loadAttempter := func(peer.ID, graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
51+
loadAttempter := func(graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
5452
callCount++
5553
return types.AsyncLoadResult{
5654
Err: fmt.Errorf("something went wrong"),
@@ -62,9 +60,8 @@ func TestAsyncLoadInitialLoadFails(t *testing.T) {
6260
linkContext := ipld.LinkContext{}
6361
requestID := graphsync.NewRequestID()
6462
resultChan := make(chan types.AsyncLoadResult, 1)
65-
p := testutil.GeneratePeers(1)[0]
6663

67-
lr := NewLoadRequest(p, requestID, link, linkContext, resultChan)
64+
lr := NewLoadRequest(requestID, link, linkContext, resultChan)
6865
loadAttemptQueue.AttemptLoad(lr, false)
6966

7067
var result types.AsyncLoadResult
@@ -79,7 +76,7 @@ func TestAsyncLoadInitialLoadIndeterminateRetryFalse(t *testing.T) {
7976
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
8077
defer cancel()
8178
callCount := 0
82-
loadAttempter := func(peer.ID, graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
79+
loadAttempter := func(graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
8380
var result []byte
8481
if callCount > 0 {
8582
result = testutil.RandomBytes(100)
@@ -95,10 +92,9 @@ func TestAsyncLoadInitialLoadIndeterminateRetryFalse(t *testing.T) {
9592
link := testutil.NewTestLink()
9693
linkContext := ipld.LinkContext{}
9794
requestID := graphsync.NewRequestID()
98-
p := testutil.GeneratePeers(1)[0]
9995

10096
resultChan := make(chan types.AsyncLoadResult, 1)
101-
lr := NewLoadRequest(p, requestID, link, linkContext, resultChan)
97+
lr := NewLoadRequest(requestID, link, linkContext, resultChan)
10298
loadAttemptQueue.AttemptLoad(lr, false)
10399

104100
var result types.AsyncLoadResult
@@ -114,7 +110,7 @@ func TestAsyncLoadInitialLoadIndeterminateRetryTrueThenRetriedSuccess(t *testing
114110
defer cancel()
115111
callCount := 0
116112
called := make(chan struct{}, 2)
117-
loadAttempter := func(peer.ID, graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
113+
loadAttempter := func(graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
118114
var result []byte
119115
called <- struct{}{}
120116
if callCount > 0 {
@@ -131,8 +127,7 @@ func TestAsyncLoadInitialLoadIndeterminateRetryTrueThenRetriedSuccess(t *testing
131127
linkContext := ipld.LinkContext{}
132128
requestID := graphsync.NewRequestID()
133129
resultChan := make(chan types.AsyncLoadResult, 1)
134-
p := testutil.GeneratePeers(1)[0]
135-
lr := NewLoadRequest(p, requestID, link, linkContext, resultChan)
130+
lr := NewLoadRequest(requestID, link, linkContext, resultChan)
136131
loadAttemptQueue.AttemptLoad(lr, true)
137132

138133
testutil.AssertDoesReceiveFirst(t, called, "should attempt load with no result", resultChan, ctx.Done())
@@ -151,7 +146,7 @@ func TestAsyncLoadInitialLoadIndeterminateThenRequestFinishes(t *testing.T) {
151146
defer cancel()
152147
callCount := 0
153148
called := make(chan struct{}, 2)
154-
loadAttempter := func(peer.ID, graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
149+
loadAttempter := func(graphsync.RequestID, ipld.Link, ipld.LinkContext) types.AsyncLoadResult {
155150
var result []byte
156151
called <- struct{}{}
157152
if callCount > 0 {
@@ -168,8 +163,7 @@ func TestAsyncLoadInitialLoadIndeterminateThenRequestFinishes(t *testing.T) {
168163
linkContext := ipld.LinkContext{}
169164
requestID := graphsync.NewRequestID()
170165
resultChan := make(chan types.AsyncLoadResult, 1)
171-
p := testutil.GeneratePeers(1)[0]
172-
lr := NewLoadRequest(p, requestID, link, linkContext, resultChan)
166+
lr := NewLoadRequest(requestID, link, linkContext, resultChan)
173167
loadAttemptQueue.AttemptLoad(lr, true)
174168

175169
testutil.AssertDoesReceiveFirst(t, called, "should attempt load with no result", resultChan, ctx.Done())

requestmanager/client.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ type PeerHandler interface {
7373
type AsyncLoader interface {
7474
StartRequest(graphsync.RequestID, string) error
7575
ProcessResponse(ctx context.Context, responses map[graphsync.RequestID]graphsync.LinkMetadata, blks []blocks.Block)
76-
AsyncLoad(p peer.ID, requestID graphsync.RequestID, link ipld.Link, linkContext ipld.LinkContext) <-chan types.AsyncLoadResult
76+
AsyncLoad(requestID graphsync.RequestID, link ipld.Link, linkContext ipld.LinkContext) <-chan types.AsyncLoadResult
7777
CompleteResponsesFor(requestID graphsync.RequestID)
78-
CleanupRequest(p peer.ID, requestID graphsync.RequestID)
78+
CleanupRequest(requestID graphsync.RequestID)
7979
}
8080

8181
// RequestManager tracks outgoing requests and processes incoming reponses

requestmanager/executor/executor.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type BlockHooks interface {
3838

3939
// AsyncLoadFn is a function which given a request id and an ipld.Link, returns
4040
// a channel which will eventually return data for the link or an err
41-
type AsyncLoadFn func(peer.ID, graphsync.RequestID, ipld.Link, ipld.LinkContext) <-chan types.AsyncLoadResult
41+
type AsyncLoadFn func(graphsync.RequestID, ipld.Link, ipld.LinkContext) <-chan types.AsyncLoadResult
4242

4343
// Executor handles actually executing graphsync requests and verifying them.
4444
// It has control of requests when they are in the "running" state, while
@@ -131,7 +131,7 @@ func (e *Executor) traverse(rt RequestTask) error {
131131
lnk, linkContext := rt.Traverser.CurrentRequest()
132132
// attempt to load
133133
log.Debugf("will load link=%s", lnk)
134-
resultChan := e.loader(rt.P, rt.Request.ID(), lnk, linkContext)
134+
resultChan := e.loader(rt.Request.ID(), lnk, linkContext)
135135
var result types.AsyncLoadResult
136136
// check for immediate result
137137
select {

0 commit comments

Comments
 (0)