aboutsummaryrefslogtreecommitdiff
path: root/Libs/VNLib.Plugins.Sessions.Cache.Client
diff options
context:
space:
mode:
authorLibravatar vman <public@vaughnnugent.com>2022-12-15 01:45:03 -0500
committerLibravatar vman <public@vaughnnugent.com>2022-12-15 01:45:03 -0500
commit1f2b3530ebeafa162fe4df41e691c33cb2ff0009 (patch)
tree7f60d7c761cee2df89303c3ef0550743790a63e2 /Libs/VNLib.Plugins.Sessions.Cache.Client
parenta0d5a8d40de9806e21e64475e3297a2a84effe22 (diff)
JWK sigs, session cleanup v1
Diffstat (limited to 'Libs/VNLib.Plugins.Sessions.Cache.Client')
-rw-r--r--Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionStatusException.cs3
-rw-r--r--Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs17
-rw-r--r--Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs123
-rw-r--r--Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj3
4 files changed, 106 insertions, 40 deletions
diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionStatusException.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionStatusException.cs
index b857638..bc6b217 100644
--- a/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionStatusException.cs
+++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/Exceptions/SessionStatusException.cs
@@ -29,6 +29,9 @@ using VNLib.Plugins.Essentials.Sessions;
namespace VNLib.Plugins.Sessions.Cache.Client.Exceptions
{
+ /// <summary>
+ /// Raised when the status of the session is invalid and cannot be used
+ /// </summary>
public class SessionStatusException : SessionException
{
public SessionStatusException()
diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs
index d2d4200..3b61f68 100644
--- a/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs
+++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs
@@ -37,8 +37,6 @@ using VNLib.Net.Messaging.FBM.Client;
using VNLib.Plugins.Essentials.Sessions;
using VNLib.Plugins.Essentials.Extensions;
-#nullable enable
-
namespace VNLib.Plugins.Sessions.Cache.Client
{
/// <summary>
@@ -48,9 +46,9 @@ namespace VNLib.Plugins.Sessions.Cache.Client
public abstract class RemoteSession : SessionBase
{
protected const string CREATED_TIME_ENTRY = "__.i.ctime";
-
- protected readonly FBMClient Client;
- protected readonly TimeSpan UpdateTimeout;
+
+ protected FBMClient Client { get; }
+ protected TimeSpan UpdateTimeout { get; }
private readonly AsyncLazyInitializer Initializer;
@@ -119,6 +117,8 @@ namespace VNLib.Plugins.Sessions.Cache.Client
protected set => this.SetValueType(CREATED_TIME_ENTRY, value.ToUnixTimeMilliseconds());
}
+
+
///<inheritdoc/>
protected override string IndexerGet(string key)
{
@@ -186,12 +186,5 @@ namespace VNLib.Plugins.Sessions.Cache.Client
throw;
}
}
- ///<inheritdoc/>
- protected override Task OnEvictedAsync()
- {
- //empty the dict to help the GC
- DataStore!.Clear();
- return Task.CompletedTask;
- }
}
}
diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs
index 8eed404..2e72391 100644
--- a/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs
+++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs
@@ -29,37 +29,64 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using VNLib.Net.Http;
-using VNLib.Utils;
+using VNLib.Utils.Async;
+using VNLib.Utils.Logging;
using VNLib.Utils.Memory.Caching;
using VNLib.Net.Messaging.FBM.Client;
using VNLib.Plugins.Essentials.Sessions;
-#nullable enable
-
namespace VNLib.Plugins.Sessions.Cache.Client
{
/// <summary>
/// A client that allows access to sessions located on external servers
/// </summary>
- public abstract class SessionCacheClient : VnDisposeable, ICacheHolder
+ public abstract class SessionCacheClient : ICacheHolder
{
- public class LRUSessionStore<T> : LRUCache<string, T> where T : ISession, ICacheable
+ public class LRUSessionStore<T> : LRUCache<string, T>, ICacheHolder where T : ISession
{
+ internal AsyncQueue<T> ExpiredSessions { get; }
+
+ ///<inheritdoc/>
public override bool IsReadOnly => false;
+ ///<inheritdoc/>
protected override int MaxCapacity { get; }
- public LRUSessionStore(int maxCapacity) : base(StringComparer.Ordinal) => MaxCapacity = maxCapacity;
-
+
+ public LRUSessionStore(int maxCapacity) : base(StringComparer.Ordinal)
+ {
+ MaxCapacity = maxCapacity;
+ ExpiredSessions = new (true, true);
+ }
+
+ ///<inheritdoc/>
protected override bool CacheMiss(string key, [NotNullWhen(true)] out T? value)
{
value = default;
return false;
}
+
+ ///<inheritdoc/>
protected override void Evicted(KeyValuePair<string, T> evicted)
{
- //Evice record
- evicted.Value.Evicted();
+ //add to queue, the list lock should be held during this operatio
+ _ = ExpiredSessions.TryEnque(evicted.Value);
+ }
+
+ ///<inheritdoc/>
+ public void CacheClear()
+ {
+ foreach (KeyValuePair<string, T> value in List)
+ {
+ Evicted(value);
+ }
+ Clear();
+ }
+
+ ///<inheritdoc/>
+ public void CacheHardClear()
+ {
+ CacheClear();
}
}
@@ -67,6 +94,9 @@ namespace VNLib.Plugins.Sessions.Cache.Client
protected readonly object CacheLock;
protected readonly int MaxLoadedEntires;
+ /// <summary>
+ /// The client used to communicate with the cache server
+ /// </summary>
protected FBMClient Client { get; }
/// <summary>
@@ -80,11 +110,14 @@ namespace VNLib.Plugins.Sessions.Cache.Client
CacheLock = new();
CacheTable = new(maxCacheItems);
Client = client;
- //Listen for close events
- Client.ConnectionClosed += Client_ConnectionClosed;
}
- private void Client_ConnectionClosed(object? sender, EventArgs e) => CacheHardClear();
+ private ulong _waitingCount;
+
+ /// <summary>
+ /// The number of pending connections waiting for results from the cache server
+ /// </summary>
+ public ulong WaitingConnections => _waitingCount;
/// <summary>
/// Attempts to get a session from the cache identified by its sessionId asynchronously
@@ -96,7 +129,6 @@ namespace VNLib.Plugins.Sessions.Cache.Client
/// <exception cref="SessionException"></exception>
public virtual async ValueTask<RemoteSession> GetSessionAsync(IHttpEvent entity, string sessionId, CancellationToken cancellationToken)
{
- Check();
try
{
RemoteSession? session;
@@ -113,6 +145,10 @@ namespace VNLib.Plugins.Sessions.Cache.Client
}
//Valid entry found in cache
}
+
+ //Inc waiting count
+ Interlocked.Increment(ref _waitingCount);
+
try
{
//Load session-data
@@ -128,6 +164,11 @@ namespace VNLib.Plugins.Sessions.Cache.Client
}
throw;
}
+ finally
+ {
+ //Dec waiting count
+ Interlocked.Decrement(ref _waitingCount);
+ }
}
catch (SessionException)
{
@@ -151,6 +192,47 @@ namespace VNLib.Plugins.Sessions.Cache.Client
/// <param name="sessionId">The session identifier</param>
/// <returns>The new session for the given ID</returns>
protected abstract RemoteSession SessionCtor(string sessionId);
+
+ /// <summary>
+ /// Begins waiting for expired sessions to be evicted from the cache table that
+ /// may have pending synchronization operations
+ /// </summary>
+ /// <param name="log"></param>
+ /// <param name="token"></param>
+ /// <returns></returns>
+ public async Task CleanupExpiredSessionsAsync(ILogProvider log, CancellationToken token)
+ {
+ //Close handler
+ void OnConnectionClosed(object? sender, EventArgs e) => CacheHardClear();
+
+ //Attach event
+ Client.ConnectionClosed += OnConnectionClosed;
+
+ while (true)
+ {
+ try
+ {
+ //Wait for expired session and dispose it
+ using RemoteSession session = await CacheTable.ExpiredSessions.DequeueAsync(token);
+
+ //Obtain lock on session
+ await session.WaitOneAsync(CancellationToken.None);
+
+ log.Verbose("Removed expired session {id}", session.SessionID);
+ }
+ catch (OperationCanceledException)
+ {
+ break;
+ }
+ catch(Exception ex)
+ {
+ log.Error(ex);
+ }
+ }
+
+ //remove handler
+ Client.ConnectionClosed -= OnConnectionClosed;
+ }
///<inheritdoc/>
public void CacheClear()
@@ -163,21 +245,8 @@ namespace VNLib.Plugins.Sessions.Cache.Client
//Cleanup cache when disconnected
lock (CacheLock)
{
- CacheTable.Clear();
- foreach (RemoteSession session in (IEnumerable<RemoteSession>)CacheTable)
- {
- session.Evicted();
- }
- CacheTable.Clear();
+ CacheTable.CacheHardClear();
}
}
-
- protected override void Free()
- {
- //Unsub from events
- Client.ConnectionClosed -= Client_ConnectionClosed;
- //Clear all cached sessions
- CacheHardClear();
- }
}
}
diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj b/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj
index fc99bbf..a75ebe3 100644
--- a/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj
+++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/VNLib.Plugins.Sessions.Cache.Client.csproj
@@ -12,6 +12,7 @@
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl>
<AnalysisLevel>latest-all</AnalysisLevel>
+ <Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -36,7 +37,7 @@
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\..\..\..\VNLib\Essentials\VNLib.Plugins.Essentials.csproj" />
+ <ProjectReference Include="..\..\..\..\VNLib\Essentials\src\VNLib.Plugins.Essentials.csproj" />
<ProjectReference Include="..\..\..\DataCaching\VNLib.Data.Caching\src\VNLib.Data.Caching.csproj" />
</ItemGroup>