Skip to content

Commit f4d2142

Browse files
authored
feat: implemented LRU caching for flagd provider (#47)
Signed-off-by: Florian Bacher <[email protected]>
1 parent 04bdae0 commit f4d2142

File tree

7 files changed

+922
-50
lines changed

7 files changed

+922
-50
lines changed

Diff for: src/OpenFeature.Contrib.Providers.Flagd/Cache.cs

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
using System.Collections.Generic;
2+
using System.Runtime.CompilerServices;
3+
4+
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
5+
6+
namespace OpenFeature.Contrib.Providers.Flagd
7+
{
8+
internal interface ICache<TKey, TValue>
9+
{
10+
void Add(TKey key, TValue value);
11+
TValue TryGet(TKey key);
12+
void Delete(TKey key);
13+
void Purge();
14+
}
15+
class LRUCache<TKey, TValue> : ICache<TKey, TValue> where TValue : class
16+
{
17+
private readonly int _capacity;
18+
private readonly Dictionary<TKey, Node> _map;
19+
private Node _head;
20+
private Node _tail;
21+
22+
private System.Threading.Mutex _mtx;
23+
24+
public LRUCache(int capacity)
25+
{
26+
_capacity = capacity;
27+
_map = new Dictionary<TKey, Node>();
28+
_mtx = new System.Threading.Mutex();
29+
}
30+
31+
public TValue TryGet(TKey key)
32+
{
33+
using (var mtx = new Mutex(ref _mtx))
34+
{
35+
mtx.Lock();
36+
if (_map.TryGetValue(key, out Node node))
37+
{
38+
MoveToFront(node);
39+
return node.Value;
40+
}
41+
return default(TValue);
42+
}
43+
}
44+
45+
public void Add(TKey key, TValue value)
46+
{
47+
using (var mtx = new Mutex(ref _mtx))
48+
{
49+
mtx.Lock();
50+
if (_map.TryGetValue(key, out Node node))
51+
{
52+
node.Value = value;
53+
MoveToFront(node);
54+
}
55+
else
56+
{
57+
if (_map.Count >= _capacity)
58+
{
59+
_map.Remove(_tail.Key);
60+
RemoveTail();
61+
}
62+
node = new Node(key, value);
63+
_map.Add(key, node);
64+
AddToFront(node);
65+
}
66+
}
67+
}
68+
69+
public void Delete(TKey key)
70+
{
71+
using (var mtx = new Mutex(ref _mtx))
72+
{
73+
mtx.Lock();
74+
if (_map.TryGetValue(key, out Node node))
75+
{
76+
if (node == _head)
77+
{
78+
_head = node.Next;
79+
}
80+
else
81+
{
82+
node.Prev.Next = node.Next;
83+
}
84+
if (node.Next != null)
85+
{
86+
node.Next.Prev = node.Prev;
87+
}
88+
_map.Remove(key);
89+
}
90+
}
91+
}
92+
93+
public void Purge()
94+
{
95+
using (var mtx = new Mutex(ref _mtx))
96+
{
97+
mtx.Lock();
98+
_map.Clear();
99+
}
100+
}
101+
102+
private void MoveToFront(Node node)
103+
{
104+
if (node == _head)
105+
return;
106+
node.Prev.Next = node.Next;
107+
if (node == _tail)
108+
_tail = node.Prev;
109+
else
110+
node.Next.Prev = node.Prev;
111+
AddToFront(node);
112+
}
113+
114+
private void AddToFront(Node node)
115+
{
116+
if (_head == null)
117+
{
118+
_head = node;
119+
_tail = node;
120+
return;
121+
}
122+
node.Next = _head;
123+
_head.Prev = node;
124+
_head = node;
125+
}
126+
127+
private void RemoveTail()
128+
{
129+
_tail = _tail.Prev;
130+
if (_tail != null)
131+
_tail.Next = null;
132+
else
133+
_head = null;
134+
}
135+
136+
private class Node
137+
{
138+
public TKey Key;
139+
public TValue Value;
140+
public Node Next;
141+
public Node Prev;
142+
143+
public Node(TKey key, TValue value)
144+
{
145+
Key = key;
146+
Value = value;
147+
}
148+
}
149+
150+
private class Mutex : System.IDisposable
151+
{
152+
153+
public System.Threading.Mutex _mtx;
154+
155+
public Mutex(ref System.Threading.Mutex mtx)
156+
{
157+
_mtx = mtx;
158+
}
159+
160+
public void Lock()
161+
{
162+
_mtx.WaitOne();
163+
}
164+
165+
public void Dispose()
166+
{
167+
_mtx.ReleaseMutex();
168+
}
169+
}
170+
}
171+
172+
}
173+
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System;
2+
3+
namespace OpenFeature.Contrib.Providers.Flagd
4+
5+
{
6+
internal class FlagdConfig
7+
{
8+
internal const string EnvVarHost = "FLAGD_HOST";
9+
internal const string EnvVarPort = "FLAGD_PORT";
10+
internal const string EnvVarTLS = "FLAGD_TLS";
11+
internal const string EnvVarSocketPath = "FLAGD_SOCKET_PATH";
12+
internal const string EnvVarCache = "FLAGD_CACHE";
13+
internal const string EnvVarMaxCacheSize = "FLAGD_MAX_CACHE_SIZE";
14+
internal const string EnvVarMaxEventStreamRetries = "FLAGD_MAX_EVENT_STREAM_RETRIES";
15+
internal static int CacheSizeDefault = 10;
16+
internal string Host
17+
{
18+
get { return _host; }
19+
}
20+
21+
internal bool CacheEnabled
22+
{
23+
get { return _cache; }
24+
set { _cache = value; }
25+
}
26+
27+
internal int MaxCacheSize
28+
{
29+
get { return _maxCacheSize; }
30+
}
31+
32+
internal int MaxEventStreamRetries
33+
{
34+
get { return _maxEventStreamRetries; }
35+
set { _maxEventStreamRetries = value; }
36+
}
37+
38+
private string _host;
39+
private string _port;
40+
private bool _useTLS;
41+
private string _socketPath;
42+
private bool _cache;
43+
private int _maxCacheSize;
44+
private int _maxEventStreamRetries;
45+
46+
internal FlagdConfig()
47+
{
48+
_host = Environment.GetEnvironmentVariable(EnvVarHost) ?? "localhost";
49+
_port = Environment.GetEnvironmentVariable(EnvVarPort) ?? "8013";
50+
_useTLS = bool.Parse(Environment.GetEnvironmentVariable(EnvVarTLS) ?? "false");
51+
_socketPath = Environment.GetEnvironmentVariable(EnvVarSocketPath) ?? "";
52+
var cacheStr = Environment.GetEnvironmentVariable(EnvVarCache) ?? "";
53+
54+
if (cacheStr.ToUpper().Equals("LRU"))
55+
{
56+
_cache = true;
57+
_maxCacheSize = int.Parse(Environment.GetEnvironmentVariable(EnvVarMaxCacheSize) ?? $"{CacheSizeDefault}");
58+
_maxEventStreamRetries = int.Parse(Environment.GetEnvironmentVariable(EnvVarMaxEventStreamRetries) ?? "3");
59+
}
60+
}
61+
62+
internal Uri GetUri()
63+
{
64+
Uri uri;
65+
if (_socketPath != "")
66+
{
67+
uri = new Uri("unix://" + _socketPath);
68+
}
69+
else
70+
{
71+
var protocol = "http";
72+
73+
if (_useTLS)
74+
{
75+
protocol = "https";
76+
}
77+
78+
uri = new Uri(protocol + "://" + _host + ":" + _port);
79+
}
80+
return uri;
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)