I have many clients where each client is having multiple simultaneous tunnels (data fetching middleware).
I have to manage all the clients with live tunnels of each of the client.
My Tunnel
class is having many properties and functions, I am showing only useful properties As :
public class Tunnel : IEquatable<Tunnel>
{
public Guid UID { get; private set; }
public Tunnel(){
this.UID = Guid.NewGuid(); ;
}
public Tunnel(Guid uid_)
{
this.UID = uid_;
}
public bool Equals(Tunnel other)
{
return other != null && other.UID == this.UID;
}
public override bool Equals(object obj)
{
var other = obj as Tunnel;
return other != null && other.UID == this.UID;
}
public override int GetHashCode()
{
return this.UID.GetHashCode();
}
public static bool operator ==(Tunnel tunnel1, Tunnel tunnel2)
{
if (((object)tunnel1) == null || ((object)tunnel2) == null)
{ return Object.Equals(tunnel1, tunnel2); }
return tunnel1.Equals(tunnel2);
}
public static bool operator !=(Tunnel tunnel1, Tunnel tunnel2)
{
if (((object)tunnel1) == null || ((object)tunnel2) == null)
{ return !Object.Equals(tunnel1, tunnel2); }
return !(tunnel1.Equals(tunnel2));
}
// 10+ other properties
}
I have ClientConnections
class which manages all the clients with their LiveTunnels as :
public class ClientsConnections
{
internal readonly ConcurrentDictionary<Object, Dictionary<Guid, Tunnel>> ClientsSessions;
public ClientsConnections(){
this.ClientsSessions = new ConcurrentDictionary<object, Dictionary<Guid, Tunnel>>();
}
public Tunnel AddOrUpdateClientTunnel(Object ClientID, Tunnel tnl)
{
if (tnl.ClientID == null) { tnl.ClientID = ClientID; }
this.ClientsSessions.AddOrUpdate(ClientID, new Dictionary<Guid, Tunnel>() { { tnl.UID, tnl } }, (oldkey, liveTunnels) =>
{
lock (liveTunnels)
{
if (liveTunnels.ContainsKey(tnl.UID))
{
liveTunnels[tnl.UID] = tnl;
}
else
{
liveTunnels.Add(tnl.UID, tnl);
}
}
return liveTunnels;
});
return tnl;
}
public bool RemoveClientTunnel(Object ClientID, Tunnel tnl)
{
Boolean anyRemoved = false;
// When there is no tunnel i.e. current tunnel is the last in ClientSessions, remove entire key value from Concurrent Dictionary
if(this.ClientsSessions.ContainsKey(ClientID))
{
Dictionary<Guid, Tunnel> removedTunls;
Dictionary<Guid, Tunnel> liveTunls = this.ClientsSessions[ClientID];
lock (liveTunls)
{
if (liveTunls.ContainsKey(tnl.UID))
{
liveTunls.Remove(tnl.UID);
if(!anyRemoved){ anyRemoved = true;}
}
}
if (liveTunls.Count == 0)
{
//No tunnels for this ClientID, remove this client from Concurrent Dictionary
this.ClientsSessions.TryRemove(ClientID, out removedTunls);
if (removedTunls.Count != 0)
{
// Oops There were some Livetunnels, add them back
AddOrUpdateClientTunnelRestore(removedTunls);
}
}
}
return anyRemoved;
}
public bool AddOrUpdateClientTunnelRestore( Dictionary<Guid, Tunnel> tnltoAdd)
{
bool anyAdded = false;
Object ClientId = tnltoAdd[tnltoAdd.Keys.First()].ClientID;
this.ClientsSessions.AddOrUpdate(ClientId, tnltoAdd, (oldkey, liveTunnels) =>
{
lock (liveTunnels)
{
foreach (Tunnel tmpTnl in liveTunnels.Values)
{
if (!liveTunnels.ContainsKey(tmpTnl.UID))
{
liveTunnels.Add(tmpTnl.UID, tmpTnl);
if (!anyAdded) { anyAdded = true; }
}
}
}
return liveTunnels;
});
return anyAdded;
}
}
When there is no LiveTunnel of a client the entire client must be removed from the ConcurrentDictionary
.
Is there any better way to do so, specially in function RemoveClientTunnel
?
Tunnel
: Contains 10+ properties with database connections and socket connection.
For the current scenario, there are around 10,000+ clients and each client have at least 2 to 4 LiveTunnels, on an average 8 to 10 LiveTunnels per client.
Frequency : There are some time duration where the client connection frequencies are high. Eg, At 9:30 Am all clients starts to connect, around 12 Pm clients starts disconnecting (30-50%), around 2 PM clients re-connects, 5PM clients starts disconnecting.
A high traffic starts from 9:30Am. The frequency of tunnel: each clients holds the tunnel at least for 1-2 sec. minimum. If we count the minimum time duration a tunnel holds is minimum 1-2 Sec. There is no max time limit of a tunnel duration. A client can hold any number of tunnels for a very long time duration (18 Hours+)