Cache disappears after the server restarts - asp.net-mvc-4

I am testing the behaviour of cache and this are my test snippets:
At first, these lines of codes will be executed:
HttpRuntime.Cache.Insert("test", "true",
// No Dependencies
null,
// No absolute expiration (mimic the behavior of forms authentication)
System.Web.Caching.Cache.NoAbsoluteExpiration,
// Timeout 20 minutes after the last access
// (to mimic the behavior of forms authentication)
new TimeSpan(0, 20, 0),
// Setting to NotRemovable ensures that if the
// application pool restarts, you don't lose your cache
System.Web.Caching.CacheItemPriority.NotRemovable,
// No callback needed here
null);
so i have inserted a key and a value to my cache.
I will stop my server, remove that snippet and replace it with
ViewBag.test = "asd" + HttpRuntime.Cache["test"]; to check the previous cache, but it's gone already since it's not displaying anything.
Isn't it meant to be there or this is the normal behavior for it?
I did some researching but i am not sure if this is client side caching. I am kinda new to this and clarifications will be very much appreciated.

The cache is an in-memory cache that resides in your application's memory on the server, and stopping the server stops the program, clearing all its memory. So no, you can expect to never have cached values persist after a restart.

Related

How does the distributed executor service in Redisson work with regards to scoping / closuring?

If I push a Runnable to a redisson distributed executor service, what rules am I required to oblige by?
Surely , I can not have free reign, I do not see how that is possible, yet, it is not mention in the docs at all, nor are any rules apparently enforced by the API, like R extends Serializable or similar.
If I pass this runnable:
new Runnable(()-> {
// What can I access here, and have it be recreated in whatever server instance picks it up later for execution?
// newlyCreatedInstanceCreatedJustBeforeThisRunnableWasCreated.isAccissible(); // ?
// newlyComplexInstanceSuchAsADatabaseDriverThatisAccessedHere.isAccissible(); // ?
// transactionalHibernateEntityContainingStaticReferencesToComplexObjects....
// I think you get the point.
// Does Redisson serialize everything within this scope?
// When it is recreated later, surely, I can not have access to those exact objects, unless they run on the same server, right?
// If the server goes does and up, or another server executes this runnable, then what happens?
// What rules do we have to abide by here?
})
Also, what rules do we have to abide by when pushing something to a RQueue, RBlockingDequeu, or Redisson live objects?
It is not clear from the docs.
Also, would be great if a link to a single site documentation site could be provided. The one here requires a lot of clickin and navigation:
https://github.com/redisson/redisson/wiki/Table-of-Content
https://github.com/redisson/redisson/wiki/9.-distributed-services#933-distributed-executor-service-tasks
You can have an access to RedisClient and taskId. Full state of task object will be serialized.
TaskRetry setting applied to each task. If task isn't executed after 5 minutes since the moment of start then it will requeued.
I agree that the documentation is lacking some "under the hood" explanations.
I was able to execute db reads and inserts through the Callable/runnable that was submitted to the remote ExecutorService.
I configured a single Redis on a remote VM, the database and the app running locally on my laptop.
The tasks were executed without any errors.

HttpContext.Session in Blazor Server Application

