myelnet/pop

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.