-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCacheKeyManager.cs
More file actions
87 lines (72 loc) · 2.51 KB
/
CacheKeyManager.cs
File metadata and controls
87 lines (72 loc) · 2.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
namespace CleverCache;
using System.Collections.Concurrent;
internal abstract class CacheEntryManager
{
// type -> set of keys (thread-safe “set” via ConcurrentDictionary<TKey, byte>)
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<object, byte>> _keysByType = new();
// type -> dependents (direct edges). We’ll expand transitively at runtime.
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, byte>> _dependants = new();
/// <summary>
/// Adds a dependent relationship between two types.
/// </summary>
public void AddDependentCache(Type type, Type dependentType)
{
var set = _dependants.GetOrAdd(type, _ => new ConcurrentDictionary<Type, byte>());
set.TryAdd(dependentType, 0);
}
/// <summary>
/// Associates key with each type and all of its transitive dependents.
/// </summary>
public void AddKeyToTypes(Type[] types, object key)
{
var all = ExpandTransitively(types);
foreach (var t in all)
{
_keysByType.GetOrAdd(t, _ => new ConcurrentDictionary<object, byte>()).TryAdd(key, 0);
}
}
/// <summary>
/// Removes a key from every type's tracked key set.
/// Called after a key is removed from the store so the tracking set stays in sync.
/// </summary>
protected void RemoveKeyFromAllTypes(object key)
{
foreach (var typeSet in _keysByType.Values)
typeSet.TryRemove(key, out _);
}
/// <summary>
/// Snapshot keys for a given type (safe to enumerate).
/// </summary>
protected object[] SnapshotKeysFor(Type type) =>
_keysByType.TryGetValue(type, out var set) ? set.Keys.ToArray() : [];
/// <summary>
/// Snapshot of the full dependency graph and tracked keys.
/// </summary>
protected CleverCacheDiagnostics SnapshotDiagnostics()
{
var dependants = _dependants.ToDictionary(
kvp => kvp.Key,
kvp => (IReadOnlyList<Type>)kvp.Value.Keys.OrderBy(t => t.Name).ToList());
var keysByType = _keysByType.ToDictionary(
kvp => kvp.Key,
kvp => (IReadOnlyList<object>)kvp.Value.Keys.ToList());
return new CleverCacheDiagnostics(dependants, keysByType);
}
/// <summary>
/// Transitive closure over dependents, cycle-safe
/// </summary>
private HashSet<Type> ExpandTransitively(IEnumerable<Type> roots)
{
var visited = new HashSet<Type>();
var stack = new Stack<Type>(roots);
while (stack.Count > 0)
{
var t = stack.Pop();
if (!visited.Add(t)) continue;
if (!_dependants.TryGetValue(t, out var dependants)) continue;
foreach (var d in dependants.Keys)
if (!visited.Contains(d)) stack.Push(d);
}
return visited;
}
}