go-libp2p-peerstore/pstoreds/peerstore.go
Raúl Kripalani c423e9e997 pstoreds: migrate from strict TTL to GC-based expiry.
Each entry in the address book KV store now represents a full peer
with all its addresses. Expiries and TTLs are kept inline. Records are
serialised with Protobuf.

Housekeeping is performed on retrieve, and via a periodic GC routine.
The architecture mimics a write-through cache, although not strictly.
2018-11-13 16:57:51 +00:00

101 lines
2.2 KiB
Go

package pstoreds
import (
"context"
"time"
base32 "github.com/whyrusleeping/base32"
ds "github.com/ipfs/go-datastore"
query "github.com/ipfs/go-datastore/query"
peer "github.com/libp2p/go-libp2p-peer"
pstore "github.com/libp2p/go-libp2p-peerstore"
)
// Configuration object for the peerstore.
type Options struct {
// The size of the in-memory cache. A value of 0 or lower disables the cache.
CacheSize uint
// Sweep interval to purge expired addresses from the datastore.
GCInterval time.Duration
// Number of times to retry transactional writes.
WriteRetries uint
}
// DefaultOpts returns the default options for a persistent peerstore:
// * Cache size: 1024
// * TTL sweep interval: 1 second
// * WriteRetries: 5
func DefaultOpts() Options {
return Options{
CacheSize: 1024,
GCInterval: 5 * time.Minute,
WriteRetries: 5,
}
}
// NewPeerstore creates a peerstore backed by the provided persistent datastore.
func NewPeerstore(ctx context.Context, store ds.TxnDatastore, opts Options) (pstore.Peerstore, error) {
addrBook, err := NewAddrBook(ctx, store, opts)
if err != nil {
return nil, err
}
keyBook, err := NewKeyBook(ctx, store, opts)
if err != nil {
return nil, err
}
peerMetadata, err := NewPeerMetadata(ctx, store, opts)
if err != nil {
return nil, err
}
ps := pstore.NewPeerstore(keyBook, addrBook, peerMetadata)
return ps, nil
}
// uniquePeerIds extracts and returns unique peer IDs from database keys.
func uniquePeerIds(ds ds.TxnDatastore, prefix ds.Key, extractor func(result query.Result) string) (peer.IDSlice, error) {
var (
q = query.Query{Prefix: prefix.String(), KeysOnly: true}
results query.Results
err error
)
txn, err := ds.NewTransaction(true)
if err != nil {
return nil, err
}
defer txn.Discard()
if results, err = txn.Query(q); err != nil {
log.Error(err)
return nil, err
}
defer results.Close()
idset := make(map[string]struct{})
for result := range results.Next() {
k := extractor(result)
idset[k] = struct{}{}
}
if len(idset) == 0 {
return peer.IDSlice{}, nil
}
ids := make(peer.IDSlice, len(idset))
i := 0
for id := range idset {
pid, _ := base32.RawStdEncoding.DecodeString(id)
ids[i], _ = peer.IDFromBytes(pid)
i++
}
return ids, nil
}