From 9bb5ddd8f19c0ecabd7af4ee58d80c16826bc183 Mon Sep 17 00:00:00 2001 From: vman Date: Wed, 28 Dec 2022 14:19:32 -0500 Subject: Cache provider abstractions, reduced deps --- .../O2SessionProviderEntry.cs | 94 +++------- .../OAuth2Session.cs | 5 +- .../OAuth2SessionProvider.cs | 14 +- .../VNLib.Plugins.Essentials.Sessions.OAuth.csproj | 6 +- ...NLib.Plugins.Essentials.Sessions.Runtime.csproj | 11 +- .../VnCacheClient.cs | 201 --------------------- ...NLib.Plugins.Essentials.Sessions.VNCache.csproj | 5 +- .../WebSession.cs | 4 +- .../WebSessionProvider.cs | 7 +- .../WebSessionProviderEntry.cs | 87 ++------- .../MemorySessionEntrypoint.cs | 4 +- ...VNLib.Plugins.Essentials.Sessions.Memory.csproj | 2 + .../GlobalCacheStore.cs | 64 +++++++ .../IRemoteCacheStore.cs | 48 +++++ .../RemoteSession.cs | 6 +- .../SessionCacheClient.cs | 16 +- .../VNLib.Plugins.Sessions.Cache.Client.csproj | 6 +- 17 files changed, 201 insertions(+), 379 deletions(-) delete mode 100644 Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VnCacheClient.cs create mode 100644 Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs create mode 100644 Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs (limited to 'Libs') diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs index f4462a4..7e72714 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/O2SessionProviderEntry.cs @@ -27,12 +27,14 @@ using System.Text.Json; using VNLib.Net.Http; using VNLib.Utils.Logging; using VNLib.Utils.Extensions; -using VNLib.Data.Caching.Extensions; +using VNLib.Data.Caching; +using VNLib.Plugins.Sessions.Cache.Client; using VNLib.Plugins.Essentials.Oauth.Tokens; using VNLib.Plugins.Essentials.Oauth.Applications; using VNLib.Plugins.Essentials.Sessions.OAuth; using VNLib.Plugins.Essentials.Sessions.Runtime; using VNLib.Plugins.Essentials.Sessions.OAuth.Endpoints; +using VNLib.Plugins.Extensions.VNCache; using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Extensions.Loading.Routing; using VNLib.Plugins.Extensions.Loading.Sql; @@ -44,7 +46,6 @@ namespace VNLib.Plugins.Essentials.Sessions.Oauth public sealed class O2SessionProviderEntry : IRuntimeSessionProvider { - const string VNCACHE_CONFIG_KEY = "vncache"; const string OAUTH2_CONFIG_KEY = "oauth2"; private OAuth2SessionProvider? _sessions; @@ -63,14 +64,11 @@ namespace VNLib.Plugins.Essentials.Sessions.Oauth void IRuntimeSessionProvider.Load(PluginBase plugin, ILogProvider localized) { - //Try get vncache config element - IReadOnlyDictionary cacheConfig = plugin.GetConfig(VNCACHE_CONFIG_KEY); - - IReadOnlyDictionary oauth2Config = plugin.GetConfig(OAUTH2_CONFIG_KEY); + IReadOnlyDictionary oauth2Config = plugin.GetConfigForType(); //Optional application jwt token Task jwtTokenSecret = plugin.TryGetSecretAsync("application_token_key") - .ContinueWith(static t => t.Result == null ? null : t.Result.GetJsonDocument(), TaskScheduler.Default); + .ContinueWith(static t => t.Result?.GetJsonDocument(), TaskScheduler.Default); //Access token endpoint is optional if (oauth2Config.TryGetValue("token_path", out JsonElement el)) @@ -89,74 +87,38 @@ namespace VNLib.Plugins.Essentials.Sessions.Oauth plugin.Route(); } - //Run - _ = plugin.DeferTask(() => CacheWokerDoWorkAsync(plugin, localized, cacheConfig, oauth2Config), 100); - - } + int cacheLimit = oauth2Config["cache_size"].GetInt32(); + int maxTokensPerApp = oauth2Config["max_tokens_per_app"].GetInt32(); + int sessionIdSize = (int)oauth2Config["access_token_size"].GetUInt32(); + TimeSpan tokenValidFor = oauth2Config["token_valid_for_sec"].GetTimeSpan(TimeParseType.Seconds); + TimeSpan cleanupInterval = oauth2Config["gc_interval_sec"].GetTimeSpan(TimeParseType.Seconds); + string sessionIdPrefix = oauth2Config["cache_prefix"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'cache_prefix' in '{OAUTH2_CONFIG_KEY}' config"); - private async Task CreateTokenDelegateAsync(HttpEntity entity, UserApplication app, CancellationToken cancellation) - { - return await _sessions!.CreateAccessTokenAsync(entity, app, cancellation).ConfigureAwait(false); - } + //init the id provider + OAuth2SessionIdProvider idProv = new(sessionIdPrefix, maxTokensPerApp, sessionIdSize, tokenValidFor); - /* - * Starts and monitors the VNCache connection - */ + //Get shared global-cache + IGlobalCacheProvider globalCache = plugin.GetGlobalCache(); - private async Task CacheWokerDoWorkAsync(PluginBase plugin, ILogProvider localized, - IReadOnlyDictionary cacheConfig, - IReadOnlyDictionary oauth2Config) - { - //Init cache client - using VnCacheClient cache = new(plugin.IsDebug() ? localized : null, Utils.Memory.Memory.Shared); - - try - { - int cacheLimit = oauth2Config["cache_size"].GetInt32(); - int maxTokensPerApp = oauth2Config["max_tokens_per_app"].GetInt32(); - int sessionIdSize = (int)oauth2Config["access_token_size"].GetUInt32(); - TimeSpan tokenValidFor = oauth2Config["token_valid_for_sec"].GetTimeSpan(TimeParseType.Seconds); - TimeSpan cleanupInterval = oauth2Config["gc_interval_sec"].GetTimeSpan(TimeParseType.Seconds); - string sessionIdPrefix = oauth2Config["cache_prefix"].GetString() ?? throw new KeyNotFoundException($"Missing required key 'cache_prefix' in '{OAUTH2_CONFIG_KEY}' config"); - - //init the id provider - OAuth2SessionIdProvider idProv = new(sessionIdPrefix, maxTokensPerApp, sessionIdSize, tokenValidFor); - - //Try loading config - await cache.LoadConfigAsync(plugin, cacheConfig); + //Create cache store from global cache + GlobalCacheStore cacheStore = new(globalCache); - //Init session provider now that client is loaded - _sessions = new(cache.Resource!, cacheLimit, 100, idProv, plugin.GetContextOptions()); + //Init session provider now that client is loaded + _sessions = new(cacheStore, cacheLimit, 100, idProv, plugin.GetContextOptions()); - //Schedule cleanup interval with the plugin scheduler - plugin.ScheduleInterval(_sessions, cleanupInterval); + //Schedule cleanup interval with the plugin scheduler + plugin.ScheduleInterval(_sessions, cleanupInterval); - localized.Information("Session provider loaded"); + //Wait and cleanup expired sessions + _ = plugin.DeferTask(() => _sessions.CleanupExpiredSessionsAsync(localized, plugin.UnloadToken), 1000); - //Run and wait for exit - await cache.RunAsync(localized, plugin.UnloadToken); + localized.Information("Session provider loaded"); - } - catch (OperationCanceledException) - {} - catch (KeyNotFoundException e) - { - localized.Error("Missing required configuration variable for VnCache client: {0}", e.Message); - } - catch(FBMServerNegiationException fne) - { - localized.Error("Failed to negotiate connection with cache server {reason}", fne.Message); - } - catch (Exception ex) - { - localized.Error(ex, "Cache client error occured in session provider"); - } - finally - { - _sessions = null; - } + } - localized.Information("Cache client exited"); + private async Task CreateTokenDelegateAsync(HttpEntity entity, UserApplication app, CancellationToken cancellation) + { + return await _sessions!.CreateAccessTokenAsync(entity, app, cancellation).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs index 539799c..e9a69cd 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2Session.cs @@ -25,7 +25,6 @@ using System; using VNLib.Net.Http; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Sessions.Cache.Client; using VNLib.Plugins.Sessions.Cache.Client.Exceptions; @@ -44,10 +43,10 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth /// Initalizes a new /// /// The session id (or token) - /// The used as the backing cache provider + /// The used as the backing cache provider /// The ammount of time to wait for a background operation (delete, update, get) /// Called when the session has been marked as invalid and the close even hook is being executed - public OAuth2Session(string sessionId, FBMClient client, TimeSpan backgroundTimeOut, Action invalidCache) + public OAuth2Session(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut, Action invalidCache) : base(sessionId, client, backgroundTimeOut) { InvalidateCache = invalidCache; diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs index 106029f..dca7909 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/OAuth2SessionProvider.cs @@ -30,14 +30,13 @@ using Microsoft.EntityFrameworkCore; using VNLib.Net.Http; using VNLib.Utils; using VNLib.Utils.Logging; -using VNLib.Data.Caching; using VNLib.Data.Caching.Exceptions; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Sessions.Cache.Client; using VNLib.Plugins.Essentials.Oauth; using VNLib.Plugins.Essentials.Oauth.Tokens; using VNLib.Plugins.Essentials.Oauth.Applications; using VNLib.Plugins.Extensions.Loading.Events; +using VNLib.Plugins.Extensions.Loading; namespace VNLib.Plugins.Essentials.Sessions.OAuth { @@ -45,7 +44,8 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth /// /// Provides OAuth2 session management /// - internal sealed class OAuth2SessionProvider : SessionCacheClient, ISessionProvider, ITokenManager, IIntervalScheduleable + [ConfigurationName("oauth2")] + internal sealed class OAuth2SessionProvider : SessionCacheClient, ITokenManager, IIntervalScheduleable { private static readonly SessionHandle NotFoundHandle = new(null, FileProcessArgs.NotFound, null); @@ -57,7 +57,7 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth private readonly TokenStore TokenStore; private readonly uint MaxConnections; - public OAuth2SessionProvider(FBMClient client, int maxCacheItems, uint maxConnections, IOauthSessionIdFactory idFactory, DbContextOptions dbCtx) + public OAuth2SessionProvider(IRemoteCacheStore client, int maxCacheItems, uint maxConnections, IOauthSessionIdFactory idFactory, DbContextOptions dbCtx) : base(client, maxCacheItems) { factory = idFactory; @@ -66,7 +66,7 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth } /// - protected override RemoteSession SessionCtor(string sessionId) => new OAuth2Session(sessionId, Client, BackgroundTimeout, InvalidatateCache); + protected override RemoteSession SessionCtor(string sessionId) => new OAuth2Session(sessionId, Store, BackgroundTimeout, InvalidatateCache); private void InvalidatateCache(OAuth2Session session) { @@ -97,7 +97,7 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth if (WaitingConnections > MaxConnections) { //Set 503 for temporary unavail - entity.CloseResponse(System.Net.HttpStatusCode.ServiceUnavailable); + entity.CloseResponse(HttpStatusCode.ServiceUnavailable); return new SessionHandle(null, FileProcessArgs.VirtualSkip, null); } @@ -202,7 +202,7 @@ namespace VNLib.Plugins.Essentials.Sessions.OAuth try { //Remove tokens by thier object id from cache - await base.Client.DeleteObjectAsync(token.Id, cancellationToken); + await base.Store.DeleteObjectAsync(token.Id, cancellationToken); } //Ignore if the object has already been removed catch (ObjectNotFoundException) diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj index e9927b5..d6775c8 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj +++ b/Libs/VNLib.Plugins.Essentials.Sessions.OAuth/VNLib.Plugins.Essentials.Sessions.OAuth.csproj @@ -13,7 +13,8 @@ 1.0.1.1 https://www.vaughnugent.com latest-all - + True + \\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk @@ -34,11 +35,10 @@ - - + diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj b/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj index cf6d0c9..87c29e8 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj +++ b/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VNLib.Plugins.Essentials.Sessions.Runtime.csproj @@ -6,17 +6,22 @@ enable Vaughn Nugent Copyright © 2022 Vaughn Nugent - 1.0.1.1 + 1.0.1.2 False https://www.vaughnnugent.com/resources - True latest-all + True + True + \\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk + + + + - diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VnCacheClient.cs b/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VnCacheClient.cs deleted file mode 100644 index 2f7bdf2..0000000 --- a/Libs/VNLib.Plugins.Essentials.Sessions.Runtime/VnCacheClient.cs +++ /dev/null @@ -1,201 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.Sessions.Runtime -* File: VnCacheClient.cs -* -* VnCacheClient.cs is part of VNLib.Plugins.Essentials.Sessions.Runtime which is part of the larger -* VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.Sessions.Runtime is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 3 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.Sessions.Runtime is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Text.Json; -using System.Net.Sockets; -using System.Net.WebSockets; -using System.Security.Cryptography; - -using VNLib.Utils.Memory; -using VNLib.Utils.Logging; -using VNLib.Utils.Resources; -using VNLib.Utils.Extensions; -using VNLib.Data.Caching.Extensions; -using VNLib.Net.Messaging.FBM.Client; -using VNLib.Plugins.Extensions.Loading; -using VNLib.Hashing.IdentityUtility; - -namespace VNLib.Plugins.Essentials.Sessions.Runtime -{ - /// - /// A wrapper to simplify a cache client object - /// - public sealed class VnCacheClient : OpenResourceHandle - { - FBMClient? _client; - - /// - /// The wrapped client - /// - public override FBMClient? Resource => _client; - - - private TimeSpan RetryInterval; - - private readonly ILogProvider? DebugLog; - private readonly IUnmangedHeap? ClientHeap; - - /// - /// Initializes an emtpy client wrapper that still requires - /// configuration loading - /// - /// An optional debugging log - /// An optional for buffers - public VnCacheClient(ILogProvider? debugLog, IUnmangedHeap? heap = null) - { - DebugLog = debugLog; - //Default to 10 seconds - RetryInterval = TimeSpan.FromSeconds(10); - - ClientHeap = heap; - } - - /// - protected override void Free() - { - _client?.Dispose(); - _client = null; - } - - /// - /// Loads required configuration variables from the config store and - /// intializes the interal client - /// - /// - /// A dictionary of configuration varables - /// - public async Task LoadConfigAsync(PluginBase pbase, IReadOnlyDictionary config) - { - int maxMessageSize = config["max_message_size"].GetInt32(); - string? brokerAddress = config["broker_address"].GetString() ?? throw new KeyNotFoundException("Missing required configuration variable broker_address"); - - //Get keys async - Task clientPrivTask = pbase.TryGetSecretAsync("client_private_key"); - Task brokerPubTask = pbase.TryGetSecretAsync("broker_public_key"); - Task cachePubTask = pbase.TryGetSecretAsync("cache_public_key"); - - //Wait for all tasks to complete - _ = await Task.WhenAll(clientPrivTask, brokerPubTask, cachePubTask); - - using SecretResult clientPriv = await clientPrivTask ?? throw new KeyNotFoundException("Missing required secret client_private_key"); - using SecretResult brokerPub = await brokerPubTask ?? throw new KeyNotFoundException("Missing required secret broker_public_key"); - using SecretResult cachePub = await cachePubTask ?? throw new KeyNotFoundException("Missing required secret cache_public_key"); - - ReadOnlyJsonWebKey clientCert = clientPriv.GetJsonWebKey(); - ReadOnlyJsonWebKey brokerPubKey = brokerPub.GetJsonWebKey(); - ReadOnlyJsonWebKey cachePubKey = cachePub.GetJsonWebKey(); - - RetryInterval = config["retry_interval_sec"].GetTimeSpan(TimeParseType.Seconds); - - Uri brokerUri = new(brokerAddress); - - //Init the client with default settings - FBMClientConfig conf = FBMDataCacheExtensions.GetDefaultConfig(ClientHeap ?? Memory.Shared, maxMessageSize, DebugLog); - - _client = new(conf); - - //Add the configuration to the client - _client.GetCacheConfiguration() - .WithBroker(brokerUri) - .WithVerificationKey(cachePubKey) - .WithSigningCertificate(clientCert) - .WithBrokerVerificationKey(brokerPubKey) - .WithTls(brokerUri.Scheme == Uri.UriSchemeHttps); - } - - /// - /// Discovers nodes in the configured cluster and connects to a random node - /// - /// A to write log events to - /// A token to cancel the operation - /// A task that completes when the operation has been cancelled or an unrecoverable error occured - /// - /// - public async Task RunAsync(ILogProvider Log, CancellationToken cancellationToken) - { - _ = Resource ?? throw new InvalidOperationException("Client configuration not loaded, cannot connect to cache servers"); - - while (true) - { - //Load the server list - ActiveServer[]? servers; - while (true) - { - try - { - Log.Debug("Discovering cluster nodes in broker"); - //Get server list - servers = await Resource.DiscoverCacheNodesAsync(cancellationToken); - break; - } - catch (HttpRequestException re) when (re.InnerException is SocketException) - { - Log.Warn("Broker server is unreachable"); - } - catch (Exception ex) - { - Log.Warn("Failed to get server list from broker, reason {r}", ex.Message); - } - - //Gen random ms delay - int randomMsDelay = RandomNumberGenerator.GetInt32(1000, 2000); - await Task.Delay(randomMsDelay, cancellationToken); - } - - if (servers?.Length == 0) - { - Log.Warn("No cluster nodes found, retrying"); - await Task.Delay(RetryInterval, cancellationToken); - continue; - } - - try - { - Log.Debug("Connecting to random cache server"); - - //Connect to a random server - ActiveServer selected = await Resource.ConnectToRandomCacheAsync(cancellationToken); - Log.Debug("Connected to cache server {s}", selected.ServerId); - - //Wait for disconnect - await Resource.WaitForExitAsync(cancellationToken); - - Log.Debug("Cache server disconnected"); - } - catch (WebSocketException wse) - { - Log.Warn("Failed to connect to cache server {reason}", wse.Message); - continue; - } - catch (HttpRequestException he) when (he.InnerException is SocketException) - { - Log.Debug("Failed to connect to random cache server server"); - //Continue next loop - continue; - } - } - } - } -} diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj index dc73ed8..9895eea 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj +++ b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/VNLib.Plugins.Essentials.Sessions.VNCache.csproj @@ -14,6 +14,8 @@ true True latest-all + True + \\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk @@ -23,9 +25,8 @@ - - + diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs index cd5657d..87e08e7 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSession.cs @@ -23,8 +23,6 @@ */ using VNLib.Net.Http; -using VNLib.Data.Caching; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Essentials.Extensions; using VNLib.Plugins.Sessions.Cache.Client; using static VNLib.Plugins.Essentials.Sessions.ISessionExtensions; @@ -38,7 +36,7 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache protected readonly Func UpdateId; private string? _oldId; - public WebSession(string sessionId, FBMClient client, TimeSpan backgroundTimeOut, Func UpdateId) + public WebSession(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut, Func UpdateId) : base(sessionId, client, backgroundTimeOut) { this.UpdateId = UpdateId; diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs index 5747175..ebc3c3d 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProvider.cs @@ -25,7 +25,7 @@ using System; using VNLib.Net.Http; -using VNLib.Net.Messaging.FBM.Client; +using VNLib.Plugins.Extensions.Loading; using VNLib.Plugins.Sessions.Cache.Client; namespace VNLib.Plugins.Essentials.Sessions.VNCache @@ -33,6 +33,7 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache /// /// The implementation of a VNCache web based session /// + [ConfigurationName("web")] internal sealed class WebSessionProvider : SessionCacheClient, ISessionProvider { static readonly TimeSpan BackgroundUpdateTimeout = TimeSpan.FromSeconds(10); @@ -47,7 +48,7 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache /// The max number of items to store in cache /// The maxium number of waiting session events before 503s are sent /// The session-id factory - public WebSessionProvider(FBMClient client, int maxCacheItems, uint maxWaiting, IWebSessionIdFactory factory) : base(client, maxCacheItems) + public WebSessionProvider(IRemoteCacheStore client, int maxCacheItems, uint maxWaiting, IWebSessionIdFactory factory) : base(client, maxCacheItems) { this.factory = factory; MaxConnections = maxWaiting; @@ -69,7 +70,7 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache return newid; } - protected override RemoteSession SessionCtor(string sessionId) => new WebSession(sessionId, Client, BackgroundUpdateTimeout, UpdateSessionId); + protected override RemoteSession SessionCtor(string sessionId) => new WebSession(sessionId, Store, BackgroundUpdateTimeout, UpdateSessionId); public async ValueTask GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) { diff --git a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs index 8e25416..309db47 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions.VNCache/WebSessionProviderEntry.cs @@ -25,29 +25,26 @@ using System.Text.Json; using VNLib.Net.Http; -using VNLib.Utils.Memory; using VNLib.Utils.Logging; using VNLib.Utils.Extensions; -using VNLib.Plugins.Extensions.Loading; +using VNLib.Data.Caching; +using VNLib.Plugins.Sessions.Cache.Client; using VNLib.Plugins.Essentials.Sessions.Runtime; -using VNLib.Data.Caching.Extensions; +using VNLib.Plugins.Extensions.Loading; +using VNLib.Plugins.Extensions.VNCache; namespace VNLib.Plugins.Essentials.Sessions.VNCache { public sealed class WebSessionProviderEntry : IRuntimeSessionProvider { - const string VNCACHE_CONFIG_KEY = "vncache"; const string WEB_SESSION_CONFIG = "web"; private WebSessionProvider? _sessions; - public bool CanProcess(IHttpEvent entity) - { - //Web sessions can always be provided so long as cache is loaded - return _sessions != null; - } + //Web sessions can always be provided so long as cache is loaded + bool IRuntimeSessionProvider.CanProcess(IHttpEvent entity) => _sessions != null; - public ValueTask GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) + ValueTask ISessionProvider.GetSessionAsync(IHttpEvent entity, CancellationToken cancellationToken) { return _sessions!.GetSessionAsync(entity, cancellationToken); } @@ -55,77 +52,31 @@ namespace VNLib.Plugins.Essentials.Sessions.VNCache void IRuntimeSessionProvider.Load(PluginBase plugin, ILogProvider localized) { //Try get vncache config element - IReadOnlyDictionary cacheConfig = plugin.GetConfig(VNCACHE_CONFIG_KEY); - - IReadOnlyDictionary webSessionConfig = plugin.GetConfig(WEB_SESSION_CONFIG); + IReadOnlyDictionary webSessionConfig = plugin.GetConfigForType(); uint cookieSize = webSessionConfig["cookie_size"].GetUInt32(); string cookieName = webSessionConfig["cookie_name"].GetString() ?? throw new KeyNotFoundException($"Missing required element 'cookie_name' for config '{WEB_SESSION_CONFIG}'"); string cachePrefix = webSessionConfig["cache_prefix"].GetString() ?? throw new KeyNotFoundException($"Missing required element 'cache_prefix' for config '{WEB_SESSION_CONFIG}'"); + int cacheLimit = (int)webSessionConfig["cache_size"].GetUInt32(); + uint maxConnections = webSessionConfig["max_waiting_connections"].GetUInt32(); TimeSpan validFor = webSessionConfig["valid_for_sec"].GetTimeSpan(TimeParseType.Seconds); //Init id factory WebSessionIdFactoryImpl idFactory = new(cookieSize, cookieName, cachePrefix, validFor); - //Run client connection - _ = plugin.DeferTask(() => WokerDoWorkAsync(plugin, localized, idFactory, cacheConfig, webSessionConfig)); - } - - - /* - * Starts and monitors the VNCache connection - */ - - private async Task WokerDoWorkAsync( - PluginBase plugin, - ILogProvider localized, - WebSessionIdFactoryImpl idFactory, - IReadOnlyDictionary cacheConfig, - IReadOnlyDictionary webSessionConfig) - { - //Init cache client - using VnCacheClient cache = new(plugin.IsDebug() ? localized : null, Memory.Shared); - - try - { - int cacheLimit = (int)webSessionConfig["cache_size"].GetUInt32(); - uint maxConnections = webSessionConfig["max_waiting_connections"].GetUInt32(); - - //Try loading config - await cache.LoadConfigAsync(plugin, cacheConfig); - - //Init provider - _sessions = new(cache.Resource!, cacheLimit, maxConnections, idFactory); - - localized.Information("Session provider loaded"); + //Get shared global-cache + IGlobalCacheProvider globalCache = plugin.GetGlobalCache(); - //Listen for cache table events - _ = plugin.DeferTask(() => _sessions.CleanupExpiredSessionsAsync(localized, plugin.UnloadToken)); + //Create cache store from global cache + GlobalCacheStore cacheStore = new(globalCache); - //Run and wait for exit - await cache.RunAsync(localized, plugin.UnloadToken); + //Init provider + _sessions = new(cacheStore, cacheLimit, maxConnections, idFactory); - } - catch (OperationCanceledException) - { } - catch (KeyNotFoundException e) - { - localized.Error("Missing required configuration variable for VnCache client: {0}", e.Message); - } - catch (FBMServerNegiationException fne) - { - localized.Error("Failed to negotiate connection with cache server {reason}", fne.Message); - } - catch (Exception ex) - { - localized.Error(ex, "Cache client error occured in session provider"); - } - finally - { - _sessions = null; - } + //Load and run cached sessions on deferred task lib + _ = plugin.DeferTask(() => _sessions.CleanupExpiredSessionsAsync(localized, plugin.UnloadToken), 1000); - localized.Information("Cache client exited"); + localized.Information("Session provider loaded"); } } } \ No newline at end of file diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs b/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs index f58129a..07ae04b 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs +++ b/Libs/VNLib.Plugins.Essentials.Sessions/MemorySessionEntrypoint.cs @@ -77,10 +77,10 @@ namespace VNLib.Plugins.Essentials.Sessions.Memory _ = plugin.DeferTask(() => _sessions.CleanupExiredAsync(localized, plugin.UnloadToken)); //Schedule garbage collector - _ = plugin.ScheduleInterval(this, TimeSpan.FromMinutes(1)); + plugin.ScheduleInterval(this, TimeSpan.FromMinutes(1)); //Call cleanup on exit - _ = plugin.UnloadToken.RegisterUnobserved(_sessions.Cleanup); + _ = plugin.RegisterForUnload(_sessions.Cleanup); } Task IIntervalScheduleable.OnIntervalAsync(ILogProvider log, CancellationToken cancellationToken) diff --git a/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj b/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj index c4a65f5..78fe298 100644 --- a/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj +++ b/Libs/VNLib.Plugins.Essentials.Sessions/VNLib.Plugins.Essentials.Sessions.Memory.csproj @@ -8,6 +8,8 @@ Vaughn Nugent Copyright © 2022 Vaughn Nugent https://www.vaughnnugent.com/resources + True + \\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs new file mode 100644 index 0000000..df3c564 --- /dev/null +++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/GlobalCacheStore.cs @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Sessions.Cache.Client +* File: GlobalCacheStore.cs +* +* GlobalCacheStore.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Sessions.Cache.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Sessions.Cache.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.Threading; +using System.Threading.Tasks; + +using VNLib.Data.Caching; + +namespace VNLib.Plugins.Sessions.Cache.Client +{ + /// + /// A wrapper class to provide a from + /// a client instance + /// + public sealed class GlobalCacheStore : IRemoteCacheStore + { + private readonly IGlobalCacheProvider _cache; + + public GlobalCacheStore(IGlobalCacheProvider globalCache) + { + _cache = globalCache ?? throw new ArgumentNullException(nameof(globalCache)); + } + + /// + public Task AddOrUpdateObjectAsync(string objectId, string? newId, T obj, CancellationToken cancellationToken = default) + { + return _cache.AddOrUpdateAsync(objectId, newId, obj, cancellationToken); + } + + /// + public Task DeleteObjectAsync(string objectId, CancellationToken cancellationToken = default) + { + return _cache.DeleteAsync(objectId, cancellationToken); + } + + /// + public Task GetObjectAsync(string objectId, CancellationToken cancellationToken = default) + { + return _cache.GetAsync(objectId, cancellationToken); + } + } +} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs new file mode 100644 index 0000000..2a8bd49 --- /dev/null +++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/IRemoteCacheStore.cs @@ -0,0 +1,48 @@ +/* +* Copyright (c) 2022 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Sessions.Cache.Client +* File: IRemoteCacheStore.cs +* +* IRemoteCacheStore.cs is part of VNLib.Plugins.Sessions.Cache.Client which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Sessions.Cache.Client is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Sessions.Cache.Client is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System.Threading; +using System.Threading.Tasks; + +namespace VNLib.Plugins.Sessions.Cache.Client +{ + /// + /// Represents an asynchronous interface to a remote cache store + /// + public interface IRemoteCacheStore + { + /// + /// Gets an object from the cache provider by key + /// + /// The data type + /// The key/id of the object to recover + /// A token to cancel the operation + /// A task that resolves the found object or null otherwise + Task GetObjectAsync(string objectId, CancellationToken cancellationToken = default); + + Task AddOrUpdateObjectAsync(string objectId, string? newId, T obj, CancellationToken cancellationToken = default); + + Task DeleteObjectAsync(string objectId, CancellationToken cancellationToken = default); + } +} diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs index 3b61f68..af2c969 100644 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs +++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/RemoteSession.cs @@ -30,10 +30,8 @@ using System.Collections.Generic; using Microsoft.VisualStudio.Threading; using VNLib.Net.Http; -using VNLib.Data.Caching; using VNLib.Data.Caching.Exceptions; using VNLib.Utils.Extensions; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Essentials.Sessions; using VNLib.Plugins.Essentials.Extensions; @@ -47,7 +45,7 @@ namespace VNLib.Plugins.Sessions.Cache.Client { protected const string CREATED_TIME_ENTRY = "__.i.ctime"; - protected FBMClient Client { get; } + protected IRemoteCacheStore Client { get; } protected TimeSpan UpdateTimeout { get; } private readonly AsyncLazyInitializer Initializer; @@ -57,7 +55,7 @@ namespace VNLib.Plugins.Sessions.Cache.Client /// protected Dictionary? DataStore; - protected RemoteSession(string sessionId, FBMClient client, TimeSpan backgroundTimeOut) + protected RemoteSession(string sessionId, IRemoteCacheStore client, TimeSpan backgroundTimeOut) { SessionID = sessionId; UpdateTimeout = backgroundTimeOut; diff --git a/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs b/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs index 2e72391..800ad66 100644 --- a/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs +++ b/Libs/VNLib.Plugins.Sessions.Cache.Client/SessionCacheClient.cs @@ -32,7 +32,6 @@ using VNLib.Net.Http; using VNLib.Utils.Async; using VNLib.Utils.Logging; using VNLib.Utils.Memory.Caching; -using VNLib.Net.Messaging.FBM.Client; using VNLib.Plugins.Essentials.Sessions; namespace VNLib.Plugins.Sessions.Cache.Client @@ -97,19 +96,19 @@ namespace VNLib.Plugins.Sessions.Cache.Client /// /// The client used to communicate with the cache server /// - protected FBMClient Client { get; } + protected IRemoteCacheStore Store { get; } /// /// Initializes a new /// /// /// The maximum number of sessions to keep in memory - protected SessionCacheClient(FBMClient client, int maxCacheItems) + protected SessionCacheClient(IRemoteCacheStore client, int maxCacheItems) { MaxLoadedEntires = maxCacheItems; CacheLock = new(); CacheTable = new(maxCacheItems); - Client = client; + Store = client; } private ulong _waitingCount; @@ -202,12 +201,6 @@ namespace VNLib.Plugins.Sessions.Cache.Client /// 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 @@ -229,9 +222,6 @@ namespace VNLib.Plugins.Sessions.Cache.Client log.Error(ex); } } - - //remove handler - Client.ConnectionClosed -= OnConnectionClosed; } /// 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 a75ebe3..8271ed2 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 @@ -5,6 +5,8 @@ Vaughn Nugent Copyright © 2022 Vaughn Nugent 1.0.1.1 + True + \\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk @@ -38,7 +40,9 @@ - + + + -- cgit