mirror of
https://github.com/sot-tech/mochi.git
synced 2026-07-13 20:18:09 -07:00
(done) replace redigo with go-redis
* replace redis keys with RawString encoded values (delete SerializedPeer) * merge peers got from pre-hools with store data
This commit is contained in:
+33
-30
@@ -186,8 +186,8 @@ type peerShard struct {
|
||||
|
||||
type swarm struct {
|
||||
// map serialized peer to mtime
|
||||
seeders map[storage.SerializedPeer]int64
|
||||
leechers map[storage.SerializedPeer]int64
|
||||
seeders map[string]int64
|
||||
leechers map[string]int64
|
||||
}
|
||||
|
||||
type store struct {
|
||||
@@ -214,7 +214,7 @@ func (ps *store) populateProm() {
|
||||
s.RUnlock()
|
||||
}
|
||||
|
||||
storage.PromInfohashesCount.Set(float64(numInfohashes))
|
||||
storage.PromInfoHashesCount.Set(float64(numInfohashes))
|
||||
storage.PromSeedersCount.Set(float64(numSeeders))
|
||||
storage.PromLeechersCount.Set(float64(numLeechers))
|
||||
}
|
||||
@@ -246,15 +246,15 @@ func (ps *store) PutSeeder(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
default:
|
||||
}
|
||||
|
||||
pk := storage.NewSerializedPeer(p)
|
||||
pk := p.RawString()
|
||||
|
||||
shard := ps.shards[ps.shardIndex(ih, p.IP.AddressFamily)]
|
||||
shard.Lock()
|
||||
|
||||
if _, ok := shard.swarms[ih]; !ok {
|
||||
shard.swarms[ih] = swarm{
|
||||
seeders: make(map[storage.SerializedPeer]int64),
|
||||
leechers: make(map[storage.SerializedPeer]int64),
|
||||
seeders: make(map[string]int64),
|
||||
leechers: make(map[string]int64),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ func (ps *store) DeleteSeeder(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
default:
|
||||
}
|
||||
|
||||
pk := storage.NewSerializedPeer(p)
|
||||
pk := p.RawString()
|
||||
|
||||
shard := ps.shards[ps.shardIndex(ih, p.IP.AddressFamily)]
|
||||
shard.Lock()
|
||||
@@ -310,15 +310,15 @@ func (ps *store) PutLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
default:
|
||||
}
|
||||
|
||||
pk := storage.NewSerializedPeer(p)
|
||||
pk := p.RawString()
|
||||
|
||||
shard := ps.shards[ps.shardIndex(ih, p.IP.AddressFamily)]
|
||||
shard.Lock()
|
||||
|
||||
if _, ok := shard.swarms[ih]; !ok {
|
||||
shard.swarms[ih] = swarm{
|
||||
seeders: make(map[storage.SerializedPeer]int64),
|
||||
leechers: make(map[storage.SerializedPeer]int64),
|
||||
seeders: make(map[string]int64),
|
||||
leechers: make(map[string]int64),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ func (ps *store) DeleteLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) error
|
||||
default:
|
||||
}
|
||||
|
||||
pk := storage.NewSerializedPeer(p)
|
||||
pk := p.RawString()
|
||||
|
||||
shard := ps.shards[ps.shardIndex(ih, p.IP.AddressFamily)]
|
||||
shard.Lock()
|
||||
@@ -374,15 +374,15 @@ func (ps *store) GraduateLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) erro
|
||||
default:
|
||||
}
|
||||
|
||||
pk := storage.NewSerializedPeer(p)
|
||||
pk := p.RawString()
|
||||
|
||||
shard := ps.shards[ps.shardIndex(ih, p.IP.AddressFamily)]
|
||||
shard.Lock()
|
||||
|
||||
if _, ok := shard.swarms[ih]; !ok {
|
||||
shard.swarms[ih] = swarm{
|
||||
seeders: make(map[storage.SerializedPeer]int64),
|
||||
leechers: make(map[storage.SerializedPeer]int64),
|
||||
seeders: make(map[string]int64),
|
||||
leechers: make(map[string]int64),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,8 +426,8 @@ func (ps *store) AnnouncePeers(ih bittorrent.InfoHash, seeder bool, numWant int,
|
||||
if numWant == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
peers = append(peers, pk.ToPeer())
|
||||
p, _ := bittorrent.NewPeer(pk)
|
||||
peers = append(peers, p)
|
||||
numWant--
|
||||
}
|
||||
} else {
|
||||
@@ -437,15 +437,15 @@ func (ps *store) AnnouncePeers(ih bittorrent.InfoHash, seeder bool, numWant int,
|
||||
if numWant == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
peers = append(peers, pk.ToPeer())
|
||||
p, _ := bittorrent.NewPeer(pk)
|
||||
peers = append(peers, p)
|
||||
numWant--
|
||||
}
|
||||
|
||||
// Append leechers until we reach numWant.
|
||||
if numWant > 0 {
|
||||
leechers := shard.swarms[ih].leechers
|
||||
announcerPK := storage.NewSerializedPeer(announcer)
|
||||
announcerPK := announcer.RawString()
|
||||
for pk := range leechers {
|
||||
if pk == announcerPK {
|
||||
continue
|
||||
@@ -454,8 +454,8 @@ func (ps *store) AnnouncePeers(ih bittorrent.InfoHash, seeder bool, numWant int,
|
||||
if numWant == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
peers = append(peers, pk.ToPeer())
|
||||
p, _ := bittorrent.NewPeer(pk)
|
||||
peers = append(peers, p)
|
||||
numWant--
|
||||
}
|
||||
}
|
||||
@@ -500,38 +500,40 @@ func asKey(in any) any {
|
||||
return fmt.Sprint(in)
|
||||
}
|
||||
|
||||
func (ps *store) Put(ctx string, key, value any) {
|
||||
func (ps *store) Put(ctx string, value storage.Entry) error {
|
||||
m, _ := ps.contexts.LoadOrStore(ctx, new(sync.Map))
|
||||
m.(*sync.Map).Store(asKey(key), value)
|
||||
m.(*sync.Map).Store(value.Key, value.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *store) Contains(ctx string, key any) bool {
|
||||
func (ps *store) Contains(ctx string, key string) (bool, error) {
|
||||
var exist bool
|
||||
if m, found := ps.contexts.Load(ctx); found {
|
||||
_, exist = m.(*sync.Map).Load(asKey(key))
|
||||
}
|
||||
return exist
|
||||
return exist, nil
|
||||
}
|
||||
|
||||
func (ps *store) BulkPut(ctx string, pairs ...storage.Pair) {
|
||||
func (ps *store) BulkPut(ctx string, pairs ...storage.Entry) error {
|
||||
if len(pairs) > 0 {
|
||||
c, _ := ps.contexts.LoadOrStore(ctx, new(sync.Map))
|
||||
m := c.(*sync.Map)
|
||||
for _, p := range pairs {
|
||||
m.Store(asKey(p.Left), p.Right)
|
||||
m.Store(asKey(p.Key), p.Value)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *store) Load(ctx string, key any) any {
|
||||
func (ps *store) Load(ctx string, key string) (any, error) {
|
||||
var v any
|
||||
if m, found := ps.contexts.Load(ctx); found {
|
||||
v, _ = m.(*sync.Map).Load(asKey(key))
|
||||
}
|
||||
return v
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (ps *store) Delete(ctx string, keys ...any) {
|
||||
func (ps *store) Delete(ctx string, keys ...string) error {
|
||||
if len(keys) > 0 {
|
||||
if m, found := ps.contexts.Load(ctx); found {
|
||||
m := m.(*sync.Map)
|
||||
@@ -540,6 +542,7 @@ func (ps *store) Delete(ctx string, keys ...any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// collectGarbage deletes all Peers from the Storage which are older than the
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/sot-tech/mochi/bittorrent"
|
||||
)
|
||||
|
||||
// Pair - some key-value pair, used for BulkPut
|
||||
type Pair struct {
|
||||
Left, Right any
|
||||
}
|
||||
|
||||
// SerializedPeer concatenation of PeerID, net port and IP-address
|
||||
type SerializedPeer string
|
||||
|
||||
// NewSerializedPeer builds SerializedPeer from bittorrent.Peer
|
||||
func NewSerializedPeer(p bittorrent.Peer) SerializedPeer {
|
||||
b := make([]byte, bittorrent.PeerIDLen+2+len(p.IP.IP))
|
||||
copy(b[:bittorrent.PeerIDLen], p.ID[:])
|
||||
binary.BigEndian.PutUint16(b[bittorrent.PeerIDLen:bittorrent.PeerIDLen+2], p.Port)
|
||||
copy(b[bittorrent.PeerIDLen+2:], p.IP.IP)
|
||||
|
||||
return SerializedPeer(b)
|
||||
}
|
||||
|
||||
// ToPeer parses SerializedPeer to bittorrent.Peer
|
||||
func (pk SerializedPeer) ToPeer() bittorrent.Peer {
|
||||
peerID, err := bittorrent.NewPeerID([]byte(pk[:bittorrent.PeerIDLen]))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
peer := bittorrent.Peer{
|
||||
ID: peerID,
|
||||
Port: binary.BigEndian.Uint16([]byte(pk[bittorrent.PeerIDLen : bittorrent.PeerIDLen+2])),
|
||||
IP: bittorrent.IP{IP: net.IP(pk[bittorrent.PeerIDLen+2:])},
|
||||
}
|
||||
|
||||
if ip := peer.IP.To4(); ip != nil {
|
||||
peer.IP.IP = ip
|
||||
peer.IP.AddressFamily = bittorrent.IPv4
|
||||
} else if len(peer.IP.IP) == net.IPv6len { // implies toReturn.IP.To4() == nil
|
||||
peer.IP.AddressFamily = bittorrent.IPv6
|
||||
} else {
|
||||
panic("IP is neither v4 nor v6")
|
||||
}
|
||||
|
||||
return peer
|
||||
}
|
||||
|
||||
func (pk SerializedPeer) String() string {
|
||||
return string(pk)
|
||||
}
|
||||
@@ -6,7 +6,7 @@ func init() {
|
||||
// Register the metrics.
|
||||
prometheus.MustRegister(
|
||||
PromGCDurationMilliseconds,
|
||||
PromInfohashesCount,
|
||||
PromInfoHashesCount,
|
||||
PromSeedersCount,
|
||||
PromLeechersCount,
|
||||
)
|
||||
@@ -16,29 +16,29 @@ var (
|
||||
// PromGCDurationMilliseconds is a histogram used by storage to record the
|
||||
// durations of execution time required for removing expired peers.
|
||||
PromGCDurationMilliseconds = prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Name: "chihaya_storage_gc_duration_milliseconds",
|
||||
Name: "mochi_storage_gc_duration_milliseconds",
|
||||
Help: "The time it takes to perform storage garbage collection",
|
||||
Buckets: prometheus.ExponentialBuckets(9.375, 2, 10),
|
||||
})
|
||||
|
||||
// PromInfohashesCount is a gauge used to hold the current total amount of
|
||||
// PromInfoHashesCount is a gauge used to hold the current total amount of
|
||||
// unique swarms being tracked by a storage.
|
||||
PromInfohashesCount = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "chihaya_storage_infohashes_count",
|
||||
PromInfoHashesCount = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "mochi_storage_infohashes_count",
|
||||
Help: "The number of Infohashes tracked",
|
||||
})
|
||||
|
||||
// PromSeedersCount is a gauge used to hold the current total amount of
|
||||
// unique seeders per swarm.
|
||||
PromSeedersCount = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "chihaya_storage_seeders_count",
|
||||
Name: "mochi_storage_seeders_count",
|
||||
Help: "The number of seeders tracked",
|
||||
})
|
||||
|
||||
// PromLeechersCount is a gauge used to hold the current total amount of
|
||||
// unique leechers per swarm.
|
||||
PromLeechersCount = prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "chihaya_storage_leechers_count",
|
||||
Name: "mochi_storage_leechers_count",
|
||||
Help: "The number of leechers tracked",
|
||||
})
|
||||
)
|
||||
|
||||
+188
-172
@@ -55,6 +55,7 @@ const (
|
||||
defaultConnectTimeout = time.Second * 15
|
||||
)
|
||||
|
||||
// ErrSentinelAndClusterChecked returned from initializer if both Config.Sentinel and Config.Cluster provided
|
||||
var ErrSentinelAndClusterChecked = errors.New("unable to use both cluster and sentinel mode")
|
||||
|
||||
func init() {
|
||||
@@ -118,7 +119,6 @@ func (cfg Config) LogFields() log.Fields {
|
||||
//
|
||||
// This function warns to the logger when a value is changed.
|
||||
func (cfg Config) Validate() (Config, error) {
|
||||
|
||||
if cfg.Sentinel && cfg.Cluster {
|
||||
return cfg, ErrSentinelAndClusterChecked
|
||||
}
|
||||
@@ -203,7 +203,7 @@ func (cfg Config) Validate() (Config, error) {
|
||||
func connect(cfg Config) (*store, error) {
|
||||
var err error
|
||||
db := &store{
|
||||
// FIXME: get context from parent
|
||||
// FIXME: get context from parent and put into GC, middleware functions should use own ctx
|
||||
ctx: context.TODO(),
|
||||
}
|
||||
switch {
|
||||
@@ -243,8 +243,8 @@ func connect(cfg Config) (*store, error) {
|
||||
if err = db.con.Ping(db.ctx).Err(); err == nil && !errors.Is(err, redis.Nil) {
|
||||
err = nil
|
||||
} else {
|
||||
db = nil
|
||||
_ = db.con.Close()
|
||||
db = nil
|
||||
}
|
||||
return db, err
|
||||
}
|
||||
@@ -287,9 +287,7 @@ func (ps *store) runGC(gcInterval, peerLifeTime time.Duration) {
|
||||
case <-time.After(gcInterval):
|
||||
before := time.Now().Add(-peerLifeTime)
|
||||
log.Debug("storage: purging peers with no announces since", log.Fields{"before": before})
|
||||
if err := ps.collectGarbage(before); err != nil {
|
||||
log.Error("storage: collectGarbage error", log.Fields{"before": before, "error": err})
|
||||
}
|
||||
ps.collectGarbage(before)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -321,39 +319,44 @@ type store struct {
|
||||
|
||||
var groups = []string{bittorrent.IPv4.String(), bittorrent.IPv6.String()}
|
||||
|
||||
func leecherInfohashKey(af, ih string) string {
|
||||
return af + "_L_" + ih
|
||||
// leecherInfoHashKey generates string IPvN_L_hash
|
||||
func leecherInfoHashKey(addressFamily, infoHash string) string {
|
||||
return addressFamily + "_L_" + infoHash
|
||||
}
|
||||
|
||||
func seederInfohashKey(af, ih string) string {
|
||||
return af + "_S_" + ih
|
||||
// seederInfoHashKey generates string IPvN_S_hash
|
||||
func seederInfoHashKey(addressFamily, infoHash string) string {
|
||||
return addressFamily + "_S_" + infoHash
|
||||
}
|
||||
|
||||
func infohashCountKey(af string) string {
|
||||
return af + "_infohash_count"
|
||||
// seederInfoHashKey generates string IPvN_infohash_count
|
||||
func infoHashCountKey(addressFamily string) string {
|
||||
return addressFamily + "_infohash_count"
|
||||
}
|
||||
|
||||
func seederCountKey(af string) string {
|
||||
return af + "_S_count"
|
||||
// seederInfoHashKey generates string IPvN_L_count
|
||||
func leecherCountKey(addressFamily string) string {
|
||||
return addressFamily + "_L_count"
|
||||
}
|
||||
|
||||
func leecherCountKey(af string) string {
|
||||
return af + "_L_count"
|
||||
// seederInfoHashKey generates string IPvN_S_count
|
||||
func seederCountKey(addressFamily string) string {
|
||||
return addressFamily + "_S_count"
|
||||
}
|
||||
|
||||
// populateProm aggregates metrics over all groups and then posts them to
|
||||
// prometheus.
|
||||
func (ps *store) populateProm() {
|
||||
var numInfohashes, numSeeders, numLeechers int64
|
||||
var numInfoHashes, numSeeders, numLeechers int64
|
||||
|
||||
for _, group := range groups {
|
||||
if n, err := ps.con.Get(ps.ctx, infohashCountKey(group)).Int64(); err != nil && !errors.Is(err, redis.Nil) {
|
||||
if n, err := ps.con.Get(ps.ctx, infoHashCountKey(group)).Int64(); err != nil && !errors.Is(err, redis.Nil) {
|
||||
log.Error("storage: GET counter failure", log.Fields{
|
||||
"key": infohashCountKey(group),
|
||||
"key": infoHashCountKey(group),
|
||||
"error": err,
|
||||
})
|
||||
} else {
|
||||
numInfohashes += n
|
||||
numInfoHashes += n
|
||||
}
|
||||
if n, err := ps.con.Get(ps.ctx, seederCountKey(group)).Int64(); err != nil && !errors.Is(err, redis.Nil) {
|
||||
log.Error("storage: GET counter failure", log.Fields{
|
||||
@@ -373,7 +376,7 @@ func (ps *store) populateProm() {
|
||||
}
|
||||
}
|
||||
|
||||
storage.PromInfohashesCount.Set(float64(numInfohashes))
|
||||
storage.PromInfoHashesCount.Set(float64(numInfoHashes))
|
||||
storage.PromSeedersCount.Set(float64(numSeeders))
|
||||
storage.PromLeechersCount.Set(float64(numLeechers))
|
||||
}
|
||||
@@ -386,7 +389,7 @@ func (ps *store) tx(txf func(tx redis.Pipeliner) error) (err error) {
|
||||
if pipe, txErr := ps.con.TxPipelined(ps.ctx, txf); txErr == nil {
|
||||
errs := make([]string, 0)
|
||||
for _, c := range pipe {
|
||||
if err := c.Err(); err == nil {
|
||||
if err := c.Err(); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
}
|
||||
}
|
||||
@@ -409,16 +412,15 @@ func asNil(err error) error {
|
||||
func (ps *store) PutSeeder(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
addressFamily := p.IP.AddressFamily.String()
|
||||
log.Debug("storage: PutSeeder", log.Fields{
|
||||
"InfoHash": ih.String(),
|
||||
"InfoHash": ih,
|
||||
"Peer": p,
|
||||
})
|
||||
|
||||
peerKey := storage.NewSerializedPeer(p).String()
|
||||
encodedSeederInfoHash := seederInfohashKey(addressFamily, ih.String())
|
||||
encodedSeederInfoHash := seederInfoHashKey(addressFamily, ih.RawString())
|
||||
now := ps.getClock()
|
||||
|
||||
return ps.tx(func(tx redis.Pipeliner) (err error) {
|
||||
if err = tx.HSet(ps.ctx, encodedSeederInfoHash, peerKey, now).Err(); err != nil {
|
||||
if err = tx.HSet(ps.ctx, encodedSeederInfoHash, p.RawString(), now).Err(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = ps.con.Incr(ps.ctx, seederCountKey(addressFamily)).Err(); err != nil {
|
||||
@@ -427,7 +429,7 @@ func (ps *store) PutSeeder(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
if err = ps.con.HSet(ps.ctx, addressFamily, encodedSeederInfoHash, now).Err(); err != nil {
|
||||
return
|
||||
}
|
||||
err = ps.con.Incr(ps.ctx, infohashCountKey(addressFamily)).Err()
|
||||
err = ps.con.Incr(ps.ctx, infoHashCountKey(addressFamily)).Err()
|
||||
return
|
||||
})
|
||||
}
|
||||
@@ -435,13 +437,12 @@ func (ps *store) PutSeeder(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
func (ps *store) DeleteSeeder(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
addressFamily := p.IP.AddressFamily.String()
|
||||
log.Debug("storage: DeleteSeeder", log.Fields{
|
||||
"InfoHash": ih.String(),
|
||||
"InfoHash": ih,
|
||||
"Peer": p,
|
||||
})
|
||||
|
||||
peerKey := storage.NewSerializedPeer(p).String()
|
||||
encodedSeederInfoHash := seederInfohashKey(addressFamily, ih.String())
|
||||
deleted, err := ps.con.HDel(ps.ctx, encodedSeederInfoHash, peerKey).Uint64()
|
||||
encodedSeederInfoHash := seederInfoHashKey(addressFamily, ih.RawString())
|
||||
deleted, err := ps.con.HDel(ps.ctx, encodedSeederInfoHash, p.RawString()).Uint64()
|
||||
err = asNil(err)
|
||||
if err == nil {
|
||||
if deleted == 0 {
|
||||
@@ -457,17 +458,16 @@ func (ps *store) DeleteSeeder(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
func (ps *store) PutLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
addressFamily := p.IP.AddressFamily.String()
|
||||
log.Debug("storage: PutLeecher", log.Fields{
|
||||
"InfoHash": ih.String(),
|
||||
"InfoHash": ih,
|
||||
"Peer": p,
|
||||
})
|
||||
|
||||
// Update the peer in the swarm.
|
||||
encodedLeecherInfoHash := leecherInfohashKey(addressFamily, ih.String())
|
||||
peerKey := storage.NewSerializedPeer(p).String()
|
||||
encodedLeecherInfoHash := leecherInfoHashKey(addressFamily, ih.RawString())
|
||||
now := ps.getClock()
|
||||
|
||||
return ps.tx(func(tx redis.Pipeliner) (err error) {
|
||||
if err = tx.HSet(ps.ctx, encodedLeecherInfoHash, peerKey, now).Err(); err != nil {
|
||||
if err = tx.HSet(ps.ctx, encodedLeecherInfoHash, p.RawString(), now).Err(); err != nil {
|
||||
return
|
||||
}
|
||||
if err = tx.HSet(ps.ctx, addressFamily, encodedLeecherInfoHash, now).Err(); err != nil {
|
||||
@@ -481,14 +481,13 @@ func (ps *store) PutLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
func (ps *store) DeleteLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
addressFamily := p.IP.AddressFamily.String()
|
||||
log.Debug("storage: DeleteLeecher", log.Fields{
|
||||
"InfoHash": ih.String(),
|
||||
"InfoHash": ih,
|
||||
"Peer": p,
|
||||
})
|
||||
|
||||
peerKey := storage.NewSerializedPeer(p).String()
|
||||
encodedLeecherInfoHash := leecherInfohashKey(addressFamily, ih.String())
|
||||
encodedLeecherInfoHash := leecherInfoHashKey(addressFamily, ih.RawString())
|
||||
|
||||
deleted, err := ps.con.HDel(ps.ctx, encodedLeecherInfoHash, peerKey).Uint64()
|
||||
deleted, err := ps.con.HDel(ps.ctx, encodedLeecherInfoHash, p.RawString()).Uint64()
|
||||
err = asNil(err)
|
||||
if err == nil {
|
||||
if deleted == 0 {
|
||||
@@ -504,14 +503,14 @@ func (ps *store) DeleteLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) error
|
||||
func (ps *store) GraduateLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) error {
|
||||
addressFamily := p.IP.AddressFamily.String()
|
||||
log.Debug("storage: GraduateLeecher", log.Fields{
|
||||
"InfoHash": ih.String(),
|
||||
"InfoHash": ih,
|
||||
"Peer": p,
|
||||
})
|
||||
|
||||
encodedInfoHash := ih.String()
|
||||
encodedLeecherInfoHash := leecherInfohashKey(addressFamily, encodedInfoHash)
|
||||
encodedSeederInfoHash := seederInfohashKey(addressFamily, encodedInfoHash)
|
||||
peerKey := storage.NewSerializedPeer(p).String()
|
||||
encodedInfoHash := ih.RawString()
|
||||
encodedLeecherInfoHash := leecherInfoHashKey(addressFamily, encodedInfoHash)
|
||||
encodedSeederInfoHash := seederInfoHashKey(addressFamily, encodedInfoHash)
|
||||
peerKey := p.RawString()
|
||||
now := ps.getClock()
|
||||
|
||||
return ps.tx(func(tx redis.Pipeliner) error {
|
||||
@@ -532,7 +531,7 @@ func (ps *store) GraduateLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) erro
|
||||
err = tx.HSet(ps.ctx, addressFamily, encodedSeederInfoHash, now).Err()
|
||||
}
|
||||
if err == nil {
|
||||
err = tx.Incr(ps.ctx, infohashCountKey(addressFamily)).Err()
|
||||
err = tx.Incr(ps.ctx, infoHashCountKey(addressFamily)).Err()
|
||||
}
|
||||
return err
|
||||
})
|
||||
@@ -541,15 +540,15 @@ func (ps *store) GraduateLeecher(ih bittorrent.InfoHash, p bittorrent.Peer) erro
|
||||
func (ps *store) AnnouncePeers(ih bittorrent.InfoHash, seeder bool, numWant int, announcer bittorrent.Peer) (peers []bittorrent.Peer, err error) {
|
||||
addressFamily := announcer.IP.AddressFamily.String()
|
||||
log.Debug("storage: AnnouncePeers", log.Fields{
|
||||
"InfoHash": ih.String(),
|
||||
"InfoHash": ih,
|
||||
"seeder": seeder,
|
||||
"numWant": numWant,
|
||||
"Peer": announcer,
|
||||
})
|
||||
|
||||
encodedInfoHash := ih.String()
|
||||
encodedLeecherInfoHash := leecherInfohashKey(addressFamily, encodedInfoHash)
|
||||
encodedSeederInfoHash := seederInfohashKey(addressFamily, encodedInfoHash)
|
||||
encodedInfoHash := ih.RawString()
|
||||
encodedLeecherInfoHash := leecherInfoHashKey(addressFamily, encodedInfoHash)
|
||||
encodedSeederInfoHash := seederInfoHashKey(addressFamily, encodedInfoHash)
|
||||
|
||||
leechers, err := ps.con.HKeys(ps.ctx, encodedLeecherInfoHash).Result()
|
||||
err = asNil(err)
|
||||
@@ -573,9 +572,12 @@ func (ps *store) AnnouncePeers(ih bittorrent.InfoHash, seeder bool, numWant int,
|
||||
if numWant == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
peers = append(peers, storage.SerializedPeer(peerKey).ToPeer())
|
||||
numWant--
|
||||
if p, err := bittorrent.NewPeer(peerKey); err == nil {
|
||||
peers = append(peers, p)
|
||||
numWant--
|
||||
} else {
|
||||
log.Error("storage: unable to decode leecher", log.Fields{"peer": peerKey})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Append as many seeders as possible.
|
||||
@@ -583,22 +585,28 @@ func (ps *store) AnnouncePeers(ih bittorrent.InfoHash, seeder bool, numWant int,
|
||||
if numWant == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
peers = append(peers, storage.SerializedPeer(peerKey).ToPeer())
|
||||
numWant--
|
||||
if p, err := bittorrent.NewPeer(peerKey); err == nil {
|
||||
peers = append(peers, p)
|
||||
numWant--
|
||||
} else {
|
||||
log.Error("storage: unable to decode seeder", log.Fields{"peer": peerKey})
|
||||
}
|
||||
}
|
||||
|
||||
// Append leechers until we reach numWant.
|
||||
if numWant > 0 {
|
||||
announcerPK := storage.NewSerializedPeer(announcer).String()
|
||||
announcerPK := announcer.RawString()
|
||||
for _, peerKey := range leechers {
|
||||
if peerKey != announcerPK {
|
||||
if numWant == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
peers = append(peers, storage.SerializedPeer(peerKey).ToPeer())
|
||||
numWant--
|
||||
if p, err := bittorrent.NewPeer(peerKey); err == nil {
|
||||
peers = append(peers, p)
|
||||
numWant--
|
||||
} else {
|
||||
log.Error("storage: unable to decode leecher", log.Fields{"peer": peerKey})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -610,9 +618,9 @@ func (ps *store) AnnouncePeers(ih bittorrent.InfoHash, seeder bool, numWant int,
|
||||
func (ps *store) ScrapeSwarm(ih bittorrent.InfoHash, af bittorrent.AddressFamily) (resp bittorrent.Scrape) {
|
||||
resp.InfoHash = ih
|
||||
addressFamily := af.String()
|
||||
encodedInfoHash := ih.String()
|
||||
encodedLeecherInfoHash := leecherInfohashKey(addressFamily, encodedInfoHash)
|
||||
encodedSeederInfoHash := seederInfohashKey(addressFamily, encodedInfoHash)
|
||||
encodedInfoHash := ih.RawString()
|
||||
encodedLeecherInfoHash := leecherInfoHashKey(addressFamily, encodedInfoHash)
|
||||
encodedSeederInfoHash := seederInfoHashKey(addressFamily, encodedInfoHash)
|
||||
|
||||
leechersLen, err := ps.con.HLen(ps.ctx, encodedLeecherInfoHash).Result()
|
||||
err = asNil(err)
|
||||
@@ -640,70 +648,61 @@ func (ps *store) ScrapeSwarm(ih bittorrent.InfoHash, af bittorrent.AddressFamily
|
||||
return
|
||||
}
|
||||
|
||||
func (ps *store) Put(ctx string, key, value any) {
|
||||
err := ps.con.HSet(ps.ctx, ctx, key, value).Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
func (ps *store) Put(ctx string, value storage.Entry) error {
|
||||
return ps.con.HSet(ps.ctx, ctx, value.Key, value.Value).Err()
|
||||
}
|
||||
|
||||
func (ps *store) Contains(ctx string, key any) bool {
|
||||
func (ps *store) Contains(ctx string, key string) (bool, error) {
|
||||
exist, err := ps.con.HExists(ps.ctx, ctx, key).Result()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return exist
|
||||
return exist, asNil(err)
|
||||
}
|
||||
|
||||
const argNumErrorMsg = "ERR wrong number of arguments"
|
||||
|
||||
func (ps *store) BulkPut(ctx string, pairs ...storage.Pair) {
|
||||
func (ps *store) BulkPut(ctx string, pairs ...storage.Entry) (err error) {
|
||||
if l := len(pairs); l > 0 {
|
||||
args := make([]any, 0, l*2)
|
||||
for _, p := range pairs {
|
||||
args = append(args, p.Left, p.Right)
|
||||
args = append(args, p.Key, p.Value)
|
||||
}
|
||||
err := ps.con.HSet(ps.ctx, ctx, args...).Err()
|
||||
err = ps.con.HSet(ps.ctx, ctx, args...).Err()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), argNumErrorMsg) {
|
||||
log.Warn("This REDIS version/implementation does not support variadic arguments for HSET")
|
||||
for _, p := range pairs {
|
||||
if err := ps.con.HSet(ps.ctx, ctx, p.Left, p.Right).Err(); err != nil {
|
||||
panic(err)
|
||||
if err = ps.con.HSet(ps.ctx, ctx, p.Key, p.Value).Err(); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ps *store) Load(ctx string, key any) any {
|
||||
v, err := ps.con.HGet(ps.ctx, ctx, key).Result()
|
||||
err = asNil(err)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
func (ps *store) Load(ctx string, key string) (v any, err error) {
|
||||
v, err = ps.con.HGet(ps.ctx, ctx, key).Result()
|
||||
if err != nil && errors.Is(err, redis.Nil) {
|
||||
v, err = nil, nil
|
||||
}
|
||||
return v
|
||||
return
|
||||
}
|
||||
|
||||
func (ps *store) Delete(ctx string, keys ...any) {
|
||||
func (ps *store) Delete(ctx string, keys ...string) (err error) {
|
||||
if len(keys) > 0 {
|
||||
err := asNil(ps.con.HDel(ps.ctx, keys...).Err())
|
||||
err = asNil(ps.con.HDel(ps.ctx, ctx, keys...).Err())
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), argNumErrorMsg) {
|
||||
log.Warn("This REDIS version/implementation does not support variadic arguments for HDEL")
|
||||
for _, k := range keys {
|
||||
if err := asNil(ps.con.HDel(ps.ctx, ctx, k).Err()); err != nil {
|
||||
panic(err)
|
||||
if err = asNil(ps.con.HDel(ps.ctx, ctx, k).Err()); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// collectGarbage deletes all Peers from the Storage which are older than the
|
||||
@@ -751,108 +750,125 @@ func (ps *store) Delete(ctx string, keys ...any) {
|
||||
// - If the change happens after the HLEN, we will not even attempt to make the
|
||||
// transaction. The infohash key will remain in the addressFamil hash and
|
||||
// we'll attempt to clean it up the next time collectGarbage runs.
|
||||
func (ps *store) collectGarbage(cutoff time.Time) error {
|
||||
func (ps *store) collectGarbage(cutoff time.Time) {
|
||||
cutoffUnix := cutoff.UnixNano()
|
||||
start := time.Now()
|
||||
|
||||
var err error
|
||||
for _, group := range groups {
|
||||
// list all infohashes in the group
|
||||
infohashesList, err := ps.con.HKeys(ps.ctx, group).Result()
|
||||
// list all infoHashes in the group
|
||||
var infoHashes []string
|
||||
infoHashes, err = ps.con.HKeys(ps.ctx, group).Result()
|
||||
err = asNil(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ihStr := range infohashesList {
|
||||
isSeeder := len(ihStr) > 5 && ihStr[5:6] == "S"
|
||||
|
||||
// list all (peer, timeout) pairs for the ih
|
||||
ihList, err := ps.con.HGetAll(ps.ctx, ihStr).Result()
|
||||
err = asNil(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var removedPeerCount int64
|
||||
for k, v := range ihList {
|
||||
peerKey := storage.SerializedPeer(k)
|
||||
mtime, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if mtime <= cutoffUnix {
|
||||
log.Debug("storage: deleting peer", log.Fields{
|
||||
"Peer": peerKey.ToPeer(),
|
||||
})
|
||||
ret, err := ps.con.HDel(ps.ctx, ihStr, peerKey.String()).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
if err == nil {
|
||||
for _, infoHash := range infoHashes {
|
||||
isSeeder := len(infoHash) > 5 && infoHash[5:6] == "S"
|
||||
// list all (peer, timeout) pairs for the ih
|
||||
peerList, err := ps.con.HGetAll(ps.ctx, infoHash).Result()
|
||||
err = asNil(err)
|
||||
if err == nil {
|
||||
var removedPeerCount int64
|
||||
for peerKey, timeStamp := range peerList {
|
||||
var peer bittorrent.Peer
|
||||
if peer, err = bittorrent.NewPeer(peerKey); err == nil {
|
||||
if mtime, err := strconv.ParseInt(timeStamp, 10, 64); err == nil {
|
||||
if mtime <= cutoffUnix {
|
||||
log.Debug("storage: deleting peer", log.Fields{
|
||||
"Peer": peer,
|
||||
})
|
||||
var count int64
|
||||
count, err = ps.con.HDel(ps.ctx, infoHash, peerKey).Result()
|
||||
err = asNil(err)
|
||||
if err == nil {
|
||||
removedPeerCount += count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("storage: Redis: unable to delete info hash peer", log.Fields{
|
||||
"group": group,
|
||||
"infoHash": infoHash,
|
||||
"peer": peer,
|
||||
"key": peerKey,
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
}
|
||||
// DECR seeder/leecher counter
|
||||
if removedPeerCount > 0 {
|
||||
var decrCounter string
|
||||
if isSeeder {
|
||||
decrCounter = seederCountKey(group)
|
||||
} else {
|
||||
decrCounter = leecherCountKey(group)
|
||||
}
|
||||
if err := ps.con.DecrBy(ps.ctx, decrCounter, removedPeerCount).Err(); err != nil {
|
||||
log.Error("storage: Redis: unable to decrement seeder/leecher peer count", log.Fields{
|
||||
"group": group,
|
||||
"infoHash": infoHash,
|
||||
"key": decrCounter,
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
removedPeerCount += ret
|
||||
}
|
||||
}
|
||||
// DECR seeder/leecher counter
|
||||
decrCounter := leecherCountKey(group)
|
||||
if isSeeder {
|
||||
decrCounter = seederCountKey(group)
|
||||
}
|
||||
if removedPeerCount > 0 {
|
||||
if err := ps.con.DecrBy(ps.ctx, decrCounter, removedPeerCount).Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// use WATCH to avoid race condition
|
||||
// https://redis.io/topics/transactions
|
||||
_, err = ps.con.Watch(ps.ctx, ihStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ihLen, err := ps.con.HLen(ps.ctx, ihStr).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ihLen == 0 {
|
||||
// Empty hashes are not shown among existing keys,
|
||||
// in other words, it's removed automatically after `HDEL` the last field.
|
||||
// _, err := ps.con.Del(ps.ctx, ihStr)
|
||||
|
||||
_ = ps.con.Multi(ps.ctx)
|
||||
_ = ps.con.HDel(ps.ctx, group, ihStr)
|
||||
if isSeeder {
|
||||
_ = ps.con.Decr(ps.ctx, infohashCountKey(group))
|
||||
}
|
||||
_, err = redis.Values(ps.con.Exec(ps.ctx))
|
||||
if err != nil && !errors.Is(err, redis.Nil) {
|
||||
log.Error("storage: Redis EXEC failure", log.Fields{
|
||||
// use WATCH to avoid race condition
|
||||
// https://redis.io/topics/transactions
|
||||
err = asNil(ps.con.Watch(ps.ctx, func(tx *redis.Tx) (err error) {
|
||||
var infoHashCount int64
|
||||
infoHashCount, err = ps.con.HLen(ps.ctx, infoHash).Result()
|
||||
err = asNil(err)
|
||||
if err == nil && infoHashCount == 0 {
|
||||
// Empty hashes are not shown among existing keys,
|
||||
// in other words, it's removed automatically after `HDEL` the last field.
|
||||
// _, err := ps.con.Del(ps.ctx, infoHash)
|
||||
var deletedCount int64
|
||||
deletedCount, err = ps.con.HDel(ps.ctx, group, infoHash).Result()
|
||||
err = asNil(err)
|
||||
if err == nil && isSeeder && deletedCount > 0 {
|
||||
err = ps.con.Decr(ps.ctx, infoHashCountKey(group)).Err()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}, infoHash))
|
||||
if err != nil {
|
||||
log.Error("storage: Redis: unable to clean info hash records", log.Fields{
|
||||
"group": group,
|
||||
"infoHash": infoHash,
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
log.Error("storage: Redis: unable to fetch info hash peers", log.Fields{
|
||||
"group": group,
|
||||
"infohash": ihStr,
|
||||
"infoHash": infoHash,
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if _, err = ps.con.Unwatch(ps.ctx); err != nil && !errors.Is(err, redis.Nil) {
|
||||
log.Error("storage: Redis UNWATCH failure", log.Fields{"error": err})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Error("storage: Redis: unable to fetch info hashes", log.Fields{"group": group, "error": err})
|
||||
}
|
||||
}
|
||||
|
||||
duration := float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond)
|
||||
duration := time.Since(start).Milliseconds()
|
||||
log.Debug("storage: recordGCDuration", log.Fields{"timeTaken(ms)": duration})
|
||||
storage.PromGCDurationMilliseconds.Observe(duration)
|
||||
|
||||
return nil
|
||||
storage.PromGCDurationMilliseconds.Observe(float64(duration))
|
||||
}
|
||||
|
||||
func (ps *store) Stop() stop.Result {
|
||||
c := make(stop.Channel)
|
||||
go func() {
|
||||
close(ps.closed)
|
||||
if ps.closed != nil {
|
||||
close(ps.closed)
|
||||
}
|
||||
ps.wg.Wait()
|
||||
log.Info("storage: exiting. mochi does not clear data in redis when exiting. mochi keys have prefix 'IPv{4,6}_'.")
|
||||
c.Done()
|
||||
var err error
|
||||
if ps.con != nil {
|
||||
log.Info("storage: exiting. mochi does not clear data in redis when exiting. mochi keys have prefix 'IPv{4,6}_'.")
|
||||
err = ps.con.Close()
|
||||
}
|
||||
c.Done(err)
|
||||
}()
|
||||
|
||||
return c.Result()
|
||||
|
||||
@@ -11,21 +11,29 @@ import (
|
||||
"github.com/sot-tech/mochi/storage/test"
|
||||
)
|
||||
|
||||
var conf = Config{
|
||||
GarbageCollectionInterval: 10 * time.Minute,
|
||||
PrometheusReportingInterval: 10 * time.Minute,
|
||||
PeerLifetime: 30 * time.Minute,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
ConnectTimeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
func createNew() s.Storage {
|
||||
rs, err := miniredis.Run()
|
||||
var ps s.Storage
|
||||
var err error
|
||||
ps, err = New(conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
fmt.Println("unable to create real Redis connection: ", err, " using simulator")
|
||||
var rs *miniredis.Miniredis
|
||||
rs, err = miniredis.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
conf.Addresses = []string{rs.Addr()}
|
||||
ps, err = New(conf)
|
||||
}
|
||||
redisURL := fmt.Sprintf("redis://@%s/0", rs.Addr())
|
||||
ps, err := New(Config{
|
||||
GarbageCollectionInterval: 10 * time.Minute,
|
||||
PrometheusReportingInterval: 10 * time.Minute,
|
||||
PeerLifetime: 30 * time.Minute,
|
||||
RedisBroker: redisURL,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
ConnectTimeout: 10 * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
+11
-5
@@ -14,6 +14,12 @@ var (
|
||||
drivers = make(map[string]Driver)
|
||||
)
|
||||
|
||||
// Entry - some key-value pair, used for BulkPut
|
||||
type Entry struct {
|
||||
Key string
|
||||
Value any
|
||||
}
|
||||
|
||||
// Driver is the interface used to initialize a new type of Storage.
|
||||
type Driver interface {
|
||||
NewStorage(cfg any) (Storage, error)
|
||||
@@ -107,20 +113,20 @@ type Storage interface {
|
||||
// Put used to place arbitrary k-v data with specified context
|
||||
// into storage. ctx parameter used to group data
|
||||
// (i.e. data only for specific middleware module)
|
||||
Put(ctx string, key, value any)
|
||||
Put(ctx string, value Entry) error
|
||||
|
||||
// BulkPut used to place array of k-v data in specified context.
|
||||
// Useful when several data entries should be added in single transaction/connection
|
||||
BulkPut(ctx string, pairs ...Pair)
|
||||
BulkPut(ctx string, pairs ...Entry) error
|
||||
|
||||
// Contains checks if any data in specified context exist
|
||||
Contains(ctx string, key any) bool
|
||||
Contains(ctx string, key string) (bool, error)
|
||||
|
||||
// Load used to get arbitrary data in specified context by its key
|
||||
Load(ctx string, key any) any
|
||||
Load(ctx string, key string) (any, error)
|
||||
|
||||
// Delete used to delete arbitrary data in specified context by its keys
|
||||
Delete(ctx string, keys ...any)
|
||||
Delete(ctx string, keys ...string) error
|
||||
|
||||
// Stopper is an interface that expects a Stop method to stop the Storage.
|
||||
// For more details see the documentation in the stop package.
|
||||
|
||||
@@ -17,7 +17,7 @@ type benchData struct {
|
||||
peers [1000]bittorrent.Peer
|
||||
}
|
||||
|
||||
func generateInfohashes() (a [1000]bittorrent.InfoHash) {
|
||||
func generateInfoHashes() (a [1000]bittorrent.InfoHash) {
|
||||
for i := range a {
|
||||
b := make([]byte, bittorrent.InfoHashV1Len)
|
||||
rand.Read(b)
|
||||
@@ -63,7 +63,7 @@ type benchHolder struct {
|
||||
|
||||
func (bh *benchHolder) runBenchmark(b *testing.B, parallel bool, sf benchSetupFunc, ef benchExecFunc) {
|
||||
ps := bh.st()
|
||||
bd := &benchData{generateInfohashes(), generatePeers()}
|
||||
bd := &benchData{generateInfoHashes(), generatePeers()}
|
||||
spacing := int32(1000 / runtime.NumCPU())
|
||||
if sf != nil {
|
||||
err := sf(ps, bd)
|
||||
@@ -134,21 +134,21 @@ func (bh *benchHolder) Put1k(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// Put1kInfohash benchmarks the PutSeeder method of a storage.Storage by cycling
|
||||
// Put1kInfoHash benchmarks the PutSeeder method of a storage.Storage by cycling
|
||||
// through 1000 infohashes and putting the same peer into their swarms.
|
||||
//
|
||||
// Put1kInfohash can run in parallel.
|
||||
func (bh *benchHolder) Put1kInfohash(b *testing.B) {
|
||||
// Put1kInfoHash can run in parallel.
|
||||
func (bh *benchHolder) Put1kInfoHash(b *testing.B) {
|
||||
bh.runBenchmark(b, true, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
return ps.PutSeeder(bd.infohashes[i%1000], bd.peers[0])
|
||||
})
|
||||
}
|
||||
|
||||
// Put1kInfohash1k benchmarks the PutSeeder method of a storage.Storage by cycling
|
||||
// Put1kInfoHash1k benchmarks the PutSeeder method of a storage.Storage by cycling
|
||||
// through 1000 infohashes and 1000 Peers and calling Put with them.
|
||||
//
|
||||
// Put1kInfohash1k can run in parallel.
|
||||
func (bh *benchHolder) Put1kInfohash1k(b *testing.B) {
|
||||
// Put1kInfoHash1k can run in parallel.
|
||||
func (bh *benchHolder) Put1kInfoHash1k(b *testing.B) {
|
||||
bh.runBenchmark(b, true, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
err := ps.PutSeeder(bd.infohashes[i%1000], bd.peers[(i*3)%1000])
|
||||
return err
|
||||
@@ -183,11 +183,11 @@ func (bh *benchHolder) PutDelete1k(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// PutDelete1kInfohash behaves like PutDelete1k with 1000 infohashes instead of
|
||||
// PutDelete1kInfoHash behaves like PutDelete1k with 1000 infohashes instead of
|
||||
// 1000 Peers.
|
||||
//
|
||||
// PutDelete1kInfohash can not run in parallel.
|
||||
func (bh *benchHolder) PutDelete1kInfohash(b *testing.B) {
|
||||
// PutDelete1kInfoHash can not run in parallel.
|
||||
func (bh *benchHolder) PutDelete1kInfoHash(b *testing.B) {
|
||||
bh.runBenchmark(b, false, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
err := ps.PutSeeder(bd.infohashes[i%1000], bd.peers[0])
|
||||
if err != nil {
|
||||
@@ -197,11 +197,11 @@ func (bh *benchHolder) PutDelete1kInfohash(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// PutDelete1kInfohash1k behaves like PutDelete1k with 1000 infohashes in
|
||||
// PutDelete1kInfoHash1k behaves like PutDelete1k with 1000 infohashes in
|
||||
// addition to 1000 Peers.
|
||||
//
|
||||
// PutDelete1kInfohash1k can not run in parallel.
|
||||
func (bh *benchHolder) PutDelete1kInfohash1k(b *testing.B) {
|
||||
// PutDelete1kInfoHash1k can not run in parallel.
|
||||
func (bh *benchHolder) PutDelete1kInfoHash1k(b *testing.B) {
|
||||
bh.runBenchmark(b, false, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
err := ps.PutSeeder(bd.infohashes[i%1000], bd.peers[(i*3)%1000])
|
||||
if err != nil {
|
||||
@@ -234,22 +234,22 @@ func (bh *benchHolder) DeleteNonexist1k(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteNonexist1kInfohash benchmarks the DeleteSeeder method of a storage.Storage by
|
||||
// DeleteNonexist1kInfoHash benchmarks the DeleteSeeder method of a storage.Storage by
|
||||
// attempting to delete one Peer from one of 1000 infohashes.
|
||||
//
|
||||
// DeleteNonexist1kInfohash can run in parallel.
|
||||
func (bh *benchHolder) DeleteNonexist1kInfohash(b *testing.B) {
|
||||
// DeleteNonexist1kInfoHash can run in parallel.
|
||||
func (bh *benchHolder) DeleteNonexist1kInfoHash(b *testing.B) {
|
||||
bh.runBenchmark(b, true, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
_ = ps.DeleteSeeder(bd.infohashes[i%1000], bd.peers[0])
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteNonexist1kInfohash1k benchmarks the Delete method of a storage.Storage by
|
||||
// attempting to delete one of 1000 Peers from one of 1000 Infohashes.
|
||||
// DeleteNonexist1kInfoHash1k benchmarks the Delete method of a storage.Storage by
|
||||
// attempting to delete one of 1000 Peers from one of 1000 InfoHashes.
|
||||
//
|
||||
// DeleteNonexist1kInfohash1k can run in parallel.
|
||||
func (bh *benchHolder) DeleteNonexist1kInfohash1k(b *testing.B) {
|
||||
// DeleteNonexist1kInfoHash1k can run in parallel.
|
||||
func (bh *benchHolder) DeleteNonexist1kInfoHash1k(b *testing.B) {
|
||||
bh.runBenchmark(b, true, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
_ = ps.DeleteSeeder(bd.infohashes[i%1000], bd.peers[(i*3)%1000])
|
||||
return nil
|
||||
@@ -278,23 +278,23 @@ func (bh *benchHolder) GradNonexist1k(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// GradNonexist1kInfohash benchmarks the GraduateLeecher method of a storage.Storage
|
||||
// by attempting to graduate a nonexistent Peer for one of 100 Infohashes.
|
||||
// GradNonexist1kInfoHash benchmarks the GraduateLeecher method of a storage.Storage
|
||||
// by attempting to graduate a nonexistent Peer for one of 100 InfoHashes.
|
||||
//
|
||||
// GradNonexist1kInfohash can run in parallel.
|
||||
func (bh *benchHolder) GradNonexist1kInfohash(b *testing.B) {
|
||||
// GradNonexist1kInfoHash can run in parallel.
|
||||
func (bh *benchHolder) GradNonexist1kInfoHash(b *testing.B) {
|
||||
bh.runBenchmark(b, true, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
_ = ps.GraduateLeecher(bd.infohashes[i%1000], bd.peers[0])
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GradNonexist1kInfohash1k benchmarks the GraduateLeecher method of a storage.Storage
|
||||
// GradNonexist1kInfoHash1k benchmarks the GraduateLeecher method of a storage.Storage
|
||||
// by attempting to graduate one of 1000 nonexistent Peers for one of 1000
|
||||
// infohashes.
|
||||
//
|
||||
// GradNonexist1kInfohash1k can run in parallel.
|
||||
func (bh *benchHolder) GradNonexist1kInfohash1k(b *testing.B) {
|
||||
// GradNonexist1kInfoHash1k can run in parallel.
|
||||
func (bh *benchHolder) GradNonexist1kInfoHash1k(b *testing.B) {
|
||||
bh.runBenchmark(b, true, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
_ = ps.GraduateLeecher(bd.infohashes[i%1000], bd.peers[(i*3)%1000])
|
||||
return nil
|
||||
@@ -337,11 +337,11 @@ func (bh *benchHolder) PutGradDelete1k(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// PutGradDelete1kInfohash behaves like PutGradDelete with one of 1000
|
||||
// PutGradDelete1kInfoHash behaves like PutGradDelete with one of 1000
|
||||
// infohashes.
|
||||
//
|
||||
// PutGradDelete1kInfohash can not run in parallel.
|
||||
func (bh *benchHolder) PutGradDelete1kInfohash(b *testing.B) {
|
||||
// PutGradDelete1kInfoHash can not run in parallel.
|
||||
func (bh *benchHolder) PutGradDelete1kInfoHash(b *testing.B) {
|
||||
bh.runBenchmark(b, false, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
err := ps.PutLeecher(bd.infohashes[i%1000], bd.peers[0])
|
||||
if err != nil {
|
||||
@@ -355,11 +355,11 @@ func (bh *benchHolder) PutGradDelete1kInfohash(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// PutGradDelete1kInfohash1k behaves like PutGradDelete with one of 1000 Peers
|
||||
// PutGradDelete1kInfoHash1k behaves like PutGradDelete with one of 1000 Peers
|
||||
// and one of 1000 infohashes.
|
||||
//
|
||||
// PutGradDelete1kInfohash can not run in parallel.
|
||||
func (bh *benchHolder) PutGradDelete1kInfohash1k(b *testing.B) {
|
||||
// PutGradDelete1kInfoHash can not run in parallel.
|
||||
func (bh *benchHolder) PutGradDelete1kInfoHash1k(b *testing.B) {
|
||||
bh.runBenchmark(b, false, nil, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
err := ps.PutLeecher(bd.infohashes[i%1000], bd.peers[(i*3)%1000])
|
||||
if err != nil {
|
||||
@@ -403,11 +403,11 @@ func (bh *benchHolder) AnnounceLeecher(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// AnnounceLeecher1kInfohash behaves like AnnounceLeecher with one of 1000
|
||||
// AnnounceLeecher1kInfoHash behaves like AnnounceLeecher with one of 1000
|
||||
// infohashes.
|
||||
//
|
||||
// AnnounceLeecher1kInfohash can run in parallel.
|
||||
func (bh *benchHolder) AnnounceLeecher1kInfohash(b *testing.B) {
|
||||
// AnnounceLeecher1kInfoHash can run in parallel.
|
||||
func (bh *benchHolder) AnnounceLeecher1kInfoHash(b *testing.B) {
|
||||
bh.runBenchmark(b, true, putPeers, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
_, err := ps.AnnouncePeers(bd.infohashes[i%1000], false, 50, bd.peers[0])
|
||||
return err
|
||||
@@ -425,11 +425,11 @@ func (bh *benchHolder) AnnounceSeeder(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// AnnounceSeeder1kInfohash behaves like AnnounceSeeder with one of 1000
|
||||
// AnnounceSeeder1kInfoHash behaves like AnnounceSeeder with one of 1000
|
||||
// infohashes.
|
||||
//
|
||||
// AnnounceSeeder1kInfohash can run in parallel.
|
||||
func (bh *benchHolder) AnnounceSeeder1kInfohash(b *testing.B) {
|
||||
// AnnounceSeeder1kInfoHash can run in parallel.
|
||||
func (bh *benchHolder) AnnounceSeeder1kInfoHash(b *testing.B) {
|
||||
bh.runBenchmark(b, true, putPeers, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
_, err := ps.AnnouncePeers(bd.infohashes[i%1000], true, 50, bd.peers[0])
|
||||
return err
|
||||
@@ -447,10 +447,10 @@ func (bh *benchHolder) ScrapeSwarm(b *testing.B) {
|
||||
})
|
||||
}
|
||||
|
||||
// ScrapeSwarm1kInfohash behaves like ScrapeSwarm with one of 1000 infohashes.
|
||||
// ScrapeSwarm1kInfoHash behaves like ScrapeSwarm with one of 1000 infohashes.
|
||||
//
|
||||
// ScrapeSwarm1kInfohash can run in parallel.
|
||||
func (bh *benchHolder) ScrapeSwarm1kInfohash(b *testing.B) {
|
||||
// ScrapeSwarm1kInfoHash can run in parallel.
|
||||
func (bh *benchHolder) ScrapeSwarm1kInfoHash(b *testing.B) {
|
||||
bh.runBenchmark(b, true, putPeers, func(i int, ps storage.Storage, bd *benchData) error {
|
||||
ps.ScrapeSwarm(bd.infohashes[i%1000], bittorrent.IPv4)
|
||||
return nil
|
||||
@@ -463,28 +463,28 @@ func RunBenchmarks(b *testing.B, newStorage benchStorageConstructor) {
|
||||
b.Run("BenchmarkNop", bh.Nop)
|
||||
b.Run("BenchmarkPut", bh.Put)
|
||||
b.Run("BenchmarkPut1k", bh.Put1k)
|
||||
b.Run("BenchmarkPut1kInfohash", bh.Put1kInfohash)
|
||||
b.Run("BenchmarkPut1kInfohash1k", bh.Put1kInfohash1k)
|
||||
b.Run("BenchmarkPut1kInfoHash", bh.Put1kInfoHash)
|
||||
b.Run("BenchmarkPut1kInfoHash1k", bh.Put1kInfoHash1k)
|
||||
b.Run("BenchmarkPutDelete", bh.PutDelete)
|
||||
b.Run("BenchmarkPutDelete1k", bh.PutDelete1k)
|
||||
b.Run("BenchmarkPutDelete1kInfohash", bh.PutDelete1kInfohash)
|
||||
b.Run("BenchmarkPutDelete1kInfohash1k", bh.PutDelete1kInfohash1k)
|
||||
b.Run("BenchmarkPutDelete1kInfoHash", bh.PutDelete1kInfoHash)
|
||||
b.Run("BenchmarkPutDelete1kInfoHash1k", bh.PutDelete1kInfoHash1k)
|
||||
b.Run("BenchmarkDeleteNonexist", bh.DeleteNonexist)
|
||||
b.Run("BenchmarkDeleteNonexist1k", bh.DeleteNonexist1k)
|
||||
b.Run("BenchmarkDeleteNonexist1kInfohash", bh.DeleteNonexist1kInfohash)
|
||||
b.Run("BenchmarkDeleteNonexist1kInfohash1k", bh.DeleteNonexist1kInfohash1k)
|
||||
b.Run("BenchmarkDeleteNonexist1kInfoHash", bh.DeleteNonexist1kInfoHash)
|
||||
b.Run("BenchmarkDeleteNonexist1kInfoHash1k", bh.DeleteNonexist1kInfoHash1k)
|
||||
b.Run("BenchmarkPutGradDelete", bh.PutGradDelete)
|
||||
b.Run("BenchmarkPutGradDelete1k", bh.PutGradDelete1k)
|
||||
b.Run("BenchmarkPutGradDelete1kInfohash", bh.PutGradDelete1kInfohash)
|
||||
b.Run("BenchmarkPutGradDelete1kInfohash1k", bh.PutGradDelete1kInfohash1k)
|
||||
b.Run("BenchmarkPutGradDelete1kInfoHash", bh.PutGradDelete1kInfoHash)
|
||||
b.Run("BenchmarkPutGradDelete1kInfoHash1k", bh.PutGradDelete1kInfoHash1k)
|
||||
b.Run("BenchmarkGradNonexist", bh.GradNonexist)
|
||||
b.Run("BenchmarkGradNonexist1k", bh.GradNonexist1k)
|
||||
b.Run("BenchmarkGradNonexist1kInfohash", bh.GradNonexist1kInfohash)
|
||||
b.Run("BenchmarkGradNonexist1kInfohash1k", bh.GradNonexist1kInfohash1k)
|
||||
b.Run("BenchmarkGradNonexist1kInfoHash", bh.GradNonexist1kInfoHash)
|
||||
b.Run("BenchmarkGradNonexist1kInfoHash1k", bh.GradNonexist1kInfoHash1k)
|
||||
b.Run("BenchmarkAnnounceLeecher", bh.AnnounceLeecher)
|
||||
b.Run("BenchmarkAnnounceLeecher1kInfohash", bh.AnnounceLeecher1kInfohash)
|
||||
b.Run("BenchmarkAnnounceLeecher1kInfoHash", bh.AnnounceLeecher1kInfoHash)
|
||||
b.Run("BenchmarkAnnounceSeeder", bh.AnnounceSeeder)
|
||||
b.Run("BenchmarkAnnounceSeeder1kInfohash", bh.AnnounceSeeder1kInfohash)
|
||||
b.Run("BenchmarkAnnounceSeeder1kInfoHash", bh.AnnounceSeeder1kInfoHash)
|
||||
b.Run("BenchmarkScrapeSwarm", bh.ScrapeSwarm)
|
||||
b.Run("BenchmarkScrapeSwarm1kInfohash", bh.ScrapeSwarm1kInfohash)
|
||||
b.Run("BenchmarkScrapeSwarm1kInfoHash", bh.ScrapeSwarm1kInfoHash)
|
||||
}
|
||||
|
||||
@@ -171,64 +171,75 @@ func (th *testHolder) LeecherPutGraduateAnnounceDeleteAnnounce(t *testing.T) {
|
||||
|
||||
func (th *testHolder) CustomPutContainsLoadDelete(t *testing.T) {
|
||||
for _, c := range testData {
|
||||
th.st.Put("test", c.peer.String(), c.ih.RawString())
|
||||
err := th.st.Put("test", storage.Entry{Key: c.peer.String(), Value: c.ih.RawString()})
|
||||
require.Nil(t, err)
|
||||
|
||||
// check if exist in ctx we put
|
||||
contains := th.st.Contains("test", c.peer.String())
|
||||
contains, err := th.st.Contains("test", c.peer.String())
|
||||
require.Nil(t, err)
|
||||
require.True(t, contains)
|
||||
|
||||
// check if not exist in another ctx
|
||||
contains = th.st.Contains("", c.peer.String())
|
||||
contains, err = th.st.Contains("", c.peer.String())
|
||||
require.Nil(t, err)
|
||||
require.False(t, contains)
|
||||
|
||||
// check value and type in ctx we put
|
||||
out := th.st.Load("test", c.peer.String())
|
||||
out, err := th.st.Load("test", c.peer.String())
|
||||
require.Nil(t, err)
|
||||
ih, err := bittorrent.NewInfoHash(out)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, c.ih, ih)
|
||||
|
||||
// check value is nil in another ctx
|
||||
dummy := th.st.Load("", c.peer.String())
|
||||
dummy, err := th.st.Load("", c.peer.String())
|
||||
require.Nil(t, err)
|
||||
require.Nil(t, dummy)
|
||||
|
||||
th.st.Delete("test", c.peer.String())
|
||||
err = th.st.Delete("test", c.peer.String())
|
||||
require.Nil(t, err)
|
||||
|
||||
contains = th.st.Contains("peers", c.peer.String())
|
||||
contains, err = th.st.Contains("peers", c.peer.String())
|
||||
require.Nil(t, err)
|
||||
require.False(t, contains)
|
||||
}
|
||||
}
|
||||
|
||||
func (th *testHolder) CustomBulkPutContainsLoadDelete(t *testing.T) {
|
||||
pairs := make([]storage.Pair, 0, len(testData))
|
||||
keys := make([]any, 0, len(testData))
|
||||
pairs := make([]storage.Entry, 0, len(testData))
|
||||
keys := make([]string, 0, len(testData))
|
||||
for _, c := range testData {
|
||||
key := c.peer.String()
|
||||
keys = append(keys, key)
|
||||
pairs = append(pairs, storage.Pair{
|
||||
Left: key,
|
||||
Right: c.ih.RawString(),
|
||||
pairs = append(pairs, storage.Entry{
|
||||
Key: key,
|
||||
Value: c.ih.RawString(),
|
||||
})
|
||||
}
|
||||
th.st.BulkPut("test", pairs...)
|
||||
err := th.st.BulkPut("test", pairs...)
|
||||
require.Nil(t, err)
|
||||
|
||||
// check if exist in ctx we put
|
||||
for _, k := range keys {
|
||||
contains := th.st.Contains("test", k)
|
||||
contains, err := th.st.Contains("test", k)
|
||||
require.Nil(t, err)
|
||||
require.True(t, contains)
|
||||
}
|
||||
|
||||
// check value and type in ctx we put
|
||||
for _, p := range pairs {
|
||||
out := th.st.Load("test", p.Left)
|
||||
out, _ := th.st.Load("test", p.Key)
|
||||
ih, err := bittorrent.NewInfoHash(out)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, p.Right, ih.RawString())
|
||||
require.Equal(t, p.Value, ih.RawString())
|
||||
}
|
||||
|
||||
th.st.Delete("test", keys...)
|
||||
err = th.st.Delete("test", keys...)
|
||||
require.Nil(t, err)
|
||||
|
||||
for _, k := range keys {
|
||||
contains := th.st.Contains("test", k)
|
||||
contains, err := th.st.Contains("test", k)
|
||||
require.Nil(t, err)
|
||||
require.False(t, contains)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user