Losing item from session intermittently - c#

i am little lost...
we are using sqlserver session sharing and on our production (2 servers) and staging (2 servers), i am loosing one item only from the sessions intermittently. Both, staging and production are load balanced and on same servers but point to different databases.
My local development and the dev site (different server) do not have this issue. These both use the dev database.
Sql server session tables and sprocs are specific to the environment since these are setup in dev, staging or production database.
Code to place items in the session
SessionService.AddItem(SessionKeys.LoggedInUser, user); //this is always available
SessionService.AddItem(SessionKeys.Impersonator, inhouseUser); //this is lost intermittently
public static void AddItem(string key, object value)
{
CheckSessionAvailability();
HttpContext.Current.Session[key] = value;
}
Getting the item from the session:
User inhouseUser = SessionService.GetItem<User>(SessionKeys.Impersonator);
public static T GetItem<T>(string key)
{
object item = null;
CheckSessionAvailability();
item = HttpContext.Current.Session[key];
if ((item != null) && !(item is T))
{
throw new ApplicationException("Cannot convert");
}
return (T)item;
}

Our staging site is load balanced... we disabled node 2 and only made node 1 active. It worked without any issue.
Initially, we had no persistence enabled on the staging or production sites. We enabled cookie-based persistence on the staging site, and while we verified the persistence cookies were coming through in the browser, my page still wasn't consistently working as expected. We switched the load balancer to use persistence based on client IP instead, and then it did appear to be working on staging. Will implement the same on production.

Related

ASP.NET Web API: How to create a persistent collection across requests?

I have a Web API providing a backend to an Angular.JS web application. The backend API needs to track the state of user activities. (Example: it needs to note which content ID a user last retrieved from the API)
Most access to the API is authenticated via username/password. For these instances, it works fine for me to store the user state in our database.
However, we do need to allow "guest" access to the service. For guests, the state does need to be tracked but should not be persisted long-term (e.g. session-level tracking). I'd really like to not have to generate "pseudo users" in our user table just to store the state for guest users, which does not need to be maintained for a significant period of time.
My plan is to generate a random value and store it in the client as a cookie. (for guests only - we use bearer authentication for authenticated users.) I would then store whatever state is necessary in an in-memory object, such as a Dictionary, using the random value as a key. I could then expire items off the dictionary periodically. It is perfectly acceptable for this data to be lost if the Web API is ever relaunched, and it would even be acceptable for the dictionary to be reset say, every day at a certain time.
What I don't know how to do in WebAPI is create the dictionary object, so that it will persist across Web API calls. I basically need a singleton dictionary object that will maintain its contents for as long as the server is running the Web API (barring a scheduled clearing or programmatic flushing)
I had the idea of dumping the Dictionary off to disk every time an API call is made, and then reading it back in when it's needed, but this does not allow for multiple simultaneous in-flight requests. The only method I can think of right now is to add another database table (guest_state or something) and replicate the users table, and then setup some sort of manual method to regularly clean out the data in the guest table.
Summary: what I need is
a way to store some data persistently in a Web API backend without having to go off to a database
preferably store this data in a Dictionary object so I can use randomly-generated session IDs as the key, and an object to store the state
the data is OK to be cleared after a set period of time or on a regular basis (not too frequently, maybe a minimum of a 6 hour persistence)
I figured out a solution using the Singleton pattern:
public static class Services
{
private static Dictionary<string, string> cache;
private static object cacheLock = new object();
public static Dictionary<string,string> AppCache
{
get
{
lock (cacheLock)
{
if (cache == null)
{
cache = new Dictionary<string, string>();
}
return cache;
}
}
}
}
public class testController()
{
[HttpGet]
public HttpResponseMessage persist()
{
HttpResponseMessage hrm = Request.CreateResponse();
hrm.StatusCode = HttpStatusCode.OK;
Services.AppCache.Add(Guid.NewGuid().ToString(), DateTime.Now.ToString());
string resp = "";
foreach (string s in Services.AppCache.Keys)
{
resp += String.Format("{0}\t{1}\n", s, Services.AppCache[s]);
}
resp += String.Format("{0} records.", Services.AppCache.Keys.Count);
hrm.Content = new StringContent(resp, System.Text.Encoding.ASCII, "text/plain");
return hrm;
}
}
It seems the Services.AppCache object successfully holds onto data until either the idle timeout expires or the application pool recycles. Luckily I can control all of that in IIS, so I moved my app to its own AppPool and setup the idle timeout and recycling as appropriate, based on when I'm ok with the data being flushed.
Sadly, if you don't have control over IIS (or can't ask the admin to set the settings for you), this may not work if the default expirations are too soon for you... At that point using something like a LocalDB file or even a flat JSON file might be more useful.

