satellite/metainfo/metabase: optimize ConvertAliasesToPieces
old time/op new time/op delta 7.56µs ± 5% 4.93µs ± 2% -34.75% (p=0.000 n=5+15) old alloc/op new alloc/op delta 6.86kB ± 0% 3.85kB ± 0% -43.87% (p=0.000 n=5+18) old allocs/op new allocs/op delta 19.0 ± 0% 17.0 ± 0% -10.53% (p=0.000 n=5+18) Change-Id: Iedf24087766b3bd90934f2daa7ac186c3503a341
This commit is contained in:
parent
67e26aafcd
commit
5e954ad487
@ -163,18 +163,36 @@ func (cache *NodeAliasCache) ConvertPiecesToAliases(ctx context.Context, pieces
|
||||
func (cache *NodeAliasCache) ConvertAliasesToPieces(ctx context.Context, aliasPieces AliasPieces) (_ Pieces, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
aliases := make([]NodeAlias, len(aliasPieces))
|
||||
latest := cache.getLatest()
|
||||
|
||||
pieces := make(Pieces, len(aliasPieces))
|
||||
var missing []NodeAlias
|
||||
|
||||
for i, aliasPiece := range aliasPieces {
|
||||
pieces[i].Number, aliases[i] = aliasPiece.Number, aliasPiece.Alias
|
||||
node, ok := latest.Node(aliasPiece.Alias)
|
||||
if !ok {
|
||||
missing = append(missing, aliasPiece.Alias)
|
||||
continue
|
||||
}
|
||||
pieces[i].Number = aliasPiece.Number
|
||||
pieces[i].StorageNode = node
|
||||
}
|
||||
|
||||
nodes, err := cache.Nodes(ctx, aliases)
|
||||
if err != nil {
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
for i, n := range nodes {
|
||||
pieces[i].StorageNode = n
|
||||
if len(missing) > 0 {
|
||||
var err error
|
||||
latest, err = cache.refresh(ctx, nil, missing)
|
||||
if err != nil {
|
||||
return nil, Error.New("failed to refresh node alias db: %w", err)
|
||||
}
|
||||
|
||||
for i, aliasPiece := range aliasPieces {
|
||||
node, ok := latest.Node(aliasPiece.Alias)
|
||||
if !ok {
|
||||
return nil, Error.New("aliases missing in database: %v", missing)
|
||||
}
|
||||
pieces[i].Number = aliasPiece.Number
|
||||
pieces[i].StorageNode = node
|
||||
}
|
||||
}
|
||||
|
||||
return pieces, nil
|
||||
@ -182,38 +200,57 @@ func (cache *NodeAliasCache) ConvertAliasesToPieces(ctx context.Context, aliasPi
|
||||
|
||||
// NodeAliasMap contains bidirectional mapping between node ID and a NodeAlias.
|
||||
type NodeAliasMap struct {
|
||||
node map[NodeAlias]storj.NodeID
|
||||
node []storj.NodeID
|
||||
alias map[storj.NodeID]NodeAlias
|
||||
}
|
||||
|
||||
// NewNodeAliasMap creates a new alias map from the given entries.
|
||||
func NewNodeAliasMap(entries []NodeAliasEntry) *NodeAliasMap {
|
||||
m := &NodeAliasMap{
|
||||
node: make(map[NodeAlias]storj.NodeID, len(entries)),
|
||||
node: make([]storj.NodeID, len(entries)),
|
||||
alias: make(map[storj.NodeID]NodeAlias, len(entries)),
|
||||
}
|
||||
for _, e := range entries {
|
||||
m.node[e.Alias] = e.ID
|
||||
m.setNode(e.Alias, e.ID)
|
||||
m.alias[e.ID] = e.Alias
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// setNode sets a value in `m.node` and increases the size when necessary.
|
||||
func (m *NodeAliasMap) setNode(alias NodeAlias, value storj.NodeID) {
|
||||
if int(alias) >= len(m.node) {
|
||||
m.node = append(m.node, make([]storj.NodeID, int(alias)-len(m.node)+1)...)
|
||||
}
|
||||
m.node[alias] = value
|
||||
}
|
||||
|
||||
// Merge merges the other map into m.
|
||||
func (m *NodeAliasMap) Merge(other *NodeAliasMap) {
|
||||
for k, v := range other.node {
|
||||
m.node[k] = v
|
||||
if !v.IsZero() {
|
||||
m.setNode(NodeAlias(k), v)
|
||||
}
|
||||
}
|
||||
for k, v := range other.alias {
|
||||
m.alias[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Node returns NodeID for the given alias.
|
||||
func (m *NodeAliasMap) Node(alias NodeAlias) (x storj.NodeID, ok bool) {
|
||||
if int(alias) >= len(m.node) {
|
||||
return storj.NodeID{}, false
|
||||
}
|
||||
v := m.node[alias]
|
||||
return v, !v.IsZero()
|
||||
}
|
||||
|
||||
// Nodes returns NodeID-s for the given aliases and aliases that are not in this map.
|
||||
func (m *NodeAliasMap) Nodes(aliases []NodeAlias) (xs []storj.NodeID, missing []NodeAlias) {
|
||||
xs = make([]storj.NodeID, 0, len(aliases))
|
||||
for _, p := range aliases {
|
||||
if x, ok := m.node[p]; ok {
|
||||
if x, ok := m.Node(p); ok {
|
||||
xs = append(xs, x)
|
||||
} else {
|
||||
missing = append(missing, p)
|
||||
@ -243,7 +280,7 @@ func (m *NodeAliasMap) ContainsAll(nodeIDs []storj.NodeID, nodeAliases []NodeAli
|
||||
}
|
||||
}
|
||||
for _, alias := range nodeAliases {
|
||||
if _, ok := m.node[alias]; !ok {
|
||||
if _, ok := m.Node(alias); !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -255,5 +292,5 @@ func (m *NodeAliasMap) Size() int {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
return len(m.node)
|
||||
return len(m.alias)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package metabase_test
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
@ -188,6 +189,32 @@ func TestNodeAliasMap(t *testing.T) {
|
||||
require.Equal(t, []storj.NodeID{n1, n2, n3}, missing)
|
||||
}
|
||||
|
||||
{
|
||||
aggregate := metabase.NewNodeAliasMap(nil)
|
||||
alpha := metabase.NewNodeAliasMap([]metabase.NodeAliasEntry{
|
||||
{ID: n1, Alias: 1},
|
||||
{ID: n2, Alias: 2},
|
||||
})
|
||||
aggregate.Merge(alpha)
|
||||
beta := metabase.NewNodeAliasMap([]metabase.NodeAliasEntry{
|
||||
{ID: n3, Alias: 5},
|
||||
})
|
||||
aggregate.Merge(beta)
|
||||
|
||||
aliases, missing := aggregate.Aliases([]storj.NodeID{n1, n2, n3})
|
||||
require.Empty(t, missing)
|
||||
require.Equal(t, []metabase.NodeAlias{1, 2, 5}, aliases)
|
||||
|
||||
nodes2, missing2 := aggregate.Nodes([]metabase.NodeAlias{1, 2, 5})
|
||||
require.Empty(t, missing2)
|
||||
require.Equal(t, []storj.NodeID{n1, n2, n3}, nodes2)
|
||||
|
||||
nodes3, missing3 := aggregate.Nodes([]metabase.NodeAlias{3, 4})
|
||||
require.Empty(t, nodes3)
|
||||
require.Equal(t, []metabase.NodeAlias{3, 4}, missing3)
|
||||
|
||||
}
|
||||
|
||||
m := metabase.NewNodeAliasMap([]metabase.NodeAliasEntry{
|
||||
{n1, 1},
|
||||
{n2, 2},
|
||||
@ -264,6 +291,36 @@ func TestNodeAliasMap(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNodeAliasCache_ConvertAliasesToPieces(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
|
||||
aliasDB := &NodeAliasDB{}
|
||||
cache := metabase.NewNodeAliasCache(aliasDB)
|
||||
|
||||
nodeIDs := make([]storj.NodeID, 80)
|
||||
for i := range nodeIDs {
|
||||
nodeIDs[i] = testrand.NodeID()
|
||||
}
|
||||
aliases, err := cache.Aliases(ctx, nodeIDs)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
aliasPieces := make([]metabase.AliasPiece, len(aliases))
|
||||
for i, alias := range aliases {
|
||||
aliasPieces[i] = metabase.AliasPiece{Number: uint16(i), Alias: alias}
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
pieces, err := cache.ConvertAliasesToPieces(ctx, aliasPieces)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
runtime.KeepAlive(pieces)
|
||||
}
|
||||
}
|
||||
|
||||
var _ metabase.NodeAliasDB = (*NodeAliasDB)(nil)
|
||||
|
||||
// NodeAliasDB is an inmemory alias database for testing.
|
||||
|
Loading…
Reference in New Issue
Block a user