using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; using ZeroLevel.Models; using ZeroLevel.Services.Collections; namespace ZeroLevel.Network { /* One IPEndpoint binded with one service. Service can have one key, one type, one group. Therefore IPEndpoint can be binded with one key, one type and one group. One key can refer to many IPEndPoints. One type can refer to many IPEndPoints. One group can refer to many IPEndPoints. */ public sealed class ServiceRouteStorage : IServiceRoutesStorage { private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); private Dictionary> _tableByKey = new Dictionary>(); private Dictionary> _tableByGroups = new Dictionary>(); private Dictionary> _tableByTypes = new Dictionary>(); private Dictionary _endpoints = new Dictionary(); public void Set(IPEndPoint endpoint) { var key = $"{endpoint.Address}:{endpoint.Port}"; if (_in_transaction == 1) { TransAppendByKeys(key, endpoint); _tran_endpoints[endpoint] = new string[] { key, null, null }; return; } _lock.EnterWriteLock(); try { if (_endpoints.ContainsKey(endpoint)) { if (_tableByKey.ContainsKey(key)) { return; } RemoveLocked(endpoint); } AppendByKeys(key, endpoint); _endpoints.Add(endpoint, new string[3] { $"{endpoint.Address}:{endpoint.Port}", null, null }); } finally { _lock.ExitWriteLock(); } } public void Set(IEnumerable endpoints) { foreach (var ep in endpoints) { Set(ep); } } public void Set(string key, IPEndPoint endpoint) { key = key.ToUpperInvariant(); if (_in_transaction == 1) { TransAppendByKeys(key, endpoint); _tran_endpoints[endpoint] = new string[] { key, null, null }; return; } _lock.EnterWriteLock(); try { if (_endpoints.ContainsKey(endpoint)) { var exists = _endpoints[endpoint]; if (exists[0] != null && _tableByKey.ContainsKey(exists[0]) && _tableByKey[exists[0]].Count == 1 && _tableByKey[exists[0]].Contains(endpoint)) { return; } RemoveLocked(endpoint); } AppendByKeys(key, endpoint); _endpoints.Add(endpoint, new string[3] { key, null, null }); } finally { _lock.ExitWriteLock(); } } public void Set(string key, IEnumerable endpoints) { key = key.ToUpperInvariant(); if (_in_transaction == 1) { foreach (var endpoint in endpoints) { TransAppendByKeys(key, endpoint); _tran_endpoints[endpoint] = new string[] { key, null, null }; } return; } _lock.EnterWriteLock(); try { if (_tableByKey.ContainsKey(key)) { if (_tableByKey[key].Source.OrderingEquals(endpoints)) { return; } var drop = _tableByKey[key].Source.ToArray(); for (int i = 0; i < drop.Length; i++) { RemoveLocked(drop[i]); } } foreach (var ep in endpoints) { _endpoints.Add(ep, new string[3] { key.ToUpperInvariant(), null, null }); AppendByKeys(key, ep); } } finally { _lock.ExitWriteLock(); } } public void Set(string key, string type, string group, IPEndPoint endpoint) { if (key == null) { key = $"{endpoint.Address}:{endpoint.Port}"; } else { key = key.ToUpperInvariant(); } type = type.ToUpperInvariant(); group = group.ToUpperInvariant(); if (_in_transaction == 1) { TransAppendByKeys(key, endpoint); if (type != null) { TransAppendByType(type, endpoint); } if (group != null) { TransAppendByGroup(group, endpoint); } _tran_endpoints[endpoint] = new string[] { key, type, group }; return; } _lock.EnterWriteLock(); try { RemoveLocked(endpoint); AppendByKeys(key, endpoint); if (type != null) { AppendByType(type, endpoint); } if (group != null) { AppendByGroup(group, endpoint); } _endpoints.Add(endpoint, new string[3] { key.ToUpperInvariant(), type.ToUpperInvariant(), group.ToUpperInvariant() }); } finally { _lock.ExitWriteLock(); } } public void Set(string key, string type, string group, IEnumerable endpoints) { if (_in_transaction == 1) { key = key.ToUpperInvariant(); type = type.ToUpperInvariant(); group = group.ToUpperInvariant(); foreach (var endpoint in endpoints) { TransAppendByKeys(key, endpoint); if (type != null) { TransAppendByType(type, endpoint); } if (group != null) { TransAppendByGroup(group, endpoint); } _tran_endpoints[endpoint] = new string[] { key, type, group }; } return; } foreach (var ep in endpoints) { RemoveLocked(ep); Set(key, type, group, ep); } } public void Remove(IPEndPoint endpoint) { _lock.EnterWriteLock(); try { RemoveLocked(endpoint); } finally { _lock.ExitWriteLock(); } } #region GET public InvokeResult Get(string key) { key = key.ToUpperInvariant(); if (_tableByKey.ContainsKey(key)) { if (_tableByKey[key].MoveNext()) return InvokeResult.Succeeding(_tableByKey[key].Current); } return InvokeResult.Fault($"No endpoints by key '{key}'"); } public InvokeResult> GetAll(string key) { key = key.ToUpperInvariant(); if (_tableByKey.ContainsKey(key)) { if (_tableByKey[key].MoveNext()) return InvokeResult.Succeeding(_tableByKey[key].GetCurrentSeq()); } return InvokeResult.Fault>($"No endpoints by key '{key}'"); } public IEnumerable> GetAll() { return _tableByKey.SelectMany(pair => pair.Value.Source.Select(s => new KeyValuePair(pair.Key, s))); } public InvokeResult GetByType(string type) { type = type.ToUpperInvariant(); if (_tableByTypes.ContainsKey(type)) { if (_tableByTypes[type].MoveNext()) return InvokeResult.Succeeding(_tableByTypes[type].Current); } return InvokeResult.Fault($"No endpoints by type '{type}'"); } public InvokeResult> GetAllByType(string type) { type = type.ToUpperInvariant(); if (_tableByTypes.ContainsKey(type)) { if (_tableByTypes[type].MoveNext()) return InvokeResult.Succeeding(_tableByTypes[type].GetCurrentSeq()); } return InvokeResult.Fault>($"No endpoints by type '{type}'"); } public InvokeResult GetByGroup(string group) { group = group.ToUpperInvariant(); if (_tableByGroups.ContainsKey(group)) { if (_tableByGroups[group].MoveNext()) return InvokeResult.Succeeding(_tableByGroups[group].Current); } return InvokeResult.Fault($"No endpoints by group '{group}'"); } public InvokeResult> GetAllByGroup(string group) { group = group.ToUpperInvariant(); if (_tableByGroups.ContainsKey(group)) { if (_tableByGroups[group].MoveNext()) return InvokeResult.Succeeding(_tableByGroups[group].GetCurrentSeq()); } return InvokeResult.Fault>($"No endpoints by group '{group}'"); } #endregion #region Private private void AppendByKeys(string key, IPEndPoint endpoint) { Append(key, endpoint, _tableByKey); } private void AppendByType(string type, IPEndPoint endpoint) { Append(type, endpoint, _tableByTypes); } private void AppendByGroup(string group, IPEndPoint endpoint) { Append(group, endpoint, _tableByGroups); } private void Append(string key, IPEndPoint value, Dictionary> dict) { if (!dict.ContainsKey(key)) { dict.Add(key, new RoundRobinCollection()); } dict[key].Add(value); } private void RemoveLocked(IPEndPoint endpoint) { if (_endpoints.ContainsKey(endpoint)) { var refs = _endpoints[endpoint]; if (refs[0] != null && _tableByKey.ContainsKey(refs[0])) _tableByKey[refs[0]].Remove(endpoint); if (refs[1] != null && _tableByTypes.ContainsKey(refs[1])) _tableByTypes[refs[1]].Remove(endpoint); if (refs[2] != null && _tableByGroups.ContainsKey(refs[2])) _tableByGroups[refs[2]].Remove(endpoint); _endpoints.Remove(endpoint); } } #endregion #region Transactional private Dictionary> _tran_tableByKey = new Dictionary>(); private Dictionary> _tran_tableByGroups = new Dictionary>(); private Dictionary> _tran_tableByTypes = new Dictionary>(); private Dictionary _tran_endpoints = new Dictionary(); private int _in_transaction = 0; internal void BeginUpdate() { if (Interlocked.Exchange(ref _in_transaction, 1) == 0) { _tran_endpoints.Clear(); _tran_tableByKey.Clear(); _tran_tableByGroups.Clear(); _tran_tableByTypes.Clear(); } else { throw new System.Exception("Transaction started already"); } } internal void Commit() { if (Interlocked.Exchange(ref _in_transaction, 0) == 1) { _lock.EnterWriteLock(); try { _endpoints = _tran_endpoints.Select(pair => pair).ToDictionary(p => p.Key, p => p.Value); _tableByGroups = _tran_tableByGroups.Select(pair => pair).ToDictionary(p => p.Key, p => new RoundRobinCollection(p.Value)); _tableByKey = _tran_tableByKey.Select(pair => pair).ToDictionary(p => p.Key, p => new RoundRobinCollection(p.Value)); _tableByTypes = _tran_tableByTypes.Select(pair => pair).ToDictionary(p => p.Key, p => new RoundRobinCollection(p.Value)); } finally { _lock.ExitWriteLock(); } _tran_endpoints.Clear(); _tran_tableByKey.Clear(); _tran_tableByGroups.Clear(); _tran_tableByTypes.Clear(); } } internal void Rollback() { if (Interlocked.Exchange(ref _in_transaction, 0) == 1) { _tran_endpoints.Clear(); _tran_tableByKey.Clear(); _tran_tableByGroups.Clear(); _tran_tableByTypes.Clear(); } } private void TransAppendByKeys(string key, IPEndPoint endpoint) { TransAppend(key.ToUpperInvariant(), endpoint, _tran_tableByKey); } private void TransAppendByType(string type, IPEndPoint endpoint) { TransAppend(type.ToUpperInvariant(), endpoint, _tran_tableByTypes); } private void TransAppendByGroup(string group, IPEndPoint endpoint) { TransAppend(group.ToUpperInvariant(), endpoint, _tran_tableByGroups); } private void TransAppend(string key, IPEndPoint value, Dictionary> dict) { if (!dict.ContainsKey(key)) { dict.Add(key, new List()); } dict[key].Add(value); } #endregion } }