ASP.Net Session State Provider Failover Scenario - asp.net

We had implemented Redis session state provider to our web application and it works like a charm but i wonder what happens if redis server fails or web server couldn't connect to redis server.
Is there any way to use InProc Session State management as failover of Redis?
I cannot find any documentation about declaring multiple session state providers so if redis fails, system can continue to work with using inproc. (I accept to lose session states in redis and start from scratch in case of fail and lose again session states inproc and start from scratch again if redis become available)

You need to define slave for your redis-server and use redis sentinel to monitor your server

I have been having a similar issue with Redis failing as a backing for our session store and I can not find anything that allows for failover/failback to an other SessionStateProvider.
I was hoping there was something out there that would write to both Redis and SqlServer in mem table or similar and then read from 1, if fails read from 2. But, this does not seem to exist yet.

I'm using StackExchange library to connect to redis server.It's just a simple code which just shows how to subscribe to event and don't take it a final solution.Whenever sentinel chooses new server you will receive an event for that so you can select new server.
ConnectionMultiplexer multiplexer =
ConnectionMultiplexer.Connect(new ConfigurationOptions
{
CommandMap = CommandMap.Sentinel,
EndPoints = { { "127.0.0.1", 26379 }, { "127.0.0.1", 26380 } },
AllowAdmin = true,
TieBreaker = "",
ServiceName = "mymaster",
SyncTimeout = 5000
});
multiplexer.GetSubscriber().Subscribe("*", (c, m) =>
{
Debug.WriteLine("the message=" + m);
Debug.WriteLine("channel=" + c);
try
{
var sentinelServer = multiplexer.GetServer("127.0.0.1", 26379).SentinelGetMasterAddressByName("mymaster");
Debug.WriteLine("Current server=" + sentinelServer);
Debug.Flush();
}
catch (Exception)
{
var sentinelServer = multiplexer.GetServer("127.0.0.1", 26380).SentinelGetMasterAddressByName("mymaster");
Debug.WriteLine("Current server=" + sentinelServer );
Debug.Flush();
}
});

Related

Net Core 6: Session ID Changes After Redirect to AuthenticationHandler.AuthenticateAsync()

:: Using A Custom DistributedCache (Redis Implementation) ::
On all Redirect(url) calls, the session changes. I know the official MS Docs says that IAuthenticationHandler "Created per request to handle authentication for a particular scheme". Meaning even redirects cause a new session
public async Task<AuthenticateResult> AuthenticateAsync() { }
The issue I am having is that during the new session ALL CONTEXT is lost. Everything I saved in the Context (including the ClaimsPrincipal of the user) gets lost. I cannot even fetch transients or singletons attached to my service through:
var SessionProvider = Context.RequestServices.GetService(typeof(ISessionProvider)) as SessionProvider;
Because there is no context. Here is the implementation I followed::
ASP.NET Core 2.0 authentication middleware
Please guide me on any assistance on how to persist Session during AuthenticationAysnc() calls.
Edit :: Here is my service code
service.AddAuthentication(options => {
options.DefaultAuthenticateScheme = "CoreAuthScheme";
options.DefaultChallengeScheme = "CoreAuthScheme";
options.DefaultScheme = "CoreAuthScheme";
}).AddCustomAuth(o => {})
And With Web App
//Register Session Security (Goes before Routing)
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
Thank you
Solution ::
Proper Implementation of these two library srcs are needed if you are using a custom IDistributedCache. Obviously The IDistributedCache here is Redis Implementation, but the same goes for SQL, and other DB caching (get the proper src implementation of IDistributedCache)
Session Management
https://github.com/dotnet/aspnetcore/tree/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Middleware/Session/src
IDistributedCache (Redis)
https://github.com/dotnet/aspnetcore/tree/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Caching/StackExchangeRedis/src
The reason why I was losing session is because the default implementation of DistributedSession uses Offsets and special encodings to process values returned from the IDistributedCache Redis I implemented. See below:
DistributedSession.Deserialize(...)
var expectedEntries = DeserializeNumFrom3Bytes(content);
_sessionIdBytes = ReadBytes(content, IdByteCount);
for (var i = 0; i < expectedEntries; i++)
{
var keyLength = DeserializeNumFrom2Bytes(content);
var key = new EncodedKey(ReadBytes(content, keyLength));
var dataLength = DeserializeNumFrom4Bytes(content);
_store.SetValue(key, ReadBytes(content, dataLength));
}
Once you have those two libraries properly sourced and implemented. Session Management Works :). AuthenticateAsync calls the Distributed Session which calls DistributedCache and data is properly serialized and deserialized
----------------------------------------------------------- alimaslax

Why does AbortOnConnectFail option does not work well with Redis Sentinel Connection in ConnectionMultiplexer?