MemoryCache is not keeping the cache between different services for the same IIS site (WebApi 2)

My goal using MemoryCache is to prevent the same user to login more than one time in my application.
I have 3 services registered in the same IIS site: user, navigation and evaluation services. I implemented the MemoryCache in my framework which is used as reference between all the services. I have a filter that verify whether I have the user token cached or not for every request made from any service.
When I login in my application, it will send 3 requests: 2 for the 'user' service and 1 for the 'navigation' service. The first request will set the cache using the 'user' service. At this time, I will have my object cached in memory. The second request uses the 'user' service again, which verifies that the token is there and return the result successfully.
My problem: The last request uses the 'navigation' service, and at this point, it can't access the cache (or the cache is no longer available) and throws an exception that I implemented.
IIS Site
Sites
-> APR
-> API
-> User
-> V1 (application)
-> Navigation
-> V1 (application)
-> Evaluation
-> V1 (application)
Below is my code:
public static CacheItemPolicy policy = new CacheItemPolicy();
public static MemoryCache cache = MemoryCache.Default;
public static bool GetCache<T>(string key, out T output, T defaultValue)
{
try
{
output = (T)cache.Get(key);
if (output != null)
{
return true;
}
}
catch { }
output = defaultValue;
return false;
}
public static void SetCache(string key, object obj, DateTime date)
{
try
{
policy.AbsoluteExpiration = date;
cache.Set(key, obj, policy);
}
catch { }
}
public static void RemoveCache(string key)
{
cache.Remove(key);
}
I realized that MemoryCache sets the cache in the application level of the IIS site. Even through I have the same site to host my services, each service has it own application, as you can see in my edited post. Thus, when the 'user' service sets the cache for the login request, it sets the cache in the memory of the 'user' service application. Therefore, when a request from the 'navigation' service is sent, it cannot access the 'user' service application.
As a solution, I had to guarantee that when a user login in my application, all the services must set the cache in the memory of its own application. Since the 'user' service already sets the cache for the login request, I created a simple request for each the 'navigation' and 'evaluation' services to set the cache in theirs service application. In this way, calling all these three requests just one time when the user login, all the services will have the token cached in memory.
Try to use Redis server, it is not application dependend. Should resolve your problem.

Session is not set for the first time on server while working fine on local machine in ASP.Net

I am working on e-commerce project where I am storing products and other related values in session. But SOMETIME session is not set on live while it works fine on local environment. I tried to log value of session but it is null for that instance. Please help me finding solution.
else
{ // This code always run whenever first request is made
dtProduct = DAL.ShoppingCart.GetProducts(Convert.ToInt64(id)).Tables[0];
dtProduct.Rows[0]["Qty"] = qty;
dtProduct.Rows[0]["Total"] = total;
dtProduct.AcceptChanges();
}
Session["product"] = dtProduct;
// Logged session value here, value of qty and total looks correct but session is null
Note This issue comes rarely and I cannot figure it out as it runs fine on local machine.

Finding Connection by UserId in SignalR

I have a webpage that uses ajax polling to get stock market updates from the server. I'd like to use SignalR instead, but I'm having trouble understanding how/if it would work.
ok, it's not really stock market updates, but the analogy works.
The SignalR examples I've seen send messages to either the current connection, all connections, or groups. In my example the stock updates happen outside of the current connection, so there's no such thing as the 'current connection'. And a user's account is associated with a few stocks, so sending a stock notification to all connections or to groups doesn't work either. I need to be able to find a connection associated with a certain userId.
Here's a fake code example:
foreach(var stock in StockService.GetStocksWithBigNews())
{
var userIds = UserService.GetUserIdsThatCareAboutStock(stock);
var connections = /* find connections associated with user ids */;
foreach(var connection in connections)
{
connection.Send(...);
}
}
In this question on filtering connections, they mention that I could keep current connections in memory but (1) it's bad for scaling and (2) it's bad for multi node websites. Both of these points are critically important to our current application. That makes me think I'd have to send a message out to all nodes to find users connected to each node >> my brain explodes in confusion.
THE QUESTION
How do I find a connection for a specific user that is scalable? Am I thinking about this the wrong way?
I created a little project last night to learn this also. I used 1.0 alpha and it was Straight forward. I created a Hub and from there on it just worked :)
I my project i have N Compute Units(some servers processing work), when they start up they invoke the ComputeUnitRegister.
await HubProxy.Invoke("ComputeUnitReqisted", _ComputeGuid);
and every time they do something they call
HubProxy.Invoke("Running", _ComputeGuid);
where HubProxy is :
HubConnection Hub = new HubConnection(RoleEnvironment.IsAvailable ?
RoleEnvironment.GetConfigurationSettingValue("SignalREndPoint"):
"http://taskqueue.cloudapp.net/");
IHubProxy HubProxy = Hub.CreateHubProxy("ComputeUnits");
I used RoleEnviroment.IsAvailable because i can now run this as a Azure Role , a Console App or what ever in .NET 4.5. The Hub is placed in a MVC4 Website project and is started like this:
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(50);
RouteTable.Routes.MapHubs();
public class ComputeUnits : Hub
{
public Task Running(Guid MyGuid)
{
return Clients.Group(MyGuid.ToString()).ComputeUnitHeartBeat(MyGuid,
DateTime.UtcNow.ToEpochMilliseconds());
}
public Task ComputeUnitReqister(Guid MyGuid)
{
Groups.Add(Context.ConnectionId, "ComputeUnits").Wait();
return Clients.Others.ComputeUnitCameOnline(new { Guid = MyGuid,
HeartBeat = DateTime.UtcNow.ToEpochMilliseconds() });
}
public void SubscribeToHeartBeats(Guid MyGuid)
{
Groups.Add(Context.ConnectionId, MyGuid.ToString());
}
}
My clients are Javascript clients, that have methods for(let me know if you need to see the code for this also). But basicly they listhen for the ComputeUnitCameOnline and when its run they call on the server SubscribeToHeartBeats. This means that whenever the server compute unit is doing some work it will call Running, which will trigger a ComputeUnitHeartBeat on javascript clients.
I hope you can use this to see how Groups and Connections can be used. And last, its also scaled out over multiply azure roles by adding a few lines of code:
GlobalHost.HubPipeline.EnableAutoRejoiningGroups();
GlobalHost.DependencyResolver.UseServiceBus(
serviceBusConnectionString,
2,
3,
GetRoleInstanceNumber(),
topicPathPrefix /* the prefix applied to the name of each topic used */
);
You can get the connection string on the servicebus on azure, remember the Provider=SharedSecret. But when adding the nuget packaged the connectionstring syntax is also pasted into your web.config.
2 is how many topics to split it about. Topics can contain 1Gb of data, so depending on performance you can increase it.
3 is the number of nodes to split it out on. I used 3 because i have 2 Azure Instances, and my localhost. You can get the RoleNumber like this (note that i hard coded my localhost to 2).
private static int GetRoleInstanceNumber()
{
if (!RoleEnvironment.IsAvailable)
return 2;
var roleInstanceId = RoleEnvironment.CurrentRoleInstance.Id;
var li1 = roleInstanceId.LastIndexOf(".");
var li2 = roleInstanceId.LastIndexOf("_");
var roleInstanceNo = roleInstanceId.Substring(Math.Max(li1, li2) + 1);
return Int32.Parse(roleInstanceNo);
}
You can see it all live at : http://taskqueue.cloudapp.net/#/compute-units
When using SignalR, after a client has connected to the server they are served up a Connection ID (this is essential to providing real time communication). Yes this is stored in memory but SignalR also can be used in multi-node environments. You can use the Redis or even Sql Server backplane (more to come) for example. So long story short, we take care of your scale-out scenarios for you via backplanes/service bus' without you having to worry about it.

Entity Framework not working on IIS?

I have an application running on IIS that i'm testing. Everything was fine until i publish it to the server. Server's ip address is 10.0.0.19 (this is a local application). Connection strings etc. everything is properly configured. Every other query in other pages are working fine but in some pages result sets aren't coming from Entity Framework.
Here is a sample code.
List<CCAP.Data.Orm.CustomerField> fieldList = CustomerFieldProvider.GetCustomerFieldList(projectId);
StringBuilder controlsToRender = new StringBuilder();
foreach (var item in fieldList)
{
HtmlTagBuilder tagParaph = new HtmlTagBuilder("p");
HtmlTagBuilder tagLabel = new HtmlTagBuilder("label");
HtmlTagBuilder tagInput = new HtmlTagBuilder("input");
tagInput.AddAttiribute("type","text");
tagInput.AddAttiribute("style", "width :400px;");
tagInput.AddAttiribute("name", item.FieldName);
tagLabel.AddAttiribute("for",item.FieldName);
tagLabel.SetInnerText(item.FieldHeaderText);
tagParaph.AddChildElement(tagLabel);
tagParaph.AddChildElement(tagInput);
controlsToRender.Append(tagParaph.ToString());
}
return controlsToRender.ToString();
What can be wrong about this situation??
The differences when you publish it to IIS are:
Security context
Configuration file
In your case it is probably the security context. Does your connection string use a trusted connection? Does your app pool identity have access to your SQL server.

Categories