apparently there is something in my code that is stuck as on occasions it causes the script to run until time out at 6minutes.
I am still trying to find that code but in the meantime, i really need to prevent the waiting. My script typically needs 10 seconds only.
Is there a way for me to set such that any script that hits 10 seconds should be terminated.
much appreciated!
First of all, isn't it better to rewrite the code so that it does not reach 6 minutes?
Reference URL
If there is a possibility that it will exceed 6 minutes, try using a trigger to execute it for more than 6 minutes.
Reference URL
If there are multiple commands or is executed repeatedly, then TheMaster's recommendation in the comments is a good thing to be considered.
This will how it would look like.
function sampleFunction() {
var startTime = Date.now();
// do your thing, initialization, etc.
while (true) {
// do your thing, main process
// if 10 seconds is done
if ((Date.now() - startTime) >= 10000)
return;
}
}
Limitations:
If the command before the if condition checking the time takes 5 minutes, then you will be able to exit the script after that execution which will be 5 minutes later.
You can't stop a single command mid-execution. You need to wait for your if condition to be executed.
Things to note:
Exiting script through the method above works best when numerous lines of codes are being repeated in a loop and consistently checking the if condition so you can exit the script when the time comes.
You can only exit the script in between commands, not during those commands. So using this between commands that take too much time might stop the script later than expected.
It is still best to debug what is happening and why that "BUG" happens, or maybe you missed something that is causing the unexpected behavior.
Upon providing the exact code, the community will be able to help you with a more specific answer. Until then, speculations could only be provided.
Related
Given: extension that has a properly configured cronjob (let's say, every 5 minutes) in Config.xml. Also, the system cron is set to run Magento's cron.sh. The cronjob has to run a couple of times after the extension installed, and when it has no more data to process then it becomes obsolete.
Problem: the job isn't needed after it had processed all the data. However, its setup in Config.xml causes it to run every 5 minutes forever, just to check that there is no more data and die.
Question: is there any proper way (maybe with the cron_schedule table...) to 'dismiss' the cronjob programmatically from its own PHP when it sees that there is no more data? Or any other way?
The cron is used since the extension installation process shouldn't be interrupted. Maybe it's possible to schedule some PHP code in some other way than cron (but within Magento)? Thought about threading but since there is no guarantee that this feature will be built in, this doesn't seem to be the option....
Thanks in advance!
So, I found 2 possible solutions: 1) it seems to be possible to create/remove crontabs via core_config_data table without config.xml; 2) remove the crontab node from config.xml after all data is processed + clean the cache + remove all pending tasks. I've managed to implement the 2nd, and it works (I know that the 1st approach is much better, but I just had no time to dig it out).
The 2nd looks like:
if ($more_data) {
// processing...
} else { // Dismissing the cron
$config_xml_path = Mage::getModuleDir('etc', 'the_extension') . '/config.xml';
$config_xml = simplexml_load_file($config_xml_path) or die("Error: Cannot create object");
if (isset($config_xml) && isset($config_xml->crontab)) {
unset($config_xml->crontab);
$config_xml->asXML($config_xml_path);
}
// Cleaning
Mage::app()->cleanCache();
$schedule = Mage::getModel('cron/schedule');
$sch_col = $schedule->getCollection()
->addFilter('job_code', 'the_extension_cronFunc')
->addFilter('status', 'pending');
foreach ($sch_col as $s) {
$s->delete();
}
}
Question on agents: I specifically want to create a Periodic Task, but only want to run it once every day, say 1am, not every 30 minutes which is the default. In the OnInvoke, do I simply check for the hour, and run it only if current hour matches that desired hour.
But on the next OnInvoke call, it will try to run again in 30 minute, maybe when it's 1:31am.
So I guess I'd use a stored boolean in the app settings to mark as "already run for today" or similar, and then check against that value?
If you specifically want to run a custom action at 1 am, i'm not sure that a single boolean would be enough to make it work.
I guess that you plan to reset your boolean at 1:31 to prepare the execution of the next day, but what if your periodic task is also called at 1h51 (so called more than 2 times between 1am and 2am).
How could this happen? Well maybe this could happen if the device is reboot but i'm not quiet sure about it. In any case, storing the last execution datetime somewhere and comparing it to the current one can be a safer way to ensure that your action is only invoked once per day.
One question remains : Where to store your boolean or datetime (depending which one you'll pick)?
AppSetting does not seem to be a recommanded place according msdn :
Passing information between the foreground app and background agents
can be challenging because it is not possible to predict if the agent
and the app will run simultaneously. The following are recommended
patterns for this.
For Periodic and Resource-intensive Agents: Use LINQ 2 SQL or a file in isolated storage that is guarded with a Mutex. For
one-direction communication where the foreground app writes and the
agent only reads, we recommend using an isolated storage file with a
Mutex. We recommend that you do not use IsolatedStorageSettings to
communicate between processes because it is possible for the data to
become corrupt.
A simple file in isolated storage should get the job done.
If you're going by date (once per day) and it's valid that the task can run at 11pm on a day and 1am the next, then after the agent has run you could store the current date (forgetting about time). Then whenever the agent runs again in 30 minutes, check if the date the task last ran is the same as the current date.
protected override void OnInvoke(ScheduledTask task)
{
var lastRunDate = (DateTime)IsolatedStorageSettings.ApplicationSettings["LastRunDate"];
if(DateTime.Today.Subtract(lastRunDate).Days > 0)
{
// it's a greater date than when the task last ran
// DO STUFF!
// save the date - we only care about the date part
IsolatedStorageSettings.ApplicationSettings["LastRunDate"] = DateTime.Today;
IsolatedStorageSettings.ApplicationSettings.Save();
}
NotifyComplete();
}
I'm using Google Spreadsheet for my tax administration. All financial transactions that I have done in a year are in the spreadsheet. Because the tax rules in my country are quite complex, I've developed a number of JavaScript functions to help me with my calculations. There are many rows in my spreadsheet, about 1000. Each row has multiple references to those JavaScript functions.
This system worked beautifully before, but today I've found that Google installed some kind of runtime limiting system into Google Spreadsheet, causing many of my columns to abort with the following error:
Service invoked too many times in a short time: exec maxSimultaneous.
Try Utilities.sleep(1000) between calls.
After some investigation, it would appear that this time limit is there to protect against scripts that take too long time to run. My case is different: my scripts are all short, O(1) algorithms that do nothing but calculating some numbers. A typical script looks like this:
// Calculates the total amount excluding Value Added Tax, given
// an amount including Value Added Tax, and other sorts of information.
function ex_btw(inc_btw, commentaar, soort, btw_verlegd, btw_pct) {
Utilities.sleep(1000);
var soort = soort.toLowerCase();
if (soort == 'eten en drinken'
|| commentaar.match(/treinkaartje/i)
|| commentaar.match(/treinticket/i)
|| commentaar.match(/taxi/i)
|| commentaar.match(/ boek /i))
{
return inc_btw / 1.06;
} else if (soort == 'priveonttrekking'
|| soort == 'boete'
|| soort == 'belasting'
|| commentaar.match(/postzegel/i)
|| btw_verlegd == 'Ja')
{
return inc_btw;
} else {
return inc_btw / (1 + btw_pct);
}
}
The script is then invoked like this from a cell:
=IF(B6<>""; ex_btw(B6;D6;E6;J6;S6); "")
Maybe my problem is that I have too many script calls. Every single row calls about 6 of such scripts, so with 1000 rows I call 6000 times per spreadsheet.
How do I solve this problem? Is there a way to increase the execution limit, or is there a way to make the scripts run slower so that they don't hit the execution limit? As you can see in the example code I've already tried inserting Utilities.sleep(1000), but that doesn't seem to solve the problem. I don't care whether the scripts run slowly, I just just care that they finish without errors.
Can I pay to have the limit increased? I need to hand in my taxes in a few days.
Other alternatives that I've considered, but that are not feasible.
Using non-JavaScript functions. Not feasible because: they don't support collaboration like Google Spreadsheet does. I regularly go over the spreadsheet with a colleague to check whether we've made any mistakes. It helps that the both of us can immediately see any changes the other makes.
Have one huge-ass JavaScript function that iterates over rows and populates cells. Not feasible because:
Too error prone, it's very easy to make mistakes compared to my current method.
Does not update cells automatically until I re-run the script. I want to see any calculations immediately after I update other cells, just like a spreadsheet is supposed to do.
Using other spreadsheets like Excel and OpenOffice Calc. Not feasible because: they don't appear to offer the same scripting capabilities.
Writing my own financing app. Not feasible because: it takes too much take, it's not my core business, and tax rules change almost every year so I will have to constantly update the app. I can update a spreadsheet very quickly, but writing a financing app takes too much time.
I solved it by making every function sleep for a random period, like this:
Utilities.sleep(Math.random() * 5000);
It is important that the sleeping time is random, not constant. Apparently Google limits the maximum number of functions that may simultaneously be using CPU.
an alternative to the custom function might be to have an onEdit function trigger and then process either just the entered data or the whole column of numbers and place the results of the function in the target cell(s) as a number.
might be quicker
I have code like:
begin
RunProgram:=TProcess.Create(nil);
RunProgram.Commandline:='calc.exe';
RunProgram.Execute;
RunProgram.Commandline:='notepad.exe';
RunProgram.Execute;
RunProgram.Free;
end.
and I would like to put a sleep or delay after executing calc.exe
You had the right idea - it's Sleep.
begin
RunProgram:=TProcess.Create(nil);
RunProgram.Commandline:='calc.exe';
RunProgram.Execute;
Sleep(1000); // Adds a 1 second delay
RunProgram.Commandline:='notepad.exe';
RunProgram.Execute;
RunProgram.Free;
end.
You may need to add the Windows unit (or possibly a different one - I'm not familiar with FreePascal's unit arrangement) to your uses clause to be able to compile the Sleep function.
I have a class with several methods I must run in an specific order. So, I created a kind of chain where each one calls the next in its last sentence (sometimes with calls, sometimes dispatching events):
internal function a(evt:SwapEvent):void { //started from custom event
...
b();
}
private function b():void {
...
bTimer = new Timer(bTime*1000,1);
bTimer.addEventListener(TimerEvent.TIMER, bEnd);
bTimer.start();
}
private function bEnd(evt:TimerEvent):void {
bTimer.removeEventListener(TimerEvent.TIMER, bEnd);
bTimer = null;
c();
}
private function c():void {
...
dispatchEvent(new CustomEvents(CustomEvents.NEXT_FRAME,1));
}
....
private function g():void {
// tell the second class the sequence finished
}
The problem is at some point and before arriving at last method, I need to run again a sub-sequence, let's say from function c() to e(). And it's causing problems in the form of an increasing delay between functions (I have 2 timers)
I guess the solution is something like this:
a();
b();
...
if (some condition) {
c();
...
e();
}
...
f();
g();
But, as far as I know, Flash Player doesn't make a synchronous execution. i.e., it doesn't wait until method a() completes to execute b()
Any idea/suggestion on a clean and bullet-proof implementation? This application will run in an endless loop 24x7
Thanks in advance,
After reading your code properly, yes, you are right. b() gets executed as that line of code is reached.
I might be tempted to create a queue of methods to execute. Execute one, check to see if you have time to execute another before the frame needs updating, and repeat. You can always insert new commands in to the queue at any time, so b() could insert endB() next in the queue.
Both the sequencers at http://www.dpdk.nl/opensource/running-tasks-in-order-with-a-task-based-sequence-manager and http://as3.casalib.org/docs/org_casalib_time_Sequence.html should do what you need. The former might be a bit of overkill as it looks like you need to create individual classes for each of your tasks which may be a little too much overhead. On the other hand the Conditional Tasks look like they are exactly what you need. The latter being simpler in that you can specify methods to execute. However, there doesn't seem to be a built in way to condition the tasks, but that may just be as easy as creating a task that conditionally adds tasks.
It might help to know a bit more how the conditions work.
Other points:
Tail calls are not very efficient in AS. Think of it as adding more to the stack every time you call a method. Once the method returns (even if you don't explicitly use return) the method gets knocked off the stack and the previously called method will continue to execute. So the more tail calls, the bigger the stack and the more to clean up.
As soon you start executing code the player hangs until the code has completely run and execution has returned to the player. You have around a 15 second execution limit before the player will kill your script, so you have to account for that when endlessly executing code. The solution is to execute some code then wait till the next frame to execute more.
You don't have to recreate your Timers, you can create them once as instance variables and just call start. The timers will return execution to the player (if no more method calls are made).
A somewhat simplified version, but I'm sure you get the picture.
ADDENDUM: I think you should look in to the Command Pattern (http://en.wikipedia.org/wiki/Command_pattern). You can store an array of commands to be executed (either synchronously, or asynchronously) and when one returns or dispatches a complete event you execute the next. I think you can create a simple implementation of the Command Pattern to do what you need without all the overhead of sequencers I mentioned earlier, but it's always good to get an idea of how others have done it.
There are a number of libraries that provide sequenced execution in AS3. Usually they are for performing Animation so they will generally have a bias toward that.
For example, Twease and Tweener will let you do sequenced actions as well as generic animation actions.
Another particularly good sequencing library is included as part of the asapframework ... which has a class called ActionQueue to which you can add TimedAction items. The great thing about TimedAction & ActionQueue is the feature to add an undo method and arguments, if you need to run the sequence backwards.
Using sequencers is a lot simpler than setting up a mass of timers manually.
i don't really understand your question. actionscript is synchronous in general (except for some AIR functions that can be executed asynchronously), but a timer is asynchronous in the sense that your code will not pause and wait for the timer to complete before continuing the main thread.
if you want your methods to only execute following the completion of a timer, use the timer event TIMER_COMPLETE event listener.