I am trying to use HttpContext.Session in my ASP.NET Core Blazor Server application (as described in this MS Doc, I mean: all correctly set up in startup)
Here is the code part when I try to set a value:
var session = _contextAccessor.HttpContext?.Session;
if (session != null && session.IsAvailable)
{
session.Set(key, data);
await session.CommitAsync();
}
When this code called in Razor component's OnAfterRenderAsync the session.Set throws following exception:
The session cannot be established after the response has started.
I (probably) understand the message, but this renders the Session infrastructure pretty unusable: the application needs to access its state in every phase of the execution...
Question
Should I forget completely the DistributedSession infrastructure, and go for Cookies, or Browser SessionStorage? ...or is there a workaround here still utilizing HttpContext.Session? I would not want to just drop the distributed session infra for a way lower level implementation...
(just for the record: Browser's Session Storage is NOT across tabs, which is a pain)
Blazor is fundamentally incompatible with the concept of traditional server-side sessions, especially in the client-side or WebAssembly hosting model where there is no server-side to begin with. Even in the "server-side" hosting model, though, communication with the server is over websockets. There's only one initial request. Server-side sessions require a cookie which must be sent to the client when the session is established, which means the only point you could do that is on the first load. Afterwards, there's no further requests, and thus no opportunity to establish a session.
The docs give guidance on how to maintain state in a Blazor app. For the closest thing to traditional server-side sessions, you're looking at using the browser's sessionStorage.
Note: I know this answer is a little old, but I use sessions with WebSockets just fine, and I wanted to share my findings.
Answer
I think this Session.Set() error that you're describing is a bug, since Session.Get() works just fine even after the response has started, but Session.Set() doesn't. Regardless, the workaround (or "hack" if you will) includes making a throwaway call to Session.Set() to "prime" the session for future writing. Just find a line of code in your application where you KNOW the response hasn't sent, and insert a throwaway call to Session.Set() there. Then you will be able to make subsequent calls to Session.Set() with no error, including ones after the response has started, inside your OnInitializedAsync() method. You can check if the response is started by checking the property HttpContext.Response.HasStarted.
Try adding this app.Use() snippet into your Startup.cs Configure() method. Try to ensure the line is placed somewhere before app.UseRouting():
...
...
app.UseHttpsRedirection();
app.UseStaticFiles();
//begin Set() hack
app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
//this throwaway session variable will "prime" the Set() method
//to allow it to be called after the response has started
var TempKey = Guid.NewGuid().ToString(); //create a random key
Context.Session.Set(TempKey, Array.Empty<byte>()); //set the throwaway session variable
Context.Session.Remove(TempKey); //remove the throwaway session variable
await Next(); //continue on with the request
});
//end Set() hack
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
...
...
Background Info
The info I can share here is not Blazor specific, but will help you pinpoint what's happening in your setup, as I've come across the same error myself. The error occurs when BOTH of the following criteria are met simultaneously:
Criteria 1. A request is sent to the server with no session cookie, or the included session cookie is invalid/expired.
Criteria 2. The request in Criteria 1 makes a call to Session.Set() after the response has started. In other words, if the property HttpContext.Response.HasStarted is true, and Session.Set() is called, the exception will be thrown.
Important: If Criteria 1 is not met, then calling Session.Set() after the response has started will NOT cause the error.
That is why the error only seems to happen upon first load of a page--it's because often in first loads, there is no session cookie that the server can use (or the one that was provided is invalid or too old), and the server has to spin up a new session data store (I don't know why it has to spin up a new one for Set(), that's why I say I think this is a bug). If the server has to spin up a new session data store, it does so upon the first call to Session.Set(), and new session data stores cannot be spun up after the response has started. On the other hand, if the session cookie provided was a valid one, then no new data store needs to be spun up, and thus you can call Session.Set() anytime you want, including after the response has started.
What you need to do, is make a preliminary call to Session.Set() before the response gets started, so that the session data store gets spun up, and then your call to Session.Set() won't cause the error.
SessionStorege has more space than cookies.
Syncing (two ways!) the sessionStorage is impossible correctly
I think you are thinking that if it is on the browser, how can you access that in C#? Please see some examples. It actually read from the browser and transfers (use) on the server side.
sessionstorage and localstorage in blazor are encrypted. We do not need to do extra for encryption. The same applies for serialization.

Redis-backed session state not saving everything

We are trying to move from server session (IIS) to Redis-backed session. I updated my web.config with the custom sessionState configuration. I'm finding that only SOME of my key/value pairs are being saved. Of the 5 I expect to be in there, there are only 2. I verified all my code is ultimately hitting HttpContext.Current.Session.Add. I verified that my POCOs are marked as serializable. Looking at monitoring, I see that it adds the first two pairs, then everything after that just doesn't make it. No hit, no rejection, no exceptions. Nothing.
Anyone ever see this? Know where I could start to look to resolve?
TIA,
Matt
Update 1: I've switched to using a JSON serializer to store my data. Same thing. Doesn't seem to be a serialization issue.
Update 2: I've now downloaded the source code, compiled and am debugging it. The method SetAndReleaseItemExclusive, which seems to send the session items to Redis, is only hit once, though it should be hit more than once as my web site handle SSO and bounces from page to page to load the user and such. Have to investigate why it's only firing once...
Figured it out. Turns out that my AJAX request to an "API" endpoint without my MVC app did not have the appropriate session state attached. Therefore, the SetAndReleaseItemExclusive was never called. Adding this fixed it:
protected void Application_PostAuthorizeRequest()
{
if (HttpContext.Current.Request.Url.LocalPath == "/api/user/load")
HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
else
HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
}

Session lost while switching from one context to another in tomcat

