I was not able to find similar issue, so opening a new one instead.
When a huge .po file is imported with _locale_import_po, the time limit of 240 seconds is hit, effectively terminating the execution even in console mode. This can not be fixed in php.ini, as the function below, present on the first line in _locale_import_po, is overriding it.
drupal_set_time_limit(240);
I suggest to place a variable that will store the value. This way it can be overridden in settings.php like so:
$conf['drupal__locale_import_po__time_limit'] = 480;
...
drupal_set_time_limit(variable_get('drupal__locale_import_po__time_limit', 240));
Uploading a patch shortly.
Comments
Comment #1
ndobromirov commentedImproved title and description.
Uploading patch as proposed.
The solution is just extending the current one, giving some more flexibility for the developers.
BR,
Nikolay Dobromirov.
Comment #4
cilefen commented@ndobromirov Can you provide a link to one of the huge .po files so I can reproduce the issue please?
Comment #5
cilefen commentedComment #6
ndobromirov commentedHi,
Sorry but I can not provide the file, as it is related to a company project and I am afraid it might expose something from the translations, and manually going thorough it will be a painful process to cleanup such data.
In relation to the size of the file it was about 3 MB, containing around 20-22k translations. I think such file can be generated easily.
I will try to generate one this week, but please do not wait for me if you find time to generate the file.
- Nick.
Comment #7
cilefen commentedIf anyone following this can find a large enough .po file on https://localize.drupal.org/ to test with that will be great.
Comment #8
othermachines commentedCan this instead be solved in the same way as#2233929-21: drupal_set_time_limit should not be able to change the time limit if it's already unlimited?Scratch that. It apparently already should be. Not sure why my import is failing with 240 seconds exceeded with max_execution_time set to unlimited. Will report back if I find anything.
Comment #9
ndobromirov commentedThe issue is that the method _locale_import_po sets the limit explicitly in it's first line, so it does no matter what kind of non-zero config you have.
If you are doing the import from console it might work if you have the code from the issue referenced in #7.
If you are doing the import from web-UI, then your PHP's original time limit is very likely non-zero and it is the default one - 30 seconds. Then the prevention implemented in #7 is irrelevant, and the call
drupal_set_time_limit(240);will set the time limit to 240, effectively breaking the import after4 minutes with the error.Other solution here could be to have the import set the value to zero, and keep the old value. After finish, it will set it back, as an easy workaround the patch in 1 will allow you to set a high enough time limit to allow the import to finish normally.
Comment #10
David_Rothstein commentedInstead of making this configurable, it would be better to fix it directly.
Couldn't we do additional drupal_set_time_limit() calls inside _locale_import_read_po() itself, e.g. after it has processed each X lines of the file? That way the time limit would always be long enough. This would be in addition to (or instead of) the single call that's currently in _locale_import_po().
Also, is this an issue in Drupal 8? I think maybe not - and I vaguely remember seeing a similar issue to this one at one point, although perhaps it's not in the Drupal 7 queue.
Comment #11
ndobromirov commented#10 I am +1 on this as the better approach.
There is just one thing though. It can not be addition to the 240, as any time limit that is set after the first one will override it and reset the request's timer. If we are making per row time limit, the 240 one is obsolete/not needed.
The patch from 1 year ago was aimed as a fast hack with low intrusion on the core code, so my import could continue at that time.
Here is a patch that will set a 30 seconds limit on every 10 rows parsed from the .po file, also removing the old limit call. I am hiding the old patch, as this one is genuinely better - it will not require config tweaks to import a huge file.
I do not think that a couple of function calls and if statements will pose overhead, compared to file/network I/O that are performed in the loop, so no benchmarks are needed. Setting to needs review.
Comment #12
othermachines commentedThanks for the explanation, @ndobromirov. But looking again at the
drupal_set_time_limit()function (the code from the patch referenced in #8 is in 7.43), I'm still flummoxed as to why max_execution_time = 0 isn't being registered. The argument set in_locale_import_poshould need to survive this check. I don't want to distract you from the task at hand, but if you don't mind humouring me for a sec it seems like something that's important to understand.At the time I was using the web UI to import the file. I am running multiple versions of PHP on Windows, and was careful about targeting the correct .ini file (or at least I thought I was :)). But perhaps I am missing something embarrassingly obvious.
Would you mind expanding on this?
(As a side note, I was able to bypass the problem with the
innodb_flush_log_at_trx_commitfix suggested over at #1131048: Interface translation import times out during install.)Comment #13
ndobromirov commentedFirst sorry, the comment should have been #8. No idea why I've written #7 two times...
PHP on Unix and windows (as far as I remember) has 2 configurations. One that is for console (php-cli.ini), where the default is 0, so PHP will run endlessly for utility scripts in the console, and one for web (php.ini), where the limit is 30 seconds (by default).
General rule of thumb is to not change time limits, as it can starve resources from your web-server, so the time limit is rarely changed on production servers, this is why I assumed that it is the default one.
This code from drupal_set_time_limit in case of console will do nothing (in default), as $current ins zero and it will stay like so.
In case of web (30 seconds / non-zero value), it will work, so the one call to drupal_set_time_limit with 240 will be invoked and the import will run for the next 240 seconds correctly. If it's longer (for a single file) it will fail with the TTL error, as nothing is setting it again to refresh the timer. Note that even if the time limit was configured to be more than the 240 seconds in php.ini, the run-time call will set it to 240, effectively reducing it, after the first imported file.
The patch in #11 assumes that the import process will be able to parse 10 rows within 30 seconds. And then refresh the timer.
The solution from your last reference is just improving the speed that the DB is importing the files (that is good). Note that a large-enough file will break this again, as the core problem is not fixed.
Comment #14
othermachines commented@ndobromirov - Thanks, I appreciate that. I do understand all of this, already, so unfortunately my circumstance (in a development environment where I'm not necessarily using defaults) remains a mystery. Moving on!... I'll see if I can dig up that *.po file again and give the patch a test in the next few days. Cheers -
Comment #15
hgoto commented@ndobromirov the patch #11 looks good to me.
But I think the comment can be improved.
IMO, "Refresh time limit every 10 parsed rows." doesn't provide very useful information. (It just explains what the following lines do.) Isn't the original comment "Try to allocate enough time to parse and import the data." better here?
Comment #16
othermachines commentedIn testing this morning (Drupal 7.50) I attempted to import a 655KB file and predictably ran into the timeout.
With this patch I was able to successfully import the file.
Note: For this test I set
innodb_flush_log_at_trx_commit = 1in MySQL my.ini, which I do believe is the default value and makes innodb inserts/updates very slow. Setting this to '2' makes imports speedy enough on my machine to avoid the timeout, with or without the patch (which is why this fell off my radar - sorry about that).Regarding @hgoto's critique, I have to disagree. "Try to allocate enough time to parse and import the data." says to me that we're going to guess a number and hope it's enough, which was entirely appropriate before. "... to parse and import the data" should be obvious (it's an import function). Given that there are people with varying levels of PHP proficiency here I think it's entirely useful to describe what those three lines are doing, and "Refresh time limit every 10 parsed rows." does that nicely.
Comment #17
ndobromirov commentedOk, if 2 people are considering the patch working can it be moved to RTBC, to have some progress?
Comments and similar nits can be fixed by the committer upon commit, in the end - it's a 3 line patch :)
Comment #18
othermachines commentedSetting to RTBC.
In answer to David Rothstein's question in #10, the problem doesn't exist in Drupal 8 since batch api is used.
Comment #19
David_Rothstein commentedCommitted to 7.x - thanks!
I think this is OK - it gives me slight pause that we're calling drupal_set_time_limit() with a smallish number, given the fact that in some scenarios this function can decrease the overall time limit, plus the fact that this function is called from other locations including st().... But since we're doing it every 10 rows, I think in practice the time limit will usually get increased enough not to affect other things.
For the code comment, I split the difference and did this on commit:
Comment #21
ndobromirov commented