Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce datetime calls in peer discovery by factor of 100 #5751

Merged
merged 13 commits into from
Jun 3, 2023
8 changes: 7 additions & 1 deletion src/Nethermind/Nethermind.Core/Crypto/PublicKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class PublicKey : IEquatable<PublicKey>
private Address? _address;

private byte[]? _prefixedBytes;
private int _hashCode;

public PublicKey(string? hexString)
: this(Core.Extensions.Bytes.FromHexString(hexString ?? throw new ArgumentNullException(nameof(hexString))))
Expand All @@ -37,6 +38,7 @@ public PublicKey(ReadOnlySpan<byte> bytes)
}

Bytes = bytes.Slice(bytes.Length - 64, 64).ToArray();
_hashCode = GetHashCode(Bytes);
}

public Address Address
Expand Down Expand Up @@ -97,7 +99,11 @@ public override bool Equals(object? obj)

public override int GetHashCode()
{
byte[] bytes = Bytes;
return _hashCode;
}

private static int GetHashCode(byte[] bytes)
{
long l0 = Unsafe.ReadUnaligned<long>(ref MemoryMarshal.GetArrayDataReference(bytes));
long l1 = Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(bytes), sizeof(long)));
long l2 = Unsafe.ReadUnaligned<long>(ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(bytes), sizeof(long) * 2));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Nethermind.Network.Benchmarks
{
public class NodeStatsCtorBenchmarks
{
private DateTime _now = DateTime.UtcNow;
private Node _node;

[GlobalSetup]
Expand All @@ -35,7 +36,7 @@ public void Light()
public long LightRep()
{
NodeStatsLight stats = new NodeStatsLight(_node);
return stats.CurrentNodeReputation;
return stats.CurrentNodeReputation(_now);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void Is_bonded_at_start()
{
Node node = new(TestItem.PublicKeyA, IPAddress.Loopback.ToString(), 30000);
NodeBucketItem nodeBucketItem = new(node, DateTime.UtcNow);
nodeBucketItem.IsBonded.Should().BeTrue();
nodeBucketItem.IsBonded(DateTime.UtcNow).Should().BeTrue();
}

[Test]
Expand Down
3 changes: 2 additions & 1 deletion src/Nethermind/Nethermind.Network.Discovery/DiscoveryApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -500,9 +500,10 @@ private async Task RunDiscoveryPersistenceCommit()
try
{
IReadOnlyCollection<INodeLifecycleManager> managers = _discoveryManager.GetNodeLifecycleManagers();
DateTime utcNow = DateTime.UtcNow;
//we need to update all notes to update reputation
_discoveryStorage.UpdateNodes(managers.Select(x => new NetworkNode(x.ManagedNode.Id, x.ManagedNode.Host,
x.ManagedNode.Port, x.NodeStats.NewPersistedNodeReputation)).ToArray());
x.ManagedNode.Port, x.NodeStats.NewPersistedNodeReputation(utcNow))).ToArray());

if (!_discoveryStorage.AnyPendingChange())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public void OnIncomingMsg(DiscoveryMsg msg)
manager.OnStateChanged += ManagerOnOnStateChanged;
if (!isPersisted)
{
_discoveryStorage.UpdateNodes(new[] { new NetworkNode(manager.ManagedNode.Id, manager.ManagedNode.Host, manager.ManagedNode.Port, manager.NodeStats.NewPersistedNodeReputation) });
_discoveryStorage.UpdateNodes(new[] { new NetworkNode(manager.ManagedNode.Id, manager.ManagedNode.Host, manager.ManagedNode.Port, manager.NodeStats.NewPersistedNodeReputation(DateTime.UtcNow)) });
}

return manager;
Expand Down Expand Up @@ -280,9 +280,9 @@ private void CleanUpLifecycleManagers()
}
}
}

DateTime utcNow = DateTime.UtcNow;
foreach ((Keccak key, INodeLifecycleManager value) in _nodeLifecycleManagers.ToArray()
.OrderBy(x => x.Value.NodeStats.CurrentNodeReputation))
.OrderBy(x => x.Value.NodeStats.CurrentNodeReputation(utcNow)))
{
if (RemoveManager((key, value.ManagedNode.Id)))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ public IEnumerable<NodeBucketItem> BondedItems
lock (_nodeBucketLock)
{
LinkedListNode<NodeBucketItem>? node = _items.Last;
DateTime utcNow = DateTime.UtcNow;
while (node is not null)
{
if (!node.Value.IsBonded)
if (!node.Value.IsBonded(utcNow))
{
break;
}
Expand All @@ -55,9 +56,10 @@ public int BondedItemsCount
{
int result = _items.Count;
LinkedListNode<NodeBucketItem>? node = _items.Last;
DateTime utcNow = DateTime.UtcNow;
while (node is not null)
{
if (node.Value.IsBonded)
if (node.Value.IsBonded(utcNow))
{
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public NodeBucketItem(Node? node, DateTime lastContactTime)

public DateTime LastContactTime { get; private set; }

public bool IsBonded => LastContactTime > DateTime.UtcNow - TimeSpan.FromDays(2);
public bool IsBonded(DateTime utcNow) => LastContactTime > utcNow - TimeSpan.FromDays(2);

public void OnContactReceived()
{
Expand Down
9 changes: 6 additions & 3 deletions src/Nethermind/Nethermind.Network.Stats/INodeStats.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;

using Nethermind.Stats.Model;

namespace Nethermind.Stats
Expand All @@ -19,11 +21,12 @@ public interface INodeStats

void AddTransferSpeedCaptureEvent(TransferSpeedType speedType, long bytesPerMillisecond);
long? GetAverageTransferSpeed(TransferSpeedType speedType);
(bool Result, NodeStatsEventType? DelayReason) IsConnectionDelayed();
(bool Result, NodeStatsEventType? DelayReason) IsConnectionDelayed(DateTime nowUTC);

long CurrentNodeReputation { get; }
long CurrentNodeReputation() => CurrentNodeReputation(DateTime.UtcNow);
long CurrentNodeReputation(DateTime nowUTC);
long CurrentPersistedNodeReputation { get; set; }
long NewPersistedNodeReputation { get; }
long NewPersistedNodeReputation(DateTime nowUTC);

P2PNodeDetails P2PNodeDetails { get; }
SyncPeerNodeDetails EthNodeDetails { get; }
Expand Down
4 changes: 1 addition & 3 deletions src/Nethermind/Nethermind.Network.Stats/Model/Node.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public sealed class Node : IFormattable, IEquatable<Node>
private string _clientId;
private string _paddedHost;
private string _paddedPort;
private int _hashCode;

/// <summary>
/// Node public key - same as in enode.
Expand Down Expand Up @@ -85,7 +84,6 @@ public Node(PublicKey id, IPEndPoint address, bool isStatic = false)
{
Id = id;
IdHash = Keccak.Compute(Id.PrefixedBytes);
_hashCode = id.GetHashCode();
IsStatic = isStatic;
SetIPEndPoint(address);
}
Expand Down Expand Up @@ -121,7 +119,7 @@ public override bool Equals(object obj)
return false;
}

public override int GetHashCode() => _hashCode;
public override int GetHashCode() => Id.GetHashCode();

public override string ToString() => ToString(Format.WithPublicKey);

Expand Down
47 changes: 24 additions & 23 deletions src/Nethermind/Nethermind.Network.Stats/NodeStatsLight.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class NodeStatsLight : INodeStats
private DateTime? _lastDisconnectTime;
private DateTime? _lastFailedConnectionTime;

private (DateTimeOffset, NodeStatsEventType) _delayConnectDeadline = (DateTimeOffset.Now - TimeSpan.FromSeconds(1), NodeStatsEventType.None);
private (DateTime, NodeStatsEventType) _delayConnectDeadline = (DateTime.UtcNow - TimeSpan.FromSeconds(1), NodeStatsEventType.None);

private static readonly Random Random = new();

Expand All @@ -55,11 +55,11 @@ public NodeStatsLight(Node node, decimal latestSpeedWeight = 0.25m)
Node = node;
}

public long CurrentNodeReputation => CalculateCurrentReputation();
public long CurrentNodeReputation(DateTime nowUTC) => CalculateCurrentReputation(nowUTC);

public long CurrentPersistedNodeReputation { get; set; }

public long NewPersistedNodeReputation => IsReputationPenalized() ? -100 : (CurrentPersistedNodeReputation + CalculateSessionReputation()) / 2;
public long NewPersistedNodeReputation(DateTime nowUTC) => IsReputationPenalized(nowUTC) ? -100 : (CurrentPersistedNodeReputation + CalculateSessionReputation()) / 2;

public P2PNodeDetails P2PNodeDetails { get; private set; }

Expand All @@ -85,7 +85,7 @@ public void AddNodeStatsEvent(NodeStatsEventType nodeStatsEventType)

if (_statsParameters.DelayDueToEvent.TryGetValue(nodeStatsEventType, out TimeSpan delay))
{
UpdateDelayConnectDeadline(delay, nodeStatsEventType);
UpdateDelayConnectDeadline(DateTime.UtcNow, delay, nodeStatsEventType);
}

Increment(nodeStatsEventType);
Expand All @@ -98,7 +98,8 @@ public void AddNodeStatsHandshakeEvent(ConnectionDirection connectionDirection)

public void AddNodeStatsDisconnectEvent(DisconnectType disconnectType, DisconnectReason disconnectReason)
{
_lastDisconnectTime = DateTime.UtcNow;
DateTime nowUTC = DateTime.UtcNow;
_lastDisconnectTime = nowUTC;
if (disconnectType == DisconnectType.Local)
{
_lastLocalDisconnect = disconnectReason;
Expand All @@ -112,24 +113,24 @@ public void AddNodeStatsDisconnectEvent(DisconnectType disconnectType, Disconnec
{
if (_statsParameters.DelayDueToLocalDisconnect.TryGetValue(disconnectReason, out TimeSpan delay))
{
UpdateDelayConnectDeadline(delay, NodeStatsEventType.LocalDisconnectDelay);
UpdateDelayConnectDeadline(nowUTC, delay, NodeStatsEventType.LocalDisconnectDelay);
}
}
else if (disconnectType == DisconnectType.Remote)
{
if (_statsParameters.DelayDueToRemoteDisconnect.TryGetValue(disconnectReason, out TimeSpan delay))
{
UpdateDelayConnectDeadline(delay, NodeStatsEventType.RemoteDisconnectDelay);
UpdateDelayConnectDeadline(nowUTC, delay, NodeStatsEventType.RemoteDisconnectDelay);
}
}

Increment(NodeStatsEventType.Disconnect);
}

private void UpdateDelayConnectDeadline(TimeSpan delay, NodeStatsEventType reason)
private void UpdateDelayConnectDeadline(DateTime nowUTC, TimeSpan delay, NodeStatsEventType reason)
{
DateTimeOffset newDeadline = DateTimeOffset.Now + delay;
(DateTimeOffset currentDeadline, NodeStatsEventType _) = _delayConnectDeadline;
DateTime newDeadline = nowUTC + delay;
(DateTime currentDeadline, NodeStatsEventType _) = _delayConnectDeadline;
if (newDeadline > currentDeadline)
{
_delayConnectDeadline = (newDeadline, reason);
Expand Down Expand Up @@ -212,35 +213,35 @@ private void UpdateValue(ref decimal? currentValue, decimal newValue)
});
}

public (bool Result, NodeStatsEventType? DelayReason) IsConnectionDelayed()
public (bool Result, NodeStatsEventType? DelayReason) IsConnectionDelayed(DateTime nowUTC)
{
if (IsDelayedDueToDisconnect())
if (IsDelayedDueToDisconnect(nowUTC))
{
return (true, NodeStatsEventType.Disconnect);
}

if (IsDelayedDueToFailedConnection())
if (IsDelayedDueToFailedConnection(nowUTC))
{
return (true, NodeStatsEventType.ConnectionFailed);
}

(DateTimeOffset outgoingDelayDeadline, NodeStatsEventType reason) = _delayConnectDeadline;
if (outgoingDelayDeadline > DateTime.Now)
(DateTime outgoingDelayDeadline, NodeStatsEventType reason) = _delayConnectDeadline;
if (outgoingDelayDeadline > nowUTC)
{
return (true, reason);
}

return (false, null);
}

private bool IsDelayedDueToDisconnect()
private bool IsDelayedDueToDisconnect(DateTime nowUTC)
{
if (!_lastDisconnectTime.HasValue)
{
return false;
}

double timePassed = DateTime.UtcNow.Subtract(_lastDisconnectTime.Value).TotalMilliseconds;
double timePassed = nowUTC.Subtract(_lastDisconnectTime.Value).TotalMilliseconds;
int disconnectDelay = GetDisconnectDelay();
if (disconnectDelay <= 500)
{
Expand All @@ -257,14 +258,14 @@ private bool IsDelayedDueToDisconnect()
return result;
}

private bool IsDelayedDueToFailedConnection()
private bool IsDelayedDueToFailedConnection(DateTime nowUTC)
{
if (!_lastFailedConnectionTime.HasValue)
{
return false;
}

double timePassed = DateTime.UtcNow.Subtract(_lastFailedConnectionTime.Value).TotalMilliseconds;
double timePassed = nowUTC.Subtract(_lastFailedConnectionTime.Value).TotalMilliseconds;
int failedConnectionDelay = GetFailedConnectionDelay();
bool result = timePassed < failedConnectionDelay;

Expand Down Expand Up @@ -309,9 +310,9 @@ private int GetDisconnectDelay()
return disconnectDelay;
}

private long CalculateCurrentReputation()
private long CalculateCurrentReputation(DateTime nowUTC)
{
return IsReputationPenalized() ? -100 : CurrentPersistedNodeReputation / 2 + CalculateSessionReputation();
return IsReputationPenalized(nowUTC) ? -100 : CurrentPersistedNodeReputation / 2 + CalculateSessionReputation();
}

private bool HasDisconnectedOnce => _lastLocalDisconnect.HasValue || _lastRemoteDisconnect.HasValue;
Expand Down Expand Up @@ -374,7 +375,7 @@ private long CalculateSessionReputation()
return discoveryReputation + 100 * rlpxReputation;
}

private bool IsReputationPenalized()
private bool IsReputationPenalized(DateTime nowUTC)
{
if (!HasDisconnectedOnce)
{
Expand All @@ -399,7 +400,7 @@ private bool IsReputationPenalized()
{
if (_lastRemoteDisconnect == DisconnectReason.TooManyPeers || _lastRemoteDisconnect == DisconnectReason.AlreadyConnected)
{
double timeFromLastDisconnect = DateTime.UtcNow.Subtract(_lastDisconnectTime ?? DateTime.MinValue).TotalMilliseconds;
double timeFromLastDisconnect = nowUTC.Subtract(_lastDisconnectTime ?? DateTime.MinValue).TotalMilliseconds;
return timeFromLastDisconnect < _statsParameters.PenalizedReputationTooManyPeersTimeout;
}

Expand Down
9 changes: 5 additions & 4 deletions src/Nethermind/Nethermind.Network.Stats/NodeStatsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ private void CleanupTimerOnElapsed(object sender, EventArgs e)

if (deleteCount > 0)
{
DateTime utcNow = DateTime.UtcNow;
IEnumerable<Node> toDelete = _nodeStats
.OrderBy(n => n.Value.CurrentNodeReputation)
.OrderBy(n => n.Value.CurrentNodeReputation(utcNow))
.Select(n => n.Key)
.Take(_nodeStats.Count - _maxCount);

Expand Down Expand Up @@ -102,7 +103,7 @@ public void ReportEvent(Node node, NodeStatsEventType eventType)
public (bool Result, NodeStatsEventType? DelayReason) IsConnectionDelayed(Node node)
{
INodeStats stats = GetOrAdd(node);
return stats.IsConnectionDelayed();
return stats.IsConnectionDelayed(DateTime.UtcNow);
}

public CompatibilityValidationType? FindCompatibilityValidationResult(Node node)
Expand All @@ -114,7 +115,7 @@ public void ReportEvent(Node node, NodeStatsEventType eventType)
public long GetCurrentReputation(Node node)
{
INodeStats stats = GetOrAdd(node);
return stats.CurrentNodeReputation;
return stats.CurrentNodeReputation(DateTime.UtcNow);
}

public void ReportP2PInitializationEvent(Node node, P2PNodeDetails p2PNodeDetails)
Expand Down Expand Up @@ -149,7 +150,7 @@ public void ReportDisconnect(Node node, DisconnectType disconnectType, Disconnec
public long GetNewPersistedReputation(Node node)
{
INodeStats stats = GetOrAdd(node);
return stats.NewPersistedNodeReputation;
return stats.NewPersistedNodeReputation(DateTime.UtcNow);
}

public long GetCurrentPersistedReputation(Node node)
Expand Down
Loading