aboutsummaryrefslogtreecommitdiff
path: root/libs/VNLib.Plugins.Sessions.Cache.Client/src/RemoteSession.cs
diff options
context:
space:
mode:
Diffstat (limited to 'libs/VNLib.Plugins.Sessions.Cache.Client/src/RemoteSession.cs')
-rw-r--r--libs/VNLib.Plugins.Sessions.Cache.Client/src/RemoteSession.cs170
1 files changed, 67 insertions, 103 deletions
diff --git a/libs/VNLib.Plugins.Sessions.Cache.Client/src/RemoteSession.cs b/libs/VNLib.Plugins.Sessions.Cache.Client/src/RemoteSession.cs
index af2c969..421d07d 100644
--- a/libs/VNLib.Plugins.Sessions.Cache.Client/src/RemoteSession.cs
+++ b/libs/VNLib.Plugins.Sessions.Cache.Client/src/RemoteSession.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Sessions.Cache.Client
@@ -23,85 +23,49 @@
*/
using System;
-using System.Threading;
-using System.Threading.Tasks;
using System.Collections.Generic;
-using Microsoft.VisualStudio.Threading;
-
-using VNLib.Net.Http;
-using VNLib.Data.Caching.Exceptions;
using VNLib.Utils.Extensions;
using VNLib.Plugins.Essentials.Sessions;
using VNLib.Plugins.Essentials.Extensions;
+
namespace VNLib.Plugins.Sessions.Cache.Client
{
/// <summary>
/// Base class for cacheable lazy initialized session entires
/// that exist in a remote caching server
/// </summary>
- public abstract class RemoteSession : SessionBase
+ public abstract class RemoteSession : SessionBase, IRemoteSession
{
protected const string CREATED_TIME_ENTRY = "__.i.ctime";
-
- protected IRemoteCacheStore Client { get; }
- protected TimeSpan UpdateTimeout { get; }
- private readonly AsyncLazyInitializer Initializer;
-
/// <summary>
- /// The lazy loaded data-store
+ /// The session data store
/// </summary>
- protected Dictionary<string, string>? DataStore;
-
- protected RemoteSession(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut)
- {
- SessionID = sessionId;
- UpdateTimeout = backgroundTimeOut;
- Client = client;
- Initializer = new(InitializeAsync, null);
- }
+ protected readonly IDictionary<string, string> DataStore;
/// <summary>
- /// The data initializer, loads the data store from the connected cache server
+ /// The reason that the session was destroyed if an error occured
/// </summary>
- /// <returns>A task that completes when the get operation completes</returns>
- protected virtual async Task InitializeAsync()
- {
- //Setup timeout cancellation for the get, to cancel it
- using CancellationTokenSource cts = new(UpdateTimeout);
- //get or create a new session
- DataStore = await Client.GetObjectAsync<Dictionary<string, string>>(SessionID, cancellationToken: cts.Token);
- }
- /// <summary>
- /// Updates the current sessin agaisnt the cache store
- /// </summary>
- /// <returns>A task that complets when the update has completed</returns>
- protected virtual async Task ProcessUpdateAsync()
- {
- //Setup timeout cancellation for the update, to cancel it
- using CancellationTokenSource cts = new(UpdateTimeout);
- await Client.AddOrUpdateObjectAsync(SessionID, null, DataStore, cts.Token);
- }
+ protected Exception? ErrorCause { get; set; }
+
/// <summary>
- /// Delets the current session in the remote store
+ /// Initialzies a new session
/// </summary>
- /// <returns>A task that completes when instance has been deleted</returns>
- protected virtual async Task ProcessDeleteAsync()
+ /// <param name="sessionId">The id of the current session</param>
+ /// <param name="initialData">The initial data</param>
+ /// <param name="isNew">A flag that determines if the session is considered new</param>
+ protected RemoteSession(string sessionId, IDictionary<string, string> initialData, bool isNew)
{
- //Setup timeout cancellation for the update, to cancel it
- using CancellationTokenSource cts = new(UpdateTimeout);
- try
- {
- await Client.DeleteObjectAsync(SessionID, cts.Token);
- }
- catch (ObjectNotFoundException)
- {
- //This is fine, if the object does not exist, nothing to invalidate
- }
+ SessionID = sessionId;
+ DataStore = initialData;
+ IsNew = isNew;
}
-
+
+ ///<inheritdoc/>
+ public override string SessionID { get; }
+
///<inheritdoc/>
public override DateTimeOffset Created
{
@@ -113,15 +77,14 @@ namespace VNLib.Plugins.Sessions.Cache.Client
return DateTimeOffset.FromUnixTimeMilliseconds(unixMs);
}
- protected set => this.SetValueType(CREATED_TIME_ENTRY, value.ToUnixTimeMilliseconds());
- }
-
+ set => this.SetValueType(CREATED_TIME_ENTRY, value.ToUnixTimeMilliseconds());
+ }
///<inheritdoc/>
protected override string IndexerGet(string key)
{
//Get the value at the key or an empty string as a default
- return DataStore!.GetValueOrDefault(key, string.Empty);
+ return DataStore.GetValueOrDefault(key) ?? string.Empty;
}
///<inheritdoc/>
protected override void IndexerSet(string key, string value)
@@ -130,59 +93,60 @@ namespace VNLib.Plugins.Sessions.Cache.Client
if (value == null)
{
//Set modified flag
- IsModified |= DataStore!.Remove(key);
+ IsModified |= DataStore.Remove(key);
}
else
{
//Store the value at the specified key
- DataStore![key] = value;
+ DataStore[key] = value;
IsModified = true;
}
}
+
-
- /*
- * If the data-store is not found it means the session does not
- * exist in cache, so its technically not dangerous to reuse,
- * so the new mask needs to be set, but the old ID is going
- * to be reused
- */
+ ///<inheritdoc/>
+ public virtual SessionStatus GetStatus()
+ {
+ SessionStatus status = SessionStatus.None;
- /// <summary>
- /// Waits for exclusive access to the session, and initializes
- /// session data (loads it from the remote store)
- /// </summary>
- /// <param name="entity">The event to attach a session to</param>
- /// <param name="cancellationToken">A token to cancel the operaion</param>
- /// <returns></returns>
- public virtual async Task WaitAndLoadAsync(IHttpEvent entity, CancellationToken cancellationToken)
+ status |= Flags.IsSet(INVALID_MSK) ? SessionStatus.Delete : SessionStatus.None;
+ status |= Flags.IsSet(REGEN_ID_MSK) ? SessionStatus.RegenId : SessionStatus.None;
+ status |= Flags.IsSet(MODIFIED_MSK) ? SessionStatus.UpdateOnly: SessionStatus.None;
+
+ return status;
+ }
+
+ ///<inheritdoc/>
+ public virtual IDictionary<string, string> GetSessionData() => DataStore;
+
+ ///<inheritdoc/>
+ public virtual void Destroy(Exception? cause)
{
- //Wait for exclusive access
- await base.WaitOneAsync(cancellationToken);
- try
- {
- //Lazily initalize the current instance
- await Initializer.InitializeAsync(cancellationToken);
- //See if data-store is null (new session was created
- if (DataStore == null)
- {
- //New session was created
- DataStore = new(10);
- //Set is-new flag
- Flags.Set(IS_NEW_MSK);
- //Set created time
- Created = DateTimeOffset.UtcNow;
- //Init ipaddress
- UserIP = entity.Server.GetTrustedIp();
- //Set modified flag so session will be updated
- IsModified = true;
- }
- }
- catch
- {
- MainLock.Release();
- throw;
- }
+ //Set invalid status
+ ErrorCause = cause;
+ Flags.Set(INVALID_MSK);
+
+ //Clear all session data
+ DataStore.Clear();
+ }
+
+ ///<inheritdoc/>
+ public virtual bool IsValid(out Exception? cause)
+ {
+ /*
+ * Were reusing the invalid mask assuming that when a session is invalidated
+ * it will be deleted and destroyed
+ */
+
+ cause = ErrorCause;
+ return !Flags.IsSet(INVALID_MSK);
+ }
+
+ ///<inheritdoc/>
+ public virtual void SessionUpdateComplete()
+ {
+ //Reset flags
+ Flags.ClearAll();
}
}
}