forked from open-feature/dotnet-sdk-contrib
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implemented LRU caching for flagd provider (open-feature#47)
Signed-off-by: Florian Bacher <[email protected]>
- Loading branch information
Showing
7 changed files
with
922 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
using System.Collections.Generic; | ||
using System.Runtime.CompilerServices; | ||
|
||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] | ||
|
||
namespace OpenFeature.Contrib.Providers.Flagd | ||
{ | ||
internal interface ICache<TKey, TValue> | ||
{ | ||
void Add(TKey key, TValue value); | ||
TValue TryGet(TKey key); | ||
void Delete(TKey key); | ||
void Purge(); | ||
} | ||
class LRUCache<TKey, TValue> : ICache<TKey, TValue> where TValue : class | ||
{ | ||
private readonly int _capacity; | ||
private readonly Dictionary<TKey, Node> _map; | ||
private Node _head; | ||
private Node _tail; | ||
|
||
private System.Threading.Mutex _mtx; | ||
|
||
public LRUCache(int capacity) | ||
{ | ||
_capacity = capacity; | ||
_map = new Dictionary<TKey, Node>(); | ||
_mtx = new System.Threading.Mutex(); | ||
} | ||
|
||
public TValue TryGet(TKey key) | ||
{ | ||
using (var mtx = new Mutex(ref _mtx)) | ||
{ | ||
mtx.Lock(); | ||
if (_map.TryGetValue(key, out Node node)) | ||
{ | ||
MoveToFront(node); | ||
return node.Value; | ||
} | ||
return default(TValue); | ||
} | ||
} | ||
|
||
public void Add(TKey key, TValue value) | ||
{ | ||
using (var mtx = new Mutex(ref _mtx)) | ||
{ | ||
mtx.Lock(); | ||
if (_map.TryGetValue(key, out Node node)) | ||
{ | ||
node.Value = value; | ||
MoveToFront(node); | ||
} | ||
else | ||
{ | ||
if (_map.Count >= _capacity) | ||
{ | ||
_map.Remove(_tail.Key); | ||
RemoveTail(); | ||
} | ||
node = new Node(key, value); | ||
_map.Add(key, node); | ||
AddToFront(node); | ||
} | ||
} | ||
} | ||
|
||
public void Delete(TKey key) | ||
{ | ||
using (var mtx = new Mutex(ref _mtx)) | ||
{ | ||
mtx.Lock(); | ||
if (_map.TryGetValue(key, out Node node)) | ||
{ | ||
if (node == _head) | ||
{ | ||
_head = node.Next; | ||
} | ||
else | ||
{ | ||
node.Prev.Next = node.Next; | ||
} | ||
if (node.Next != null) | ||
{ | ||
node.Next.Prev = node.Prev; | ||
} | ||
_map.Remove(key); | ||
} | ||
} | ||
} | ||
|
||
public void Purge() | ||
{ | ||
using (var mtx = new Mutex(ref _mtx)) | ||
{ | ||
mtx.Lock(); | ||
_map.Clear(); | ||
} | ||
} | ||
|
||
private void MoveToFront(Node node) | ||
{ | ||
if (node == _head) | ||
return; | ||
node.Prev.Next = node.Next; | ||
if (node == _tail) | ||
_tail = node.Prev; | ||
else | ||
node.Next.Prev = node.Prev; | ||
AddToFront(node); | ||
} | ||
|
||
private void AddToFront(Node node) | ||
{ | ||
if (_head == null) | ||
{ | ||
_head = node; | ||
_tail = node; | ||
return; | ||
} | ||
node.Next = _head; | ||
_head.Prev = node; | ||
_head = node; | ||
} | ||
|
||
private void RemoveTail() | ||
{ | ||
_tail = _tail.Prev; | ||
if (_tail != null) | ||
_tail.Next = null; | ||
else | ||
_head = null; | ||
} | ||
|
||
private class Node | ||
{ | ||
public TKey Key; | ||
public TValue Value; | ||
public Node Next; | ||
public Node Prev; | ||
|
||
public Node(TKey key, TValue value) | ||
{ | ||
Key = key; | ||
Value = value; | ||
} | ||
} | ||
|
||
private class Mutex : System.IDisposable | ||
{ | ||
|
||
public System.Threading.Mutex _mtx; | ||
|
||
public Mutex(ref System.Threading.Mutex mtx) | ||
{ | ||
_mtx = mtx; | ||
} | ||
|
||
public void Lock() | ||
{ | ||
_mtx.WaitOne(); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_mtx.ReleaseMutex(); | ||
} | ||
} | ||
} | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
using System; | ||
|
||
namespace OpenFeature.Contrib.Providers.Flagd | ||
|
||
{ | ||
internal class FlagdConfig | ||
{ | ||
internal const string EnvVarHost = "FLAGD_HOST"; | ||
internal const string EnvVarPort = "FLAGD_PORT"; | ||
internal const string EnvVarTLS = "FLAGD_TLS"; | ||
internal const string EnvVarSocketPath = "FLAGD_SOCKET_PATH"; | ||
internal const string EnvVarCache = "FLAGD_CACHE"; | ||
internal const string EnvVarMaxCacheSize = "FLAGD_MAX_CACHE_SIZE"; | ||
internal const string EnvVarMaxEventStreamRetries = "FLAGD_MAX_EVENT_STREAM_RETRIES"; | ||
internal static int CacheSizeDefault = 10; | ||
internal string Host | ||
{ | ||
get { return _host; } | ||
} | ||
|
||
internal bool CacheEnabled | ||
{ | ||
get { return _cache; } | ||
set { _cache = value; } | ||
} | ||
|
||
internal int MaxCacheSize | ||
{ | ||
get { return _maxCacheSize; } | ||
} | ||
|
||
internal int MaxEventStreamRetries | ||
{ | ||
get { return _maxEventStreamRetries; } | ||
set { _maxEventStreamRetries = value; } | ||
} | ||
|
||
private string _host; | ||
private string _port; | ||
private bool _useTLS; | ||
private string _socketPath; | ||
private bool _cache; | ||
private int _maxCacheSize; | ||
private int _maxEventStreamRetries; | ||
|
||
internal FlagdConfig() | ||
{ | ||
_host = Environment.GetEnvironmentVariable(EnvVarHost) ?? "localhost"; | ||
_port = Environment.GetEnvironmentVariable(EnvVarPort) ?? "8013"; | ||
_useTLS = bool.Parse(Environment.GetEnvironmentVariable(EnvVarTLS) ?? "false"); | ||
_socketPath = Environment.GetEnvironmentVariable(EnvVarSocketPath) ?? ""; | ||
var cacheStr = Environment.GetEnvironmentVariable(EnvVarCache) ?? ""; | ||
|
||
if (cacheStr.ToUpper().Equals("LRU")) | ||
{ | ||
_cache = true; | ||
_maxCacheSize = int.Parse(Environment.GetEnvironmentVariable(EnvVarMaxCacheSize) ?? $"{CacheSizeDefault}"); | ||
_maxEventStreamRetries = int.Parse(Environment.GetEnvironmentVariable(EnvVarMaxEventStreamRetries) ?? "3"); | ||
} | ||
} | ||
|
||
internal Uri GetUri() | ||
{ | ||
Uri uri; | ||
if (_socketPath != "") | ||
{ | ||
uri = new Uri("unix://" + _socketPath); | ||
} | ||
else | ||
{ | ||
var protocol = "http"; | ||
|
||
if (_useTLS) | ||
{ | ||
protocol = "https"; | ||
} | ||
|
||
uri = new Uri(protocol + "://" + _host + ":" + _port); | ||
} | ||
return uri; | ||
} | ||
} | ||
} |
Oops, something went wrong.