I have a tomcat server on which I have deployed 10 web applications.
I have a Filter which reads an incoming request from one application (say "A"), reads a certain cookie value, and gets another context name (Say "B") from the database based on the value of the cookie.
This filter then rewrites the request with context A(e.g. http://1.1.1.1/A/XYZServlet) to a new request having context as B (e.g. http://1.1.1.1/B/XYZServlet).
On doing this, all the session variables are lost when the new request gets executed.
How can I make sure that the session and all the session variables(related to context A) are retained when my filter redirects the old request(context A) to the new request(context B) ?
Setting emptySessionPath = "true" in server.xml cannot be a solution due to certain architecture reasons.
Tomcat sessions are scoped to a single web application. In your case, when you switch to a different context, the session data is not lost, but simply not applicable to the new context. The scoping is controlled by the session cookie's path, it will contain the context of the creating web application and thus be only sent to the Tomcat for requests within that context.
If you want to extend the session's scope, several options come to mind: You could modify the session cookie (JSESSIONID by default) to the root context path /. Yet session cookie management is very deeply embedded in Tomcat and hard (if not impossible) to modify. I once tried to change the session cookie to be persistent with a fixed validity duration, but didn't find a way to do so.
A less elegant but maybe working way would be to duplicate the session cookie. I'm not sure if it will work, but in your filter, you could read the session cookie and create a duplicate with the target context as path. On subsequent requests, the faked session cookie would be included, possibly making your previous session data available.

.Net 4.0 MemoryCache Clearing

I am using a .Net 4.0 MemoryCache in my WCF service.
I originally was using the Default Cache as below:
var cache = MemoryCache.Default;
Then doing the usual pattern as trying to find something in the Cache, getting, then
setting into the cache if did not find (code snippet / pseudo code as below):
var geoCoordinate = cache.Get(cacheKey) as GeoCoordinate;
if (geoCoordinate == null)
{
geoCoordinate = get it from somewhere;
cache.Set(cacheKey, geoCoordinate, DateTimeOffset.Now.AddDays(7));
}
I was finding that my entries were disappearing after approx. 2 minutes. Even if my code placed the missing entries back into the cache, subsequent cache Gets would return null.
My WCF Service is being hosted by IIS 7.5. If I recycled the App Pool, everything would work normally for 2 minutes, but then the pattern as described above would repeat.
After doing some researching I then did the below to replace:
var cache = MemoryCache.Default;
// WITH NEW CODE TO CREATE CACHE AS BELOW:
var config = new NameValueCollection();
//Hack: Set Polling interval to 10 days, so will no clear cache.
config.Add("pollingInterval", "10:00:00:00");
config.Add("physicalMemoryLimitPercentage", "90");
config.Add("cacheMemoryLimitMegabytes", "2000");
//instantiate cache
var cache = new MemoryCache("GeneralCache", config);
It seems that no matter what I place into physicalMemoryLimitPercentage, or cacheMemoryLimitMegabytes does not seem to help. But placing the pollingInterval to a large datespan does.
ie: If I set as below:
config.Add("pollingInterval", "00:00:15:00");
Then everything works fine for 15 minutes.
Note: If my WCF service is hosted by IISExpress on my dev environment, I cannot reproduce.
This also seems to happen when my WCF service is hosted by IIS 7.5.
My app pool on IIS 7.5 is NOT recycling.
Has anybody experienced something like this?
I have seen the below:
MemoryCache does not obey memory limits in configuration
Thanks,
Matt
I too have seen this issue and filed a bug with MS here with a simple reproducer project.
This has been resolved by MS in the above bug I filed - with a work around there and an upcoming QFE for .net 4 as well as confirmation that this isn't a problem in 4.5
I have not yet tried the work around
I can however give some more information on conditions required by myself to recreate this. The application pool needed to be in Integrated Pipeline mode for me to see this issue - Classic mode fixes this issue though removes some of the benefits of moving to IIS 7.5.
Equally when using Integrated mode I also did not see this issue if I used a built-in application pool identity such as ApplicationPoolIdentity. However my app needs to run as a custom identity using a service account and it is at this point at which I see the behavior. Therefore if you don't need Integrated mode or a custom Identity you can maybe work around this.
Perhaps the built-in accounts have permissions to do the cache memory statistics gathering initiated by the pollingInterval that my custom Identity does not have, I don't know.
Hope this helps or even that someone else can join more of the dots to figure out a better work around.