linktracker.go 2.78 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
package linktracker

import (
	gsmsg "github.com/ipfs/go-graphsync/message"
	"github.com/ipld/go-ipld-prime"
)

// LinkTracker records links being traversed to determine useful information
// in crafting responses for a peer. Specifically, if any in progress request
// has already sent a block for a given link, don't send it again.
// Second, keep track of whether links are missing blocks so you can determine
// at the end if a complete response has been transmitted.
type LinkTracker struct {
14
	missingBlocks                     map[gsmsg.GraphSyncRequestID]map[ipld.Link]struct{}
15 16 17 18 19 20 21
	linksWithBlocksTraversedByRequest map[gsmsg.GraphSyncRequestID][]ipld.Link
	traversalsWithBlocksInProgress    map[ipld.Link]int
}

// New makes a new link tracker
func New() *LinkTracker {
	return &LinkTracker{
22
		missingBlocks:                     make(map[gsmsg.GraphSyncRequestID]map[ipld.Link]struct{}),
23 24 25 26 27
		linksWithBlocksTraversedByRequest: make(map[gsmsg.GraphSyncRequestID][]ipld.Link),
		traversalsWithBlocksInProgress:    make(map[ipld.Link]int),
	}
}

28 29 30 31 32 33 34 35 36 37 38 39 40 41
// BlockRefCount returns the number of times a present block has been traversed
// by in progress requests
func (lt *LinkTracker) BlockRefCount(link ipld.Link) int {
	return lt.traversalsWithBlocksInProgress[link]
}

// IsKnownMissingLink returns whether the given request recorded the given link as missing
func (lt *LinkTracker) IsKnownMissingLink(requestID gsmsg.GraphSyncRequestID, link ipld.Link) bool {
	missingBlocks, ok := lt.missingBlocks[requestID]
	if !ok {
		return false
	}
	_, ok = missingBlocks[link]
	return ok
42 43 44 45 46 47 48 49 50
}

// RecordLinkTraversal records that we traversed a link during a request, and
// whether we had the block when we did it.
func (lt *LinkTracker) RecordLinkTraversal(requestID gsmsg.GraphSyncRequestID, link ipld.Link, hasBlock bool) {
	if hasBlock {
		lt.linksWithBlocksTraversedByRequest[requestID] = append(lt.linksWithBlocksTraversedByRequest[requestID], link)
		lt.traversalsWithBlocksInProgress[link]++
	} else {
51 52 53 54 55 56
		missingBlocks, ok := lt.missingBlocks[requestID]
		if !ok {
			missingBlocks = make(map[ipld.Link]struct{})
			lt.missingBlocks[requestID] = missingBlocks
		}
		missingBlocks[link] = struct{}{}
57 58 59 60 61 62
	}
}

// FinishRequest records that we have completed the given request, and returns
// true if all links traversed had blocks present.
func (lt *LinkTracker) FinishRequest(requestID gsmsg.GraphSyncRequestID) (hasAllBlocks bool) {
63
	_, ok := lt.missingBlocks[requestID]
64
	hasAllBlocks = !ok
65
	delete(lt.missingBlocks, requestID)
66 67 68 69 70 71 72 73 74 75 76 77 78 79
	links, ok := lt.linksWithBlocksTraversedByRequest[requestID]
	if !ok {
		return
	}
	for _, link := range links {
		lt.traversalsWithBlocksInProgress[link]--
		if lt.traversalsWithBlocksInProgress[link] <= 0 {
			delete(lt.traversalsWithBlocksInProgress, link)
		}
	}
	delete(lt.linksWithBlocksTraversedByRequest, requestID)

	return
}