kubo/namecache/namecache.go
vyzo 2969f795b0 namecache: ipfs name follow
License: MIT
Signed-off-by: vyzo <vyzo@hackzen.org>
2019-02-05 20:28:53 +01:00

152 lines
3.1 KiB
Go

// Package namecache implements background following (resolution and pinning) of names
package namecache
import (
"context"
"strings"
"sync"
"time"
namesys "github.com/ipfs/go-ipfs/namesys"
pin "github.com/ipfs/go-ipfs/pin"
uio "gx/ipfs/QmUnHNqhSB1JgzVCxL1Kz3yb4bdyB4q1Z9AD5AUBVmt3fZ/go-unixfs/io"
resolver "gx/ipfs/QmVi2uUygezqaMTqs3Yzt5FcZFHJoYD4B7jQ2BELjj7ZuY/go-path/resolver"
ipld "gx/ipfs/QmcKKBwfz6FyQdHR2jsXrrF6XeSBXYL86anmWNewpFpoF5/go-ipld-format"
logging "gx/ipfs/QmcuXC5cxs79ro2cUuHs4HQ2bkDLJUYokwL8aivcX6HW3C/go-log"
)
const (
followInterval = 60 * time.Minute
)
var log = logging.Logger("namecache")
// NameCache represents a following cache of names
type NameCache interface {
Follow(name string, pinit bool)
Unfollow(name string)
ListFollows() []string
}
type nameCache struct {
nsys namesys.NameSystem
pinning pin.Pinner
dag ipld.DAGService
ctx context.Context
follows map[string]func()
mx sync.Mutex
}
func NewNameCache(ctx context.Context, nsys namesys.NameSystem, pinning pin.Pinner, dag ipld.DAGService) NameCache {
return &nameCache{
ctx: ctx,
nsys: nsys,
pinning: pinning,
dag: dag,
follows: make(map[string]func()),
}
}
// Follow spawns a goroutine that periodically resolves a name
// and (when pinit is true) pins it in the background
func (nc *nameCache) Follow(name string, pinit bool) {
nc.mx.Lock()
defer nc.mx.Unlock()
if _, ok := nc.follows[name]; ok {
return
}
ctx, cancel := context.WithCancel(nc.ctx)
go nc.followName(ctx, name, pinit)
nc.follows[name] = cancel
}
// Unfollow cancels a follow
func (nc *nameCache) Unfollow(name string) {
nc.mx.Lock()
defer nc.mx.Unlock()
cancel, ok := nc.follows[name]
if ok {
cancel()
delete(nc.follows, name)
}
}
// ListFollows returns a list of names currently being followed
func (nc *nameCache) ListFollows() []string {
nc.mx.Lock()
defer nc.mx.Unlock()
follows := make([]string, 0)
for name, _ := range nc.follows {
follows = append(follows, name)
}
return follows
}
func (nc *nameCache) followName(ctx context.Context, name string, pinit bool) {
nc.resolveAndPin(ctx, name, pinit)
ticker := time.NewTicker(followInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
nc.resolveAndPin(ctx, name, pinit)
case <-ctx.Done():
return
}
}
}
func (nc *nameCache) resolveAndPin(ctx context.Context, name string, pinit bool) {
log.Debugf("resolving %s", name)
if !strings.HasPrefix(name, "/ipns/") {
name = "/ipns/" + name
}
p, err := nc.nsys.Resolve(ctx, name)
if err != nil {
log.Debugf("error resolving %s: %s", name, err.Error())
return
}
log.Debugf("resolved %s to %s", name, p)
if !pinit {
return
}
log.Debugf("pinning %s", p)
r := &resolver.Resolver{
DAG: nc.dag,
ResolveOnce: uio.ResolveUnixfsOnce,
}
n, err := r.ResolvePath(ctx, p)
if err != nil {
log.Debugf("error resolving path %s to node: %s", p, err.Error())
return
}
err = nc.pinning.Pin(ctx, n, true)
if err != nil {
log.Debugf("error pinning path %s: %s", p, err.Error())
return
}
err = nc.pinning.Flush()
if err != nil {
log.Debugf("error flushing pin: %s", err.Error())
}
}