linktracker.go 2.89 KB
Newer Older
1 2 3
package linktracker

import (
4
	"gitlab.dms3.io/ld/go-ld-prime"
Hannah Howard's avatar
Hannah Howard committed
5

6
	"gitlab.dms3.io/dms3/go-graphsync"
7 8 9 10 11 12 13 14
)

// 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 {
15 16 17
	missingBlocks                     map[graphsync.RequestID]map[ld.Link]struct{}
	linksWithBlocksTraversedByRequest map[graphsync.RequestID][]ld.Link
	traversalsWithBlocksInProgress    map[ld.Link]int
18 19 20 21 22
}

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

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

// IsKnownMissingLink returns whether the given request recorded the given link as missing
36
func (lt *LinkTracker) IsKnownMissingLink(requestID graphsync.RequestID, link ld.Link) bool {
37 38 39 40 41 42
	missingBlocks, ok := lt.missingBlocks[requestID]
	if !ok {
		return false
	}
	_, ok = missingBlocks[link]
	return ok
43 44 45 46
}

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

// FinishRequest records that we have completed the given request, and returns
// true if all links traversed had blocks present.
63
func (lt *LinkTracker) FinishRequest(requestID graphsync.RequestID) (hasAllBlocks bool) {
64
	_, ok := lt.missingBlocks[requestID]
65
	hasAllBlocks = !ok
66
	delete(lt.missingBlocks, requestID)
67 68 69 70 71 72 73 74 75 76 77 78 79 80
	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
}
Hannah Howard's avatar
Hannah Howard committed
81 82 83 84 85

// Empty returns true if the link tracker is empty
func (lt *LinkTracker) Empty() bool {
	return len(lt.missingBlocks) == 0 && len(lt.traversalsWithBlocksInProgress) == 0
}