The scenario is as follows. I start an instance of MVC app to debug it. The app uses simple membership and I log in during this run. Then I go back to VS change something and start the instance again. It doesn't happen really often but sometimes at this moment membership starts acting odd. As the app starts, some action, that is behind [Authorize] attribute (to be exact the attribute is on the controller), is called. However the action fails because WebSecurity.CurrentUserId is equal -1 (the action in question just loads some user information based on WebSecurity.CurrentUserId).
If I clear cookies in browser, everything is fine, but I can't expect users to do the same when they encounter the problem.
My colleague explaind to me that it's (probably) happening because my local IIS decided to restart and some of session cookies became invalid, but if this can happen on local instance of IIS, wouldn't it be possible to also happen on the remote server?
Other important fact, the action that fails is called (more like redirected to) by a custom filter that we wrote. This filter is applied to all actions (but doesn't affect the one mentioned). Can this filter somehow make MVC ignore [Authorize] attribute?
I have a dirty workaround for this problem that should work (with this specific app), but I would prefer to prevent the problem from appearing int the first place.
I think this is related to this. Basically when the server gets reset authentication cookies die. They get recreated right away, except my app doesn't really have access to them till the page is reloaded (just like with logging in).
I partially solved the problem described above (a redirect is preformed somewhere on the way) so the application no longer gets stuck. However, if someone was logged in during the time the server restarted and he tries to preform a post after that, his post will not work and he will be redirected to a get action with the same name as the post action (our custom filter is to blame for that). Unfortunately I cannot fix the filter, because I would need user id for that and at the point at which the filter is called, it's still -1.
I guess my question is not too well written and kind of very localized (I should probably rewrite it or reask it), but the underlaying problem is more general than it seems, so let me salvage all the useful information into this answer.
Question 1: There is nothing preventing IIS from having a hiccup on a remote server and restarting the app, so yes this can (and happens) on the remote server (frequency will depend on the app itself and IIS configuration). The problem of disappearing session data seems to be related to the restarts of the app pool rather than the app itself.
Question 2: The custom filter has little to do with the situation. As pointed by Larry, in simple membership authorization is kind of unrelated to session data. If your session data is lost, the user does not stop being authorized, however user data is stored in the session. Without session you don't know who the user is. This information becomes available one action after session data was lost. So loosing session data can lead to a crash of the application or like in my case (where a custom filter depends on user data) to even weirder results.
So if you encounter unexpected disappearance of user data in your app (such as WebSecurity.CurrentUserId becoming -1), it might be worth investigating if your app pool is getting restarted (and why). Setting memory limits for an app pool seems to increase the likelihood of those restarts.
Related
I posted another question as a brute-force solution to this one (Angular: fully install service worker before anything else) but I thought I'd make a separate one to discuss the use case for when a service worker is used as intended.
According to the service worker life cycle (https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle), the SW is installed but it's only active once you then reload the page (you can claim() the page but that's only for calls that happen after the service worker is installed). The reasoning is that if and existing version is updated, the old one and the new one do not mix states and caches. I can agree with that decision.
What I have trouble understanding is why it is not immediately active once it is initially installed. Instead, it requires a page reload unless you explicitly define precaching rules in the SW. If you define caching rules with wildcards, it's not possible to precache those so you need the reload.
Given a single page PWA (like Angular), a user will discover the site and browser around on it but the page will never be reloaded during that session. If they then want to use the site offline later, they need to have refreshed or re-opened the tab at least one other time. That seems like a pretty big pitfall to me.
Am I missing something here?
Your understanding of the service worker lifecycle is correct but I do not think the pitfall you mentioned is as severe as you think it is.
If I understand you correctly, the user experience will only be negatively affected if the user loses connectivity during the initial browsing of the page (before the service worker is active) and is missing an offline asset. If this is truly a scenario you want to account for then that offline asset can be pre-cached in the browser-side javascript. Alternatively, as you mentioned, you can skipWaiting() and claim() to make the service worker active without the user refreshing the page.
I'm considering using Amazon RDS with read replicas to scale our database.
Some of our controllers in our web application are read/write, some of them are read-only. We already have an automated way for identifying which controllers are read-only, so my first approach would have been to open a connection to the master when requesting a read/write controller, else open a connection to a read replica when requesting a read-only controller.
In theory, that sounds good. But then I stumbled open the replication lag concept, which basically says that a replica can be several seconds behind the master.
Let's imagine the following use case then:
The browser posts to /create-account, which is read/write, thus connecting to the master
The account is created, transaction committed, and the browser gets redirected to /member-area
The browser opens /member-area, which is read-only, thus connecting to a replica. If the replica is even slightly behind the master, the user account might not exist yet on the replica, thus resulting in an error.
How do you realistically use read replicas in your application, to avoid these potential issues?
I worked with application which used pseudo-vertical partitioning. Since only handful of data was time-sensitive the application usually fetched from slaves and from master only in selected cases.
As an example: when the User updated their password application would always ask master for authentication prompt. When changing non-time sensitive data (like User Preferences) it would display success dialog along with information that it might take a while until everything is updated.
Some other ideas which might or might not work depending on environment:
After update compute entity checksum, store it in application cache and when fetching the data always ask for compliance with checksum
Use browser store/cookie for storing delta ensuring User always sees the latest version
Add "up-to-date" flag and invalidate synchronously on every slave node before/after update
Whatever solution you choose keep in mind it's subject of CAP Theorem.
This is a hard problem, and there are lots of potential solutions. One potential solution is to look at what facebook did,
TLDR - read requests get routed to the read only copy, but if you do a write, then for the next 20 seconds, all your reads go to the writeable master.
The other main problem we had to address was that only our master
databases in California could accept write operations. This fact meant
we needed to avoid serving pages that did database writes from
Virginia because each one would have to cross the country to our
master databases in California. Fortunately, our most frequently
accessed pages (home page, profiles, photo pages) don't do any writes
under normal operation. The problem thus boiled down to, when a user
makes a request for a page, how do we decide if it is "safe" to send
to Virginia or if it must be routed to California?
This question turned out to have a relatively straightforward answer.
One of the first servers a user request to Facebook hits is called a
load balancer; this machine's primary responsibility is picking a web
server to handle the request but it also serves a number of other
purposes: protecting against denial of service attacks and
multiplexing user connections to name a few. This load balancer has
the capability to run in Layer 7 mode where it can examine the URI a
user is requesting and make routing decisions based on that
information. This feature meant it was easy to tell the load balancer
about our "safe" pages and it could decide whether to send the request
to Virginia or California based on the page name and the user's
location.
There is another wrinkle to this problem, however. Let's say you go to
editprofile.php to change your hometown. This page isn't marked as
safe so it gets routed to California and you make the change. Then you
go to view your profile and, since it is a safe page, we send you to
Virginia. Because of the replication lag we mentioned earlier,
however, you might not see the change you just made! This experience
is very confusing for a user and also leads to double posting. We got
around this concern by setting a cookie in your browser with the
current time whenever you write something to our databases. The load
balancer also looks for that cookie and, if it notices that you wrote
something within 20 seconds, will unconditionally send you to
California. Then when 20 seconds have passed and we're certain the
data has replicated to Virginia, we'll allow you to go back for safe
pages.
(APEX 4.1.1.00.23)
I have two applications A and B that share the same session (because they use the same session cookie), and each has Maximum Session Idle Time set to the same value N. Having established a session and visited both applications, if I then spend more than N seconds working in application A (doing lots of page loads so not timing out), if I then navigate to application B it immediately times out and sends me to its login page.
I tried also calling APEX_UTIL.SET_SESSION_MAX_IDLE_SECONDS(N) in both applications, with p_scopr defaulting to 'SESSION', noting that the API docs say
This would be the most common use case when multiple Application
Express applications use a common authentication scheme and are
designed to operate as a suite in a common session.
However the same thing happens.
I want the timeout to apply to the session as a whole, not to each application independently. Is this not what the above is supposed to achieve, or am I doing something wrong?
I got the answer to this from Christian Neumueller on the Oracle APEX forum:
... it's no issue anymore in 4.2. Looking at the 4.1.1
code, it seems that the problem is how we stored the last access time.
While the APEX_UTIL call with SESSION scope would set the idle timeout
for both apps, we maintained a timer (FSP_LAST_REQUEST_TIME) for each
app. Working in TIMTEST1 only updated the timer for TIMTEST1, not for
TIMTEST2. After working with one app and switching back to the other
app, Apex sees the stale timer and decides that the session expired.
This is clearly a bug. The bad news is that a backport is not
feasible, because so much has changed in session state management.
I have an MVC 4 app and am using the default authentication provider. I'm not using persistent cookies.
I don't have any problems in development but when hosted at HostGator, I SOMETIMES get logged off when I try to create a new item (HTTP POST). When this happens, I end up at the log on page like I wasn't authenticated.
HostGator does NOT have the app on multiple web servers so I'm thinking I shouldn't have to worry about machinekey stuff. Am I wrong?
When this happens, I just log in again and create the item again and it will succeed. Once this happens, I can't recreate the issue. I try reopening the browser and even different browsers but creating items will always work. It only seems to happen again if I try much later.
Some additional info, the timeout is set to 2880 (the default for an MVC project), which I know is long but I can't see how it would be related. Still, thought I'd mention it.
So I can't look at IIS logs or event viewer to get any idea what could be happening but I can add more logging to the app. Can anyone provide ideas for what to check or what logging to add to diagnose?
Thanks
EDIT
I realized that I could get to the IIS logs so I compared the POST that succeeded and the one that failed and immediately noticed something.
When I first did the GET to load the Item/Create page/view, the cs-username was populated but when I did the POST to create the item, it was gone. I can see that when I logged in again and was able to successfully create the item, that POST did have the cs-username populated.
Why would it disappear between the GET and the POST? There was a 7 minute delay from the GET to the POST but I can see I logged on 1 minute before the GET so the session was only 8 minutes old when the post happened. I've double checked that I don't have sessionstate explicitly configured so the default should be 20 minutes. I feel like I'm onto something but not sure exactly what.
Might be worth adding Glimpse, although running that on deployed code is kinda risky. It would have the benefit, though, of letting you see what's actually happening on the server. I've never used HostGator, so I can't say for certain, but if they recycle app pools aggressively, that would invalid your login, and explain why the logoff seems to happen randomly.
Does anybody have any quick and clever ways to flip an MVC app running on Windows Azure into a "maintenace mode"
I don't have a huge need for this because I use the azure staging environment a lot but occasionally I do have the need to make sure there are no users in the production instance of the application (mainly database updates).
I'd like to be able to do this on the fly without uploading new code or swapping deployment slots. Any suggestions?
The friendliest way to do it is on login. When a user authenticates, check a maintenance mode flag in the database and don't let them log in. Let active users continue to use the application until they log out or their session times out. Keep an activity log so you can know when all users have expired.
Of course this means it will take time from when you put the app into maintenance mode and when it is effectively ready, but it's not nice to boot out an active user.
If the usage pattern of your app makes it so this methodology will not ensure no activity in a reasonable time, you can add a timeout on top of this. Check the same maintenance flag for a request every so often. Doesn't have to be every request but every five minutes or so. If necessary you can also cache the maintenance mode value locally for a reasonable period of time (a few minutes).
I would use routing for this. Have the flag be inspected during routing configuration. If it is on, route to "Maintenance" screens
I would suggest adding a Global Action Filter that respects you maintenance mode Flag.