Why does AbortOnConnectFail option in ConfigurationOption for ConnectionMultiplexer from StackExchange.Redis still makes the connection multiplexer to throw Sentinel exception connection errors like:
Unhandled exception. StackExchange.Redis.RedisConnectionException: Sentinel: Failed connecting to configured master for service: mymaster
Is there a good way to configure it so that in case it fails, it will not throw the exception an just retry?
Here is an example of how I connect to Redis:
var redisConnectionConfiguration = new ConfigurationOptions
{
ClientName = "Foo",
EndPoints =
{
{ redisConfigSection["Host"] },
},
Password = redisConfigSection["Password"],
AllowAdmin = true,
AbortOnConnectFail = false,
ReconnectRetryPolicy = new ExponentialRetry(2000),
ServiceName = isRedisSentinelEnabled ? "mymaster" : null,
};
var redisConnection = ConnectionMultiplexer.Connect(redisConnectionConfiguration, Console.Out);
We can use ConnectionMultiplexer deal with disconnection, you can check my smaple code.
Reconnecting with Lazy pattern
We have seen a few rare cases where StackExchange.Redis fails to reconnect after a connection blip (for example, due to patching). Restarting the client or creating a new ConnectionMultiplexer will fix the issue. Here is some sample code that still uses the recommended Lazypattern while allowing apps to force a reconnection periodically. Make sure to update code calling into the ConnectionMultiplexer so that they handle any ObjectDisposedException errors that occur as a result of disposing the old one.

.Net core gRPC unmanaged memory usage

I have a simple service that listens to RabbitMQ, calls some gRPC services and does other stuff like DB updates etc. I started noticing Kubernetes pods failing with OOM exception so I took out all the logic from Consume method and controller constructor and started adding back parameters one by one. Everything is normal until I pass my gRPC client to constructor- then unmanaged memory starts going up with each new message and never goes down (the Consume method is still empty). Any ideas what I'm doing wrong?
My client is added in Program.cs like this:
services.AddGrpcClient<Notification.Api.Notification.NotificationClient>(o => { o.Address = new Uri(sso.GrpcOptions.ApiBaseAddress); })
.ConfigureChannel(c =>
{
c.LoggerFactory = LoggerFactory.Create(logging =>
{
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Warning);
});
c.HttpHandler = new SubdirectoryHandler(new HttpClientHandler()
{
}, sso.GrpcOptions.NotifSubdirectory);
});
Eventually I added gRPC clients with services.AddSingleton() instead of using services.AddGrpcClient() and it fixed the increasing memory consumption.

Reset redis cache expiry using spring data redis

I have a requirement to reset the expire time if the record is accessed before its initial expire time. I am using Spring data redis API to use Redis as Cache. I am using RediscacheManager's setDefaultExpiration(5000) to set default expiration. Unable to find any solutions or documentation about resetting the expiry time. Any guidance is appreciated.
Also, wondering, why couldn't this be a natural feature of Redis Cache, after all, it should get the most used records from cache.
Wrote this method and called from appropriate places. Worked like a charm for me.
public void resetExpire(String keyPattern) {
LOG.debug("Getting Multiple keys from cache with pattern: " + keyPattern);
Set<String> keylist = redisTemplate.keys(keyPattern);
redisTemplate.executePipelined(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
keylist.forEach(key->
redisTemplate.expire(key, 5000, TimeUnit.SECONDS));
return null;
}
});
}

SignalR self host connection issue

I recently created a proof of concept console application using SignalR (self host). It worked a treat for our use. The client connected fine and I was able to send updates from the server to the client. Lovely!
I've now transferred the code from the Console application to a winforms application for a prettier UI. Now that same client won't connect to the server yet it will still connect to the old Console version.
Winforms code:
string url = "http://localhost:8080";
using (WebApp.Start(url))
{
// Let the app know the server is up
}
Console code:
string url = "http://localhost:8080";
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
Client connection code:
if (!connected)
{
int i = 0;
// Try 3 times
while (i <= 2)
{
try
{
string server = Properties.Settings.Default.Server + ":" + Properties.Settings.Default.PortNumber.ToString();
connection = new HubConnection(server);
connection.StateChanged += connection_StateChanged;
hub = connection.CreateHubProxy("MyHub");
connection.Start().Wait();
hub.On<string>("addMessage", param => { UpdateAlarmStatus(param); });
return true;
}
catch (Exception)
{
i++;
}
}
return false;
}
else
{
return true;
}
The error the client is reporting is:
Exception:Thrown: "No connection could be made because the target machine actively refused it" (System.Net.Sockets.SocketException)
A System.Net.Sockets.SocketException was thrown: "No connection could be made because the target machine actively refused it"
Time: 25/01/2015 15:09:23
Thread:Worker Thread[8232]
Why would the target machine (localhost) refuse itself which the Console version doesn't? I've been looking at the code over and over and I cannot see where I'm going wrong. Can anyone point me in the right direction please?
Thank you for reading.
Paul.
I suspect this is an issue with the configuration of your machine/infrastructure rather than the code itself, which looks fine at first glance.
Have you checked the console debug output in Visual Studio? I recently encountered an issue with similar symptoms and that was what gave me the initial clue to keep investigating. In my particular case, an exception was written to the console debug output that didn't make it to the client.
SignalR will normally negotiate with the server automatically to determine the best transport method to use. In a .NET client, the available options are LongPollingTransport, ServerSentEventsTransport and WebSocketTransport. So for some reason, your console app can use at least one of those methods, whereas your WinForms client cannot.
You can perhaps enable tracing to give you more information to work with. To do this, enter the below before you create the hub proxy:
hubConnection.TraceLevel = TraceLevels.All;
hubConnection.TraceWriter = Console.Out;
ASP.NET doco on SignalR tracing

Resources