merkledag.go 6.07 KB
Newer Older
1
// package merkledag implements the ipfs Merkle DAG datastructures.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
2 3 4
package merkledag

import (
5
	"fmt"
Jeromy's avatar
Jeromy committed
6
	"sync"
Jeromy's avatar
Jeromy committed
7

8 9
	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
	blocks "github.com/ipfs/go-ipfs/blocks"
10
	key "github.com/ipfs/go-ipfs/blocks/key"
11 12
	bserv "github.com/ipfs/go-ipfs/blockservice"
	u "github.com/ipfs/go-ipfs/util"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13 14
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
15
var log = u.Logger("merkledag")
16
var ErrNotFound = fmt.Errorf("merkledag: not found")
Jeromy's avatar
Jeromy committed
17

Jeromy's avatar
Jeromy committed
18 19
// DAGService is an IPFS Merkle DAG service.
type DAGService interface {
20
	Add(*Node) (key.Key, error)
Jeromy's avatar
Jeromy committed
21
	AddRecursive(*Node) error
22
	Get(context.Context, key.Key) (*Node, error)
Jeromy's avatar
Jeromy committed
23
	Remove(*Node) error
24 25 26

	// GetDAG returns, in order, all the single leve child
	// nodes of the passed in node.
Jeromy's avatar
Jeromy committed
27
	GetDAG(context.Context, *Node) []NodeGetter
28
	GetNodes(context.Context, []key.Key) []NodeGetter
Jeromy's avatar
Jeromy committed
29 30 31 32 33 34
}

func NewDAGService(bs *bserv.BlockService) DAGService {
	return &dagService{bs}
}

35
// dagService is an IPFS Merkle DAG service.
Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
36 37
// - the root is virtual (like a forest)
// - stores nodes' data in a BlockService
38 39
// TODO: should cache Nodes that are in memory, and be
//       able to free some of them when vm pressure is high
40
type dagService struct {
41
	Blocks *bserv.BlockService
42 43
}

44
// Add adds a node to the dagService, storing the block in the BlockService
45
func (n *dagService) Add(nd *Node) (key.Key, error) {
46
	if n == nil { // FIXME remove this assertion. protect with constructor invariant
47
		return "", fmt.Errorf("dagService is nil")
48 49 50 51 52 53 54
	}

	d, err := nd.Encoded(false)
	if err != nil {
		return "", err
	}

55 56 57
	b := new(blocks.Block)
	b.Data = d
	b.Multihash, err = nd.Multihash()
58 59 60 61 62 63 64
	if err != nil {
		return "", err
	}

	return n.Blocks.AddBlock(b)
}

Jeromy's avatar
Jeromy committed
65
// AddRecursive adds the given node and all child nodes to the BlockService
66
func (n *dagService) AddRecursive(nd *Node) error {
67 68
	_, err := n.Add(nd)
	if err != nil {
Jeromy's avatar
Jeromy committed
69
		log.Info("AddRecursive Error: %s\n", err)
70 71 72 73
		return err
	}

	for _, link := range nd.Links {
74 75 76 77 78
		if link.Node != nil {
			err := n.AddRecursive(link.Node)
			if err != nil {
				return err
			}
79 80 81 82 83 84
		}
	}

	return nil
}

85
// Get retrieves a node from the dagService, fetching the block in the BlockService
86
func (n *dagService) Get(ctx context.Context, k key.Key) (*Node, error) {
87
	if n == nil {
88
		return nil, fmt.Errorf("dagService is nil")
89 90
	}

Jeromy's avatar
Jeromy committed
91
	b, err := n.Blocks.GetBlock(ctx, k)
92 93 94 95 96 97
	if err != nil {
		return nil, err
	}

	return Decoded(b.Data)
}
Jeromy's avatar
Jeromy committed
98

Jeromy's avatar
Jeromy committed
99
// Remove deletes the given node and all of its children from the BlockService
100
func (n *dagService) Remove(nd *Node) error {
Jeromy's avatar
Jeromy committed
101 102 103 104 105 106 107 108 109 110 111
	for _, l := range nd.Links {
		if l.Node != nil {
			n.Remove(l.Node)
		}
	}
	k, err := nd.Key()
	if err != nil {
		return err
	}
	return n.Blocks.DeleteBlock(k)
}
Jeromy's avatar
Jeromy committed
112

Jeromy's avatar
Jeromy committed
113 114
// FetchGraph asynchronously fetches all nodes that are children of the given
// node, and returns a channel that may be waited upon for the fetch to complete
Jeromy's avatar
Jeromy committed
115
func FetchGraph(ctx context.Context, root *Node, serv DAGService) chan struct{} {
116
	log.Warning("Untested.")
Jeromy's avatar
Jeromy committed
117 118 119
	var wg sync.WaitGroup
	done := make(chan struct{})

Jeromy's avatar
Jeromy committed
120
	for _, l := range root.Links {
Jeromy's avatar
Jeromy committed
121
		wg.Add(1)
Jeromy's avatar
Jeromy committed
122
		go func(lnk *Link) {
Jeromy's avatar
Jeromy committed
123 124 125

			// Signal child is done on way out
			defer wg.Done()
Jeromy's avatar
Jeromy committed
126 127 128 129 130
			select {
			case <-ctx.Done():
				return
			}

Jeromy's avatar
Jeromy committed
131
			nd, err := lnk.GetNode(ctx, serv)
Jeromy's avatar
Jeromy committed
132
			if err != nil {
133
				log.Debug(err)
Jeromy's avatar
Jeromy committed
134 135
				return
			}
Jeromy's avatar
Jeromy committed
136 137 138

			// Wait for children to finish
			<-FetchGraph(ctx, nd, serv)
Jeromy's avatar
Jeromy committed
139 140
		}(l)
	}
Jeromy's avatar
Jeromy committed
141 142 143 144 145 146 147

	go func() {
		wg.Wait()
		done <- struct{}{}
	}()

	return done
Jeromy's avatar
Jeromy committed
148
}
149

Jeromy's avatar
Jeromy committed
150 151
// FindLinks searches this nodes links for the given key,
// returns the indexes of any links pointing to it
152
func FindLinks(links []key.Key, k key.Key, start int) []int {
Jeromy's avatar
Jeromy committed
153
	var out []int
Jeromy's avatar
Jeromy committed
154 155
	for i, lnk_k := range links[start:] {
		if k == lnk_k {
Jeromy's avatar
Jeromy committed
156
			out = append(out, i+start)
Jeromy's avatar
Jeromy committed
157 158
		}
	}
Jeromy's avatar
Jeromy committed
159
	return out
Jeromy's avatar
Jeromy committed
160 161
}

162
// GetDAG will fill out all of the links of the given Node.
163 164
// It returns a channel of nodes, which the caller can receive
// all the child nodes of 'root' on, in proper order.
Jeromy's avatar
Jeromy committed
165
func (ds *dagService) GetDAG(ctx context.Context, root *Node) []NodeGetter {
166
	var keys []key.Key
Jeromy's avatar
Jeromy committed
167
	for _, lnk := range root.Links {
168
		keys = append(keys, key.Key(lnk.Hash))
Jeromy's avatar
Jeromy committed
169 170 171 172 173
	}

	return ds.GetNodes(ctx, keys)
}

Jeromy's avatar
Jeromy committed
174 175
// GetNodes returns an array of 'NodeGetter' promises, with each corresponding
// to the key with the same index as the passed in keys
176
func (ds *dagService) GetNodes(ctx context.Context, keys []key.Key) []NodeGetter {
177 178 179 180 181 182

	// Early out if no work to do
	if len(keys) == 0 {
		return nil
	}

Jeromy's avatar
Jeromy committed
183 184
	promises := make([]NodeGetter, len(keys))
	sendChans := make([]chan<- *Node, len(keys))
rht's avatar
rht committed
185
	for i := range keys {
Jeromy's avatar
Jeromy committed
186 187 188
		promises[i], sendChans[i] = newNodePromise(ctx)
	}

189
	dedupedKeys := dedupeKeys(keys)
190
	go func() {
191 192 193
		ctx, cancel := context.WithCancel(ctx)
		defer cancel()

194
		blkchan := ds.Blocks.GetBlocks(ctx, dedupedKeys)
Jeromy's avatar
Jeromy committed
195

196
		for count := 0; count < len(keys); {
Jeromy's avatar
Jeromy committed
197 198 199 200 201
			select {
			case blk, ok := <-blkchan:
				if !ok {
					return
				}
Jeromy's avatar
Jeromy committed
202

Jeromy's avatar
Jeromy committed
203 204
				nd, err := Decoded(blk.Data)
				if err != nil {
Jeromy's avatar
Jeromy committed
205
					// NB: can happen with improperly formatted input data
206
					log.Debug("Got back bad block!")
Jeromy's avatar
Jeromy committed
207
					return
Jeromy's avatar
Jeromy committed
208
				}
Jeromy's avatar
Jeromy committed
209
				is := FindLinks(keys, blk.Key(), 0)
Jeromy's avatar
Jeromy committed
210
				for _, i := range is {
211
					count++
212
					sendChans[i] <- nd
Jeromy's avatar
Jeromy committed
213 214 215
				}
			case <-ctx.Done():
				return
216 217 218
			}
		}
	}()
Jeromy's avatar
Jeromy committed
219 220 221
	return promises
}

222
// Remove duplicates from a list of keys
223 224 225
func dedupeKeys(ks []key.Key) []key.Key {
	kmap := make(map[key.Key]struct{})
	var out []key.Key
226 227 228 229 230 231 232 233 234
	for _, k := range ks {
		if _, ok := kmap[k]; !ok {
			kmap[k] = struct{}{}
			out = append(out, k)
		}
	}
	return out
}

Jeromy's avatar
Jeromy committed
235 236 237 238 239 240 241 242 243 244 245 246 247 248
func newNodePromise(ctx context.Context) (NodeGetter, chan<- *Node) {
	ch := make(chan *Node, 1)
	return &nodePromise{
		recv: ch,
		ctx:  ctx,
	}, ch
}

type nodePromise struct {
	cache *Node
	recv  <-chan *Node
	ctx   context.Context
}

Jeromy's avatar
Jeromy committed
249 250 251 252
// NodeGetter provides a promise like interface for a dag Node
// the first call to Get will block until the Node is received
// from its internal channels, subsequent calls will return the
// cached node.
Jeromy's avatar
Jeromy committed
253
type NodeGetter interface {
254
	Get(context.Context) (*Node, error)
Jeromy's avatar
Jeromy committed
255 256
}

257
func (np *nodePromise) Get(ctx context.Context) (*Node, error) {
Jeromy's avatar
Jeromy committed
258 259 260 261 262 263 264 265 266
	if np.cache != nil {
		return np.cache, nil
	}

	select {
	case blk := <-np.recv:
		np.cache = blk
	case <-np.ctx.Done():
		return nil, np.ctx.Err()
267 268
	case <-ctx.Done():
		return nil, ctx.Err()
Jeromy's avatar
Jeromy committed
269 270
	}
	return np.cache, nil
271
}