I have website used by hundred of viewers every day. One of the pages has a timer that ticks every 10 seconds, when it does so it gets the latest data from the database and updates the screen.
The problem i have is that a high number of users and a high number of database connections takes its toll on the server.
Is there a better way of doing this? Updating server side and all users would benefit from the latest data but only the server is carrying out the calls every 10 seconds and not by every user?
SignalR is the way you'll want to go. Right now, your application is probably loading a page. Then you got some jQuery that probably sets a timer for 10 seconds. Then your timer is kicking off and you're probably doing an ajax call to get refreshed data, then putting that refreshed data into a <div> or something.
So essentially, every 10 seconds, your back end is calling your SQL server, doing some kind of SELECT statement, then the data from the SQL Server is being transmitted to your application server, where you are taking that data, transforming into displayable data.
SignalR, on the other hand works differently. It uses push technology. Push technology works like this. Lets say you have 5 people visiting your page right now. One person (person A) is doing something that saves something to the database. No one else is seeing this data though yet. But SignalR will send a signal out to everyone else (or just the people in which this database save affects) that says "Hey! There is newer data available. You should update now". The other people connected do an ajax call and get the refreshed data. And viola! The other 4 people now have updated data on their screen!
I hope I explained this clearly enough for you to understand! Scott Hanselman wrote a good introduction to SignalR.
I would use the setInterval() to fire an ajax function every 10 seconds.
window.setInterval("javascript function", milliseconds);
the javascript function would be similar to
function GetLatest(){
$.ajax({
type: 'POST',
url: '../Services/UpdateService.asmx/GetLatest',
data: '',
contentType: "application/json; charset=utf-8",
success: function (data) {
//use the data.d to get the info passed back from webservice. then
add your logic to update your html
},
error: function () {
//catch any bad data
}
});
}
your backend method should look like this. Object is whatever your objcet is
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public Object GetLatest()
{
Object obj= new Object ();
obj.FirstName = "Dave";
obj.LastName = "Ward";
return obj;
}
Have you considered having your server side code cache the data for that 10 seconds. Cache it and set it's expiration for 10 seconds. That way, everyone gets the same information for 10 seconds and then the first after that 10 retrieves a new data set and then caches it. That way, only the first person to refresh causes a DB query, the rest get data up to 10 seconds old. Something like this:
Cache.Insert(key, data, Nothing, DateTime.Now.AddSeconds(10), TimeSpan.Zero)
I guess this is assuming that the users are all getting the same data at each poll. If they are all getting unique datasets, this won't cut it for you.
Related
Background
I have an application that simply does the following tasks:
Take input from user
Run a SQL Stored Procedure passing the user input as a parameter
Do something
However, some tables used in the stored procedure get locked due to another scheduled jobs which I don't have control over.
What I have
So, I basically have the code that looks like this so that users are redirected to the "server is busy" page:
Dictionary<int, string> result = new Dictionary<int, string>();
try
{
result = new myRepository().GetUserInfo(userInput);
}
catch(Exception ex)
{
// in the error page, tell users to try again in a few minutes because the server is busy
Response.Redirect("ServerIsBusy.aspx");
}
What I want to do
With my current code, it takes while to display the "server is busy" message, so I'm trying to find if there is a way to detect if the stored procedure (GetUserInfo(userInput)) runs slow as quickly as possible so that I can redirect users to the "server is busy" page.
What I've tried
I tried setting httpRunTime executionTimeout to a few seconds, but as explained in many sites, this setting doesn't work well when the number is small, and it took about 20 seconds when I set it to 5 seconds.
Is there any configurations or a trick that I could use to solve this?
If you can use Ajax, i would make an asynchrone (with jQuery) request to your web server and show a buzy icon/label/message on your website.
Like this:
function doSomeThing() {
$.ajax({
type: "GET",
url: url,
async: true
});
}
When you work with databases, be sure you set the correct indices, often you get a lot of speed improvement. But be sure you set the right indices, to much and wrong can be bad too.
I'm newbie with SignalR and want to learn so much. i already read beginner documents. But in this case i've stucked. what i want to do is when a user got new message i want to fire a script, like alert or showing div like "you have new mail" for notify the recieved user. And my question is how can i do that ? is there anyone know how to achieve this ? or good "step-by-step" document? i really want to work with SignalR.
ps: i'm using Visual Studio 2012 and MsSQL server
edit: i forgot to write, notification must be fired when message created to DB
Thank you
In your Scripts use the following, naturally this is not all the code, but enough based off tutorials to get you going. Your userId will be generated server side, and somehow your script can get it off an element of the page, or whatever method you want. It runs when the connection is started and then every 10 seconds. Pinging our server side method of CheckMessage() .
This js would need refactoring but should give you the general idea.
...
var messageHub = $.connection.messageHub;
var userId = 4;
$.connection.hub.start().done(function () {
StartCheck();
}
//Runs every 10 seconds..
function StartCheck()
{
setInterval(messageHub.server.checkMessage(userId,$.connection.hub.id), 10000);
}
This method takes in a userId, assuming your db is set up that way, and grabs them all from your database; naturally the method used is probably not appropriate for your system, however change it as you need to. It also checks if the user has any messages, and if so sends down another message to our SignalR scripts.
public void CheckMessage(int userId,int connectionId)
{
var user = userRepo.RetrieveAllUsers.FirstOrDefault(u=>u.id == userId);
if(user.HasMessages)
{
Clients.Group(connectionId).DisplayMailPopUp();
}
}
Finally this message, upon being called would run your code to do the 'You have Mail alert' - be it a popup, a div being faded in or whatever.
...
messageHub.client.displayMailPopUp = function () {
alert("You have Mail!");
};
...
Hopefully this helps - I recommend the following links for reading up and building your first SignalR app:
http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/tutorial-getting-started-with-signalr-20-and-mvc-5
And a smaller sample: http://code.msdn.microsoft.com/SignalR-Getting-Started-b9d18aa9
Some architecture dilemma:
I'm using WPF as my client-side, EF Code First as my Data Access Layer, and WCF to connect between those. My probelm is hou to reupdate the UI after I did some changes to the DB, for example:
User insert new "Person" on the UI (ID=0)
User save the "Person" to the DB (ID=10, for example)
When talking about one user it's very simple - I can return the ID and update my UI as well (so next change to this person will be considered as "Update"), but what about adding more than one user at once, or updating other properties that was calculated on the server? should I return the whole graph? not to mention is very hard to remap it on the client side.
Before CodeFirst we could use STE, but it has it's own problems. anyone knows about known CodeFirst approach?
Would be happy to hear your voice.
Thanks!
You can send as the request to your wcf service the dateTime of your last update in client-side. But in the server-side you take all Persons which was updated/added after that dateTime and return it as the result. In this way you will get only modified/added Persons from your server-side.
So add lastUpdate collumn to your entity Person.
EDIT 1
If you want to server update the information in client but not client ask for news from server.
You can use the way like it works in Web Programming.
(1)The client-side asks server-side - "hey, my last update was at 20:00 10.02.2013", then server looks into DB - "is news after 20:00 10.02.2013?" if yes:
a) returns the news to the client
if no news in DB:
b) He dont returns null, but he does Thread.Sleep(somevalue). He sleeps then repeats the query to db and asks is "there news in db". So it's all repeats untill the news in DB will apear. After news in db appears he return the List<data> which is in updated after the dateTime. After that client gets the data he goes back to the point - (1).
So you dont make a lot of requests to the server but making only one request and wait for the news from the server.
Notice 2 things:
1) If the client waits too long the server side will throw the exception(dont remember actually the error code but it's not important now), so you have to catch this exception on client-side and make a new request to server-side. Also you have to configure on the server-side as long as you can wait time, to minimize the amount of requests from client.
2) You have to run this data-updater in the new thread not in the main, where the application runs.
How it will looks from the code(it may not work, i just want to show you the logic):
Server side:
public List<SomeData> Updater(DateTime clientSideLastUpdate)
{
List<SomeData> news = new List<SomeData>();
while(true)
{
List<SomeData> news = dbContext.SomeData.Where(e=>e.UpdateDateTime > clientSideLastUpdate).ToList();
if(news.Count()>0)
{
return news;
}
}
}
Client-side:
public static void Updater()
{
try
{
var news = someServiceReference.Updater(DateTime clientSideLastUpdate);
RenewDataInForms(news);
Updater();
}
catch(ServerDiesOrWhatElseExcepption)
{
Updater()
}
}
And somewhere in the code you run this updater in the new thread
Thread updaterThread = new Thread(Updater());
updaterThread.Start();
Edit 2
if you want update by one request all entities but not only SomeData then you have to add Dto object which will contain the List of every entities you want to be updatable. The server-side will complete and return this Dto object.
Hope it helps.
Brief Idea: I am developing a small social networking kinda site.
Now there's a user "A" who has 100 followers...now what i want to do is whenever user "A" submits an article in the website all his followers should get an email with the article link...that is okay i can do it.
Problem: Now, there's a submit button on the page which stores the article in the DB and sends email to the followers...as there are many followers it takes a lot of time sending the emails...so the page keeps showing loading msg till all the emails are sent..how can i send all the emails asynchronously ??
i mean after the article has been submitted ...the emails should go automatically to the followers without putting the email sending function in the click event of the button....hope am not confusing you folks.
can i do something like store the article in the DB , redirect to the article page , start sending emails in a batch of 10 per 10 mins automatically...this process should start as soon as an article has been submitted by an user.
I had a similar issue with batch emails, and various other long-running tasks.
I developed a window service which contained a job manager. When a job needs to run from the main MVC application, the web application communicates with the service over HTTP (actually, using JSON), and the service performs the meat of actually sending emails, or performing other long-running tasks.
This means the web application request returns immediately.
The web application can also poll the service to determine the status of any particular job that is running (each job is given a unique identifier).
I would create a database table containing information about all pending email notifications.
When hitting submit, you can quickly add rows to this table.
Then, a background thread can check the table and send the mails (and of course remove the successfully sent ones from the table).
Have you thought to implement it using AJAX ?
When the user press on the submit button, instead of posting back to the server, create 2 ajax calls:
The first one is to save the article to the repository (database ?).
After receiving succesfull answer from the server (which can include the article id), invoke 2nd ajax call to send the mails. The server can start a thread to send the mails so the answer to the client will be immediate.
My preferred way of invoking ajax calls is using JQuery:
$.ajax({
type: "POST",
url: "services.aspx/SubmitArticle",
data: "{articlecontent: '[put here the content you want to send]'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
error: function(response) {
// Handle Error here. The response object contains the error details
},
success: function(response) {
// Check here if the article has been saved:
// response.d property contains the server answer. It can be boolean,
// integer, string or any other complex object
// If article saved, invoke here the send mail ajax call. assuming the response.d contains
// the article id:
sendMails(response.d);
// sendMails invokes another ajax similiar to this code snippest
}
});
In the server side the async email send method can looks like:
[WebMethod]
public static bool SendMails(int articleId)
{
// Add the actual method that send mail to the thread pool
ThreadPool.QueueUserWorkItem(new WaitCallback(DoSendMail), articleId);
return true;
}
private void DoSendMail(object a)
{
int articleId = (int)a;
// Your code that sends mails goes here
}
You could use a queueing system like MassTransit, ZMQ or MSMQ.
Or... If you really wanted to create a cool app, you could pass the emailing task to a node.js app!?
When the user clicks on a link to generate report I make an AJAX call which generates a pdf file in the background.Now the files are huge running upto 10mb or more.So it takes some time.In the mean time the user should be able to navigate other links as if nothing has happened.So I need to implement in such a way that the pdf generation process gets started & user doesn't have to wait for the process to finish.Is this possible?I am using AJAX Pro with c# with dot net framework 2.0
The problem here is that as soon as the AJAX activity begins the browser enters into a hung stage & the user has to wait although he clicks on a different link.
I would probably create a 'queue' or an 'inbox' for the user ...
start your pdf generation routine with a ThreadPool.QueueUserWorkItem (you would also need to modify your generation method to output to their inbox)
then on each http request check that inbox and notify the user of the item ... you can always poll the server on an interval or somthing
Sure, but once the user navigates to another page, the Javascript that is waiting for the Ajax response is no longer running, so that request is lost. You'd have to either find a way to keep that page open (using frames or exclusively Ajaxified navigiation), or find a way to store the response and notify the user of its completion on the next page view. For instance, storing a session variable that indicates that the operation is completed, or storing it in a database with (perhaps) an "unread" boolean value.
You can have asynchronous Ajax call with which you can do other tasks while response objects returns from the Ajax page.
Here is some example, testAjax.aspx is the Ajax page here :
http_request.onreadystatechange = function() { alertContents(http_request); };
http_request.open('GET', 'testAjax.aspx?', true);
http_request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
http_request.send(null);
function alertContents(http_request)
{//debugger;
if (http_request.readyState == 4)
{
if (http_request.status == 200)
{
var vResult;
vResult=http_request.responseText;
//Write your logic after successful Ajax call here.
}
else
{
alert('There was a problem with the request.');
}
}
}