Requesting the same content multiple times from the same provider fails
Closed this issue · 1 comments
Problem
Gossip sub prevents peers from receiving duplicate messages, so when requesting the same CID multiple times generates the same message providers will treat it as duplicates and not reply again. This could be an issue if a client needs to request the same content multiple times maybe because they don't have space to cache it locally.
Solution
We need some type of ID to differentiate the requests
@gallexis this issue might be outdated but here is a pointer on how to verify:
In routing_test.go add a new test, here is an example checking a single query, you will need to update it so the client queries the same CID a second time and see if you get all responses the second time.
func TestGossipDuplicateRequests(t *testing.T) {
// background context. TODO: use a timeout to avoid failing tests hanging forever
ctx := context.Background()
// make a mock network
mn := mocknet.New(ctx)
// Generate a random file and keep reference to its location on disk
fileName := (&testutil.TestNode{}).CreateRandomFile(t, 256000)
// Keep a reference to the root CID of the file
var rootCID cid.Cid
// We can keep reference to our providers here
providers := make(map[peer.ID]*GossipRouting)
var pnodes []*testutil.TestNode
// Generate providers
for i := 0; i < 11; i++ {
n := testutil.NewTestNode(mn, t)
// Create all our service instances
tracer := NewGossipTracer()
ps, err := pubsub.NewGossipSub(ctx, n.Host, pubsub.WithEventTracer(tracer))
require.NoError(t, err)
routing := NewGossipRouting(n.Host, ps, tracer, []Region{global})
// This will start listening for gossip messages (calcResponse is mocked at routing_test.go:50)
require.NoError(t, routing.StartProviding(ctx, calcResponse))
// each provider is loading the same file in their blockstore
link, _, _ := n.LoadFileToNewStore(ctx, t, fileName)
// The Link interface must be cast to access the Cid field
rootCID = link.(cidlink.Link).Cid
providers[n.Host.ID()] = routing
pnodes = append(pnodes, n)
}
// Make a single client
n := testutil.NewTestNode(mn, t)
tracer := NewGossipTracer()
ps, err := pubsub.NewGossipSub(ctx, n.Host, pubsub.WithEventTracer(tracer))
require.NoError(t, err)
client := NewGossipRouting(n.Host, ps, tracer, []Region{global})
// calcResponse is mocked at routing_test.go:50
require.NoError(t, client.StartProviding(ctx, calcResponse))
// Connect all our nodes
require.NoError(t, mn.LinkAll())
// ConnectAllButSelf means all the nodes will dial each other except themselves
require.NoError(t, mn.ConnectAllButSelf())
// Wait for the responses the client will get here
resps := make(chan deal.QueryResponse)
// This will be called each time the client receives a response from a provider
client.SetReceiver(func(i peer.AddrInfo, r deal.QueryResponse) {
resps <- r
})
// publish a gossip message containing the CID and the selector to all the subscribed providers
err = client.Query(ctx, rootCID, sel.All())
require.NoError(t, err)
// iterate over all the responses to verify they're all here
for i := 0; i < 11; i++ {
select {
case r := <-resps:
require.Equal(t, r.Size, uint64(268009))
case <-ctx.Done():
t.Fatal("couldn't get all the responses")
}
}
}
Open a PR when you have the test running a duplicate query.