提交 5d4f3fbd 作者: Erik Ingenito

Cleanup, fix broken restart, and more tests.

License: MIT
Signed-off-by: 's avatarErik Ingenito <erik@carbonfive.com>
上级 7d07347e
...@@ -62,28 +62,13 @@ func (p *provider) handleAnnouncements() { ...@@ -62,28 +62,13 @@ func (p *provider) handleAnnouncements() {
case <-p.ctx.Done(): case <-p.ctx.Done():
return return
case entry := <-p.queue.Dequeue(): case entry := <-p.queue.Dequeue():
if err := doProvide(p.ctx, p.contentRouting, entry.cid); err != nil { log.Info("announce - start - ", entry.cid)
if err := p.contentRouting.Provide(p.ctx, entry.cid, true); err != nil {
log.Warningf("Unable to provide entry: %s, %s", entry.cid, err) log.Warningf("Unable to provide entry: %s, %s", entry.cid, err)
} }
log.Info("announce - end - ", entry.cid)
if err := entry.Complete(); err != nil {
log.Warningf("Unable to complete queue entry when providing: %s, %s", entry.cid, err)
}
} }
} }
}() }()
} }
} }
// TODO: better document this provide logic
func doProvide(ctx context.Context, contentRouting routing.ContentRouting, key cid.Cid) error {
// announce
log.Info("announce - start - ", key)
if err := contentRouting.Provide(ctx, key, true); err != nil {
log.Warningf("Failed to provide cid: %s", err)
// TODO: Maybe put these failures onto a failures queue?
return err
}
log.Info("announce - end - ", key)
return nil
}
...@@ -2,13 +2,15 @@ package provider ...@@ -2,13 +2,15 @@ package provider
import ( import (
"context" "context"
"github.com/ipfs/go-cid"
"github.com/ipfs/go-datastore"
"github.com/ipfs/go-ipfs-blocksutil"
pstore "github.com/libp2p/go-libp2p-peerstore"
"math/rand" "math/rand"
"testing" "testing"
"time" "time"
blocksutil "github.com/ipfs/go-ipfs-blocksutil"
cid "github.com/ipfs/go-cid"
datastore "github.com/ipfs/go-datastore"
pstore "github.com/libp2p/go-libp2p-peerstore"
sync "github.com/ipfs/go-datastore/sync"
) )
var blockGenerator = blocksutil.NewBlockGenerator() var blockGenerator = blocksutil.NewBlockGenerator()
...@@ -25,11 +27,10 @@ func mockContentRouting() *mockRouting { ...@@ -25,11 +27,10 @@ func mockContentRouting() *mockRouting {
func TestAnnouncement(t *testing.T) { func TestAnnouncement(t *testing.T) {
ctx := context.Background() ctx := context.Background()
defer func() { defer ctx.Done()
ctx.Done()
}()
queue, err := NewQueue(ctx, "test", datastore.NewMapDatastore()) ds := sync.MutexWrap(datastore.NewMapDatastore())
queue, err := NewQueue(ctx, "test", ds)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
......
...@@ -3,14 +3,15 @@ package provider ...@@ -3,14 +3,15 @@ package provider
import ( import (
"context" "context"
"errors" "errors"
"github.com/ipfs/go-cid"
ds "github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/namespace"
"github.com/ipfs/go-datastore/query"
"math" "math"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
cid "github.com/ipfs/go-cid"
datastore "github.com/ipfs/go-datastore"
namespace "github.com/ipfs/go-datastore/namespace"
query "github.com/ipfs/go-datastore/query"
) )
// Entry allows for the durability in the queue. When a cid is dequeued it is // Entry allows for the durability in the queue. When a cid is dequeued it is
...@@ -18,17 +19,10 @@ import ( ...@@ -18,17 +19,10 @@ import (
// receive. // receive.
type Entry struct { type Entry struct {
cid cid.Cid cid cid.Cid
key ds.Key key datastore.Key
queue *Queue queue *Queue
} }
// Complete the entry by removing it from the queue
func (e *Entry) Complete() error {
e.queue.lock.Lock()
defer e.queue.lock.Unlock()
return e.queue.remove(e.key)
}
// Queue provides a durable, FIFO interface to the datastore for storing cids // Queue provides a durable, FIFO interface to the datastore for storing cids
// //
// Durability just means that cids in the process of being provided when a // Durability just means that cids in the process of being provided when a
...@@ -44,41 +38,41 @@ type Queue struct { ...@@ -44,41 +38,41 @@ type Queue struct {
tail uint64 tail uint64
head uint64 head uint64
lock sync.Mutex enqueueLock sync.Mutex
datastore ds.Datastore ds datastore.Datastore // Must be threadsafe
dequeue chan *Entry dequeue chan *Entry
added chan struct{} added chan struct{}
} }
// NewQueue creates a queue for cids // NewQueue creates a queue for cids
func NewQueue(ctx context.Context, name string, datastore ds.Datastore) (*Queue, error) { func NewQueue(ctx context.Context, name string, ds datastore.Datastore) (*Queue, error) {
namespaced := namespace.Wrap(datastore, ds.NewKey("/"+name+"/queue/")) namespaced := namespace.Wrap(ds, datastore.NewKey("/"+name+"/queue/"))
head, tail, err := getQueueHeadTail(ctx, name, namespaced) head, tail, err := getQueueHeadTail(ctx, name, namespaced)
if err != nil { if err != nil {
return nil, err return nil, err
} }
q := &Queue{ q := &Queue{
name: name, name: name,
ctx: ctx, ctx: ctx,
head: head, head: head,
tail: tail, tail: tail,
lock: sync.Mutex{}, enqueueLock: sync.Mutex{},
datastore: namespaced, ds: namespaced,
dequeue: make(chan *Entry), dequeue: make(chan *Entry),
added: make(chan struct{}), added: make(chan struct{}),
} }
return q, nil return q, nil
} }
// Enqueue puts a cid in the queue // Enqueue puts a cid in the queue
func (q *Queue) Enqueue(cid cid.Cid) error { func (q *Queue) Enqueue(cid cid.Cid) error {
q.lock.Lock() q.enqueueLock.Lock()
defer q.lock.Unlock() defer q.enqueueLock.Unlock()
nextKey := q.queueKey(q.tail) nextKey := q.queueKey(q.tail)
if err := q.datastore.Put(nextKey, cid.Bytes()); err != nil { if err := q.ds.Put(nextKey, cid.Bytes()); err != nil {
return err return err
} }
...@@ -126,8 +120,9 @@ func (q *Queue) Run() { ...@@ -126,8 +120,9 @@ func (q *Queue) Run() {
case <-q.ctx.Done(): case <-q.ctx.Done():
return return
case q.dequeue <- entry: case q.dequeue <- entry:
q.head++
err = q.ds.Delete(entry.key)
} }
} }
}() }()
} }
...@@ -135,12 +130,7 @@ func (q *Queue) Run() { ...@@ -135,12 +130,7 @@ func (q *Queue) Run() {
// Find the next item in the queue, crawl forward if an entry is not // Find the next item in the queue, crawl forward if an entry is not
// found in the next spot. // found in the next spot.
func (q *Queue) next() (*Entry, error) { func (q *Queue) next() (*Entry, error) {
q.lock.Lock() var key datastore.Key
defer func() {
q.lock.Unlock()
}()
var nextKey ds.Key
var value []byte var value []byte
var err error var err error
for { for {
...@@ -152,9 +142,12 @@ func (q *Queue) next() (*Entry, error) { ...@@ -152,9 +142,12 @@ func (q *Queue) next() (*Entry, error) {
return nil, nil return nil, nil
default: default:
} }
nextKey = q.queueKey(q.head) key = q.queueKey(q.head)
value, err = q.datastore.Get(nextKey)
if err == ds.ErrNotFound { value, err = q.ds.Get(key)
value, err = q.ds.Get(key)
if err == datastore.ErrNotFound {
q.head++ q.head++
continue continue
} else if err != nil { } else if err != nil {
...@@ -171,21 +164,23 @@ func (q *Queue) next() (*Entry, error) { ...@@ -171,21 +164,23 @@ func (q *Queue) next() (*Entry, error) {
entry := &Entry{ entry := &Entry{
cid: id, cid: id,
key: nextKey, key: key,
queue: q, queue: q,
} }
q.head++ if err != nil {
return nil, err
}
return entry, nil return entry, nil
} }
func (q *Queue) queueKey(id uint64) ds.Key { func (q *Queue) queueKey(id uint64) datastore.Key {
return ds.NewKey(strconv.FormatUint(id, 10)) return datastore.NewKey(strconv.FormatUint(id, 10))
} }
// crawl over the queue entries to find the head and tail // crawl over the queue entries to find the head and tail
func getQueueHeadTail(ctx context.Context, name string, datastore ds.Datastore) (uint64, uint64, error) { func getQueueHeadTail(ctx context.Context, name string, datastore datastore.Datastore) (uint64, uint64, error) {
q := query.Query{} q := query.Query{}
results, err := datastore.Query(q) results, err := datastore.Query(q)
if err != nil { if err != nil {
...@@ -223,7 +218,3 @@ func getQueueHeadTail(ctx context.Context, name string, datastore ds.Datastore) ...@@ -223,7 +218,3 @@ func getQueueHeadTail(ctx context.Context, name string, datastore ds.Datastore)
return head, tail, nil return head, tail, nil
} }
func (q *Queue) remove(key ds.Key) error {
return q.datastore.Delete(key)
}
package provider
import (
"context"
"testing"
"time"
cid "github.com/ipfs/go-cid"
datastore "github.com/ipfs/go-datastore"
sync "github.com/ipfs/go-datastore/sync"
)
func makeCids(n int) []cid.Cid {
cids := make([]cid.Cid, 0, 10)
for i := 0; i < 10; i++ {
c := blockGenerator.Next().Cid()
cids = append(cids, c)
}
return cids
}
func assertOrdered(cids []cid.Cid, q *Queue, t *testing.T) {
for _, c := range cids {
select {
case dequeued := <- q.dequeue:
if c != dequeued.cid {
t.Fatalf("Error in ordering of CIDs retrieved from queue. Expected: %s, got: %s", c, dequeued.cid)
}
case <-time.After(time.Second * 1):
t.Fatal("Timeout waiting for cids to be provided.")
}
}
}
func TestBasicOperation(t *testing.T) {
ctx := context.Background()
defer ctx.Done()
ds := sync.MutexWrap(datastore.NewMapDatastore())
queue, err := NewQueue(ctx, "test", ds)
if err != nil {
t.Fatal(err)
}
queue.Run()
cids := makeCids(10)
for _, c := range cids {
err = queue.Enqueue(c)
if err != nil {
t.Fatal("Failed to enqueue CID")
}
}
assertOrdered(cids, queue, t)
}
func TestInitialization(t *testing.T) {
ctx := context.Background()
defer ctx.Done()
ds := sync.MutexWrap(datastore.NewMapDatastore())
queue, err := NewQueue(ctx, "test", ds)
if err != nil {
t.Fatal(err)
}
queue.Run()
cids := makeCids(10)
for _, c := range cids {
err = queue.Enqueue(c)
if err != nil {
t.Fatal("Failed to enqueue CID")
}
}
assertOrdered(cids[:5], queue, t)
// make a new queue, same data
queue, err = NewQueue(ctx, "test", ds)
if err != nil {
t.Fatal(err)
}
queue.Run()
assertOrdered(cids[5:], queue, t)
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论