pin.go 5 KB
Newer Older
1 2 3
package pin

import (
4 5

	//ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6

Jeromy's avatar
flush!  
Jeromy committed
7
	"encoding/json"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
8
	"errors"
Jeromy's avatar
Jeromy committed
9 10
	"sync"

Jeromy's avatar
Jeromy committed
11 12
	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
	nsds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore/namespace"
13 14 15 16 17
	"github.com/jbenet/go-ipfs/blocks/set"
	mdag "github.com/jbenet/go-ipfs/merkledag"
	"github.com/jbenet/go-ipfs/util"
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
18
var log = util.Logger("pin")
19 20 21 22
var recursePinDatastoreKey = ds.NewKey("/local/pins/recursive/keys")
var directPinDatastoreKey = ds.NewKey("/local/pins/direct/keys")
var indirectPinDatastoreKey = ds.NewKey("/local/pins/indirect/keys")

23 24 25 26 27 28 29 30
type PinMode int

const (
	Recursive PinMode = iota
	Direct
	Indirect
)

31
type Pinner interface {
Jeromy's avatar
Jeromy committed
32
	IsPinned(util.Key) bool
33 34
	Pin(*mdag.Node, bool) error
	Unpin(util.Key, bool) error
35
	Flush() error
36 37
}

38 39 40 41 42 43 44
// ManualPinner is for manually editing the pin structure
// Use with care
type ManualPinner interface {
	PinWithMode(util.Key, PinMode)
	Pinner
}

45
type pinner struct {
Jeromy's avatar
Jeromy committed
46
	lock       sync.RWMutex
47 48
	recursePin set.BlockSet
	directPin  set.BlockSet
49
	indirPin   *indirectPin
50
	dserv      mdag.DAGService
51
	dstore     ds.Datastore
52 53
}

54
func NewPinner(dstore ds.Datastore, serv mdag.DAGService) Pinner {
55 56

	// Load set from given datastore...
57 58
	rcds := nsds.Wrap(dstore, recursePinDatastoreKey)
	rcset := set.NewDBWrapperSet(rcds, set.NewSimpleBlockSet())
59

60 61 62 63
	dirds := nsds.Wrap(dstore, directPinDatastoreKey)
	dirset := set.NewDBWrapperSet(dirds, set.NewSimpleBlockSet())

	nsdstore := nsds.Wrap(dstore, indirectPinDatastoreKey)
64
	return &pinner{
65 66
		recursePin: rcset,
		directPin:  dirset,
67
		indirPin:   NewIndirectPin(nsdstore),
68
		dserv:      serv,
69
		dstore:     dstore,
70 71 72 73
	}
}

func (p *pinner) Pin(node *mdag.Node, recurse bool) error {
Jeromy's avatar
Jeromy committed
74 75
	p.lock.Lock()
	defer p.lock.Unlock()
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	k, err := node.Key()
	if err != nil {
		return err
	}

	if recurse {
		if p.recursePin.HasKey(k) {
			return nil
		}

		p.recursePin.AddBlock(k)

		err := p.pinLinks(node)
		if err != nil {
			return err
		}
	} else {
		p.directPin.AddBlock(k)
	}
	return nil
}

func (p *pinner) Unpin(k util.Key, recurse bool) error {
Jeromy's avatar
Jeromy committed
99 100
	p.lock.Lock()
	defer p.lock.Unlock()
101 102 103 104 105 106 107 108 109
	if recurse {
		p.recursePin.RemoveBlock(k)
		node, err := p.dserv.Get(k)
		if err != nil {
			return err
		}

		return p.unpinLinks(node)
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
110
	p.directPin.RemoveBlock(k)
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
	return nil
}

func (p *pinner) unpinLinks(node *mdag.Node) error {
	for _, l := range node.Links {
		node, err := l.GetNode(p.dserv)
		if err != nil {
			return err
		}

		k, err := node.Key()
		if err != nil {
			return err
		}

		p.recursePin.RemoveBlock(k)

		err = p.unpinLinks(node)
		if err != nil {
			return err
		}
	}
133 134 135 136 137 138 139 140 141
	return nil
}

func (p *pinner) pinIndirectRecurse(node *mdag.Node) error {
	k, err := node.Key()
	if err != nil {
		return err
	}

142
	p.indirPin.Increment(k)
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
	return p.pinLinks(node)
}

func (p *pinner) pinLinks(node *mdag.Node) error {
	for _, l := range node.Links {
		subnode, err := l.GetNode(p.dserv)
		if err != nil {
			// TODO: Maybe just log and continue?
			return err
		}
		err = p.pinIndirectRecurse(subnode)
		if err != nil {
			return err
		}
	}
	return nil
}

func (p *pinner) IsPinned(key util.Key) bool {
Jeromy's avatar
Jeromy committed
162 163
	p.lock.RLock()
	defer p.lock.RUnlock()
164 165 166 167
	return p.recursePin.HasKey(key) ||
		p.directPin.HasKey(key) ||
		p.indirPin.HasKey(key)
}
168

169
func LoadPinner(d ds.Datastore, dserv mdag.DAGService) (Pinner, error) {
170 171
	p := new(pinner)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
172 173 174 175 176 177
	{ // load recursive set
		var recurseKeys []util.Key
		if err := loadSet(d, recursePinDatastoreKey, &recurseKeys); err != nil {
			return nil, err
		}
		p.recursePin = set.SimpleSetFromKeys(recurseKeys)
178
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
179 180 181 182 183 184 185

	{ // load direct set
		var directKeys []util.Key
		if err := loadSet(d, directPinDatastoreKey, &directKeys); err != nil {
			return nil, err
		}
		p.directPin = set.SimpleSetFromKeys(directKeys)
186 187
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
188 189 190 191 192 193
	{ // load indirect set
		var err error
		p.indirPin, err = loadIndirPin(d, indirectPinDatastoreKey)
		if err != nil {
			return nil, err
		}
194 195
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
196
	// assign services
Jeromy's avatar
Jeromy committed
197 198 199
	p.dserv = dserv
	p.dstore = d

200 201 202 203
	return p, nil
}

func (p *pinner) Flush() error {
Jeromy's avatar
Jeromy committed
204 205
	p.lock.RLock()
	defer p.lock.RUnlock()
Jeromy's avatar
flush!  
Jeromy committed
206

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
207
	err := storeSet(p.dstore, directPinDatastoreKey, p.directPin.GetKeys())
208 209 210 211
	if err != nil {
		return err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
212
	err = storeSet(p.dstore, recursePinDatastoreKey, p.recursePin.GetKeys())
Jeromy's avatar
flush!  
Jeromy committed
213 214 215 216
	if err != nil {
		return err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
217
	err = storeIndirPin(p.dstore, indirectPinDatastoreKey, p.indirPin)
Jeromy's avatar
flush!  
Jeromy committed
218 219 220
	if err != nil {
		return err
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
221 222
	return nil
}
Jeromy's avatar
flush!  
Jeromy committed
223

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
224 225 226
// helpers to marshal / unmarshal a pin set
func storeSet(d ds.Datastore, k ds.Key, val interface{}) error {
	buf, err := json.Marshal(val)
Jeromy's avatar
flush!  
Jeromy committed
227 228 229 230
	if err != nil {
		return err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
231 232 233 234 235
	return d.Put(k, buf)
}

func loadSet(d ds.Datastore, k ds.Key, val interface{}) error {
	buf, err := d.Get(k)
236 237 238 239
	if err != nil {
		return err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
240 241 242
	bf, ok := buf.([]byte)
	if !ok {
		return errors.New("invalid pin set value in datastore")
243
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
244
	return json.Unmarshal(bf, val)
245
}
246 247 248 249 250 251 252 253 254 255 256

func (p *pinner) PinWithMode(k util.Key, mode PinMode) {
	switch mode {
	case Recursive:
		p.recursePin.AddBlock(k)
	case Direct:
		p.directPin.AddBlock(k)
	case Indirect:
		p.indirPin.Increment(k)
	}
}