Skip to content

Commit

Permalink
feat: implemented LRU caching for flagd provider (open-feature#47)
Browse files Browse the repository at this point in the history
Signed-off-by: Florian Bacher <[email protected]>
  • Loading branch information
bacherfl authored Apr 3, 2023
1 parent 04bdae0 commit f4d2142
Show file tree
Hide file tree
Showing 7 changed files with 922 additions and 50 deletions.
173 changes: 173 additions & 0 deletions src/OpenFeature.Contrib.Providers.Flagd/Cache.cs
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();
}
}
}

}

83 changes: 83 additions & 0 deletions src/OpenFeature.Contrib.Providers.Flagd/FlagdConfig.cs
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;
}
}
}
Loading

0 comments on commit f4d2142

Please sign in to comment.