I have a view that has a link using the g:link GSP tag with action that runs and updates on a significant number of rows, so it takes some time. In the meantime, the browser is in limbo and times out after 30 sec (which is the timeout on the servlet container).
Problem:
It is a bad user experience that the page times out.
The browser submits the controller action each time the timeout occurs. I have handled this by checking if the query is already running so this is no longer an issue.
Question:
How can I trigger the controller action and reload the page to the same view? Or is there a better way to handle this like triggering the action async?
I tried to reload the page using js, but it does not seem to reload the page. I have read about implementing a message queue, but it seems like a lot of work for a simple issue. Any ideas will be good. Thank you in advance.
View:
<li>
<a class=""
href="${g.createLink(controller: "hello", action: "dbAction")}">Run Update on DB
</a>
</li>
Controller Action:
def dbAction() {
some code...
myservice.dbAction();
redirect(action: 'index')progress"
}
}
My Service dbAction:
def dbAction() {
Sql sql = getSql()
sql.executeUpdate('''
update mytable
set
mydata = calculate_data,
updated_by = 'dbAction',
updated_at = now()
where
id in (1,2,3)
}
I have exactly your problem. The query takes so long to run, the controller cannot respond back to the browser. So, the browser times out.
I tried 3 different ways to fix this.
The easy way out:
I am using Tomcat. So, I set the connection timeout value longer. The connectionTimeout variable is in the server.xml file.
The lazy way out:
I don't know if Grails has any sort of Message Queue function or not. So, I rolled my own. When I click the submit button to run a very long update query, the action will INSERT INTO a sort of update command in a database table along with the state. I then use Quartz to schedule maybe every 10 seconds to read this table to check the state. If the state is NEW, I change the state to "IN PROGRESS". And then I let the trigger to run the update query in the background. After it finish, I change the state to FINISH.
So, the action is just adding a row to the database and then respond back the view that says something like... You have issued the request. The system will process your request in a few moment.
The hard way out:
I went over all my SQL and functions in the actions to calculate the time it will take the SQL and codes to finish the query. I then rearrange/rewrite the functions and procedures. I am not good enough for this. If I can get to O(n), that will be enough for me.
Related
I have an application that uses a combination of ContentService.Saved & ContentService.Saving to extend Umbraco to manage content.
I have two websites in one Umbraco installation I am using those methods to keep content up to date in different parts of the tree.
So far I have got everything working the way I wanted to.
Now I want to add a feature that: depending on which Umbraco User is logged in, will either publish the content or simply send it for approval.
So I have changed some lines of code from:
cs.SaveAndPublishWithStatus(savedNode, 0, false)
To this:
cs.SendToPublication(savedNode);
Now the problem that I am finding is that unlike the SaveAndPublishWithStatus() method, the cs.SendToPublication(); doesn't have the option of passing false so that a save event is not raised. So I get into an infinite loop.
When I attach the debugger and manually stop the infinite loop the first time it calls cs.SendToPublication(savedNode); I get exactly the behavior I want.
Any ideas about how I can get round this problem? Is there a different method that I should be using?
You are correct in saying that it currently isn't possible to set raiseEvents to false when sending an item to publication - that's a problem.
I've added that overload in v. 7.6 (http://issues.umbraco.org/issue/U4-9490).
However considering that you need this now, an interim solution could be that you make sure your code is only run once when triggered by the .Saved / .Saving events.
One way to do this would be to check the last saved date (UpdateDate) in your code. If the content was saved within the last second of the current save operation, you know that this is a save event triggered by the save happening in SendToPublication action. Then you also know that the item has already been sent to publication and that this doesn't need to be done again - thereby preventing the endless loop from happening.
My requirement is to update user session record in database on each click that hits the server.
So I have written filter for this
allExceptLogin(controller: 'login', invert: true){
before = {
}
}
Which works fine as it goes inside filter where I can update the record but the problem is that if I have more than 1 method calls on a single click then it goes inside this filter that many times.
For e.g. If I click on a page which calls 4 different methods from same or different controller then it will go inside this filter 4 time which will eventually update the record 4 times.
I need some condition which says 1 click = 1 request to this filter.
Is this possible or can this be achieved by any other way?
The server has no notion of "clicks", it only deals with requests. One possible approach is to have the client send a key param with each "click" that your filter could then process in one batch.
Another option is to set a timeout on the server (e.g. on the session object) to only process requests every x many seconds. You may miss some related calls as well, but that may be OK.
The short of it is that Grails itself does not have a built-in mechanism to differentiate between related requests.
My application feature a "main" page where most of the action happens: There are tags for filtering and a list of results in a (paginated) table, plus the possibility to select some or all results in a "shopping cart".
This page has to keep track of a whole lot of things: what tags are selected, what items are selected, and how the result table is sorted and what page it's on. Everything has to persist, so if I select a new tag, the page must partially reload but remember everything (sorting, what's selected).
Right now I'm handling everything with parameters, and for each action taken on the page, all links (select a tag/item, change page, sort table) are updated to include previous parameters + the relevant new addition. This works, obviously, but it feels kind of inefficient, as I have to reload more of the page than I want to. How is this situation normally handled? I can't find that much info on google at all, but it doesn't feel like a particularly uncommon case.
tl;dr: How to best make sure all links (to the same page) always include everything previously selected + the new action. There are a lot of links (one per tag to select/deselect, one per result item to select/deselect, one per sort option, one per page)
There are five ways to do that:
Method 1: By parameters
You mentioned this. I never think of this as it's too troublesome. Anyway it's still a solution for very simple case.
Method 2: By cookie
Save the settings to a cookie and read the cookie in controller to arrange layout settings.
Method 3: By LocalStorage
Similar to cookie but allows more space.
Method 4: By Session
If you are using ActiveRecord to save session, this could be the best solution for pure pages loading. Save the user preferences into session and load it in next layout.
Method 5: Use Ajax
This is the best solution IMO. Instead of whole page loading, use Ajax to refresh/retrieve changes you need. Using together with above method, a user can even continue his last preferences. This is the most powerful and should be applicable to your case which looks like a web app than a website.
Have you tried creating model for all those attributes? and just always load the 'latest' when on the page load, if you dont need them you can always have a flag for that session.
In my ASP.NET MVC application, one of my actions is going to take a while -- it kicks off a sequence of other tasks. I'd like to report progress to the user. I want to display text -- I don't want a simple progress bar or spinner.
How should I go about doing the two parts? First, how do I display progress to the user? Second, how should I implement the action so that progress is available to the user?
My ideas:
Create controller method, which returns JSON:
{
"Message" : "Processing something serious",
"Percentage" : "43"
}
Handle it through JS - put message span & bar indicator in seperated div, change it's content.
For progress bar i would use this one.
I started of writing a response but then I realized I should just redirect to the Progress Indicator AJAX pattern resource. It has a comprehensive yet concise discussion of the problems, solutions, usability considerations and other interesting details.
I'm doing some playing around, and currently I've got the following in mind:
In the initial controller action, start a background thread that does the actual work, return immediately.
In the web page, use a timer to query a "GetProgress" action. This'll return some JSON that can be used to update the web page. I'll be using jQuery.
In the GetProgress action, query the background thread for its progress.
I'll probably pass a Job ID to GetProgress, and use this to identify which background thread I'm asking about.
I am not really sure what to do in this situation. I have some jquery ui 1.7 tabs that are ajax enabled. Now when a tab is clicked it goes to my asp.net mvc controller action method. That then does whatever is needed and return a partial view.
Now I don't know why but sometimes my server hangs but then again I am guessing all servers hang since sometimes when I go to sites it take forever to load but if you do a refresh it loads up instantly and that's the same case with my site.
My site will load up a tab super fast for X number of times then all of a sudden a request will just hang and maybe like 15seconds later it will load up. Or if you just refresh the page it will go back and start loading them up super fast again.
The problem though is the request is sent to the server where it hangs. So I tried to setup a jquery timeout on all my ajax stuff and that calls an abort to the jquery ui tabs.
But it never works and I guess from what I gathered reading on this site is because the request is on the server and abort won't stop stuff on the server. So if I look at firebug that request that hanged is stil running.
Now this causes a huge problem for me since it screws up the entire page what is heavily ajax. Like if the user tries to click on say another tab they will most likely have to click 2 times to get it to load up. Another thing what happens is if that request ever finish hanging whatever tab they are on will merge with that tab. So sometimes one tab will have parts from all the other tabs.
So if it hangs and does not finish it really messes with the ajax tabs. I have no clue how to fix this.
So I am not sure what to do.
Let them hang for however long it takes the server to figure out how to finish that request(when a ajax request is made I disable all tabs. This is because if a person say loaded up a tab and did not let it finish and tried to go to another tab the same problem would occur with the tabs merging together). Or abort the request and have a screwed up tabs.
Great choices I got. Anyone got any better choices?
Thanks
We have a similar situation here in that the 1st person to hit the site for the day will incure a 15 second delay while things are loaded.
In our situation, on all calls to the server via jQuery, I have set a normal javascript timeout for 2 seconds. After 2 seconds I pop up a small div saying something like things are talking longer than expected and please be patient.
after a further 6 seconds i close that div and open a new one with more of an apology.
not a great solution but it does keep the user in the loop, comfortable that things are ticking along and that we acknowledge things are not perfect.
warm and fuzzies as we call them here. :)
I'm not sure what you mean by "jquery timeout". if you mean setTimeout, that definately won't work. If you mean, on the other hand,
$.ajax({
url:"myserverprocess",
timeout: 200,
error: function () { /* try again */ },
success: function (myadata) { alert("mydata")}
});
then that should work. Try the timeout option if you haven't yet.
edit:
you could try :
$(selector).tabs({
ajaxOptions: {
url:"myserverprocess",
timeout: 200,
error: function () { /* try again */ },
success: function (myadata) { alert("mydata")}
}
});
We've had good success using jquery queue manager - http://www.protofunc.com/scripts/jquery/ajaxManager/
When a second request is performed it aborts the first request. I'm not sure about a timeout, but there are several options you can play with.
You could use Jquery to set a timeout on your ajax call and then catch it in the error section. Once caught, you could deal with retrying or doing any needed cleanup.