Problem/Motivation
- Some users have reported frequent PDO exceptions when the statistics module is enabled because the query is trying to insert a negative timer value into an unsigned integer field.
- Core's timer functionality stores the results of calls to
microtime(TRUE)in a global variable. - There are a number of reported issues with PHP microtime calculations, including:
- Incompatibility with ntp server configurations (http://www.php.net/manual/en/function.microtime.php#101628).
- Odd behavior on Windows (http://www.php.net/manual/en/function.microtime.php#108255 and https://bugs.php.net/bug.php?id=42659).
- Problematic float calculations (see REQUEST_TIME).
- Inaccuracy for times less than one microsecond due to rounding errors.
microtime()is used in core for:- The timer, including the page timer, statistics logging, batch processing, simpletest, and the HTTP timeout.
- Elapsed time within batches (in addition to the timer use).
- Query logging.
- The database locking mechanism.
- The Symfony Stopwatch (not used in core or in any Symfony components we've yet bundled).
- Hashing, which is not relevant here.
- A negative elapsed time is not a possibility in the real world.
Proposed resolution
Investigate details about the environments where this is occurring, and:
- If the issue appears to be due to PHP or environment issues with microtime, add drupal API to handle microtime calculations and enforce a non-negative value.
- If the issue appears to be due to improper use of the timer global, move this value out of global scope.
Remaining tasks
More information is needed
If you have encountered this error, please reply with:
- Your server OS.
- Your web server (Apache, IIS, etc.).
- Your hosting provider, if applicable.
- Your version of PHP.
- Any relevant server-specific configurations, including whether your server is configured for ntp and whether
register_globalsis enabled (it really really should not be).
Related issues
- #1225404: Modernize lock acquisition to RAII
- #838976: lock_acquire() breaks if new expiration is the same as old one
API changes
May introduce a drupal_elapsed_microtime() or similar for calculating microtime changes and enforcing a non-negative value.
Original report by h2pm
1 user got the following message when trying to access the site. When she refreshed the error went away. (I had to type this in from screenshot so any typo is my fault.) Actual screenshot attached. This was a one-time event so far.
PDOException: SQLSTATE[22003]. Numeric value out of range: 1264 Out of
range value adjusted for column 'timer' at row 1: INSERT INTO {accesslog}
(title, path, url, hostname, uid, sid, timer, timestamp) VALUES
(:db_insert_placeholder_0, :db_insert_placeholder_1,
:db_insert_placeholder_2, :db_insert_placeholder_3,
:db_insert_placeholder_4, :db_insert_placeholder_5,
:db_insert_placeholder_6, :db_insert_placeholder_7): Array
([:db_insert_placeholder_0] => [:db_insert_placeholder_1] => node
[:db_insert_placeholder_2] => http://jrintranet/[:db_insert_placeholder_3] =>
65.205.5.170 [:db_insert_placeholder_4] => 0 [:db_insert_placeholder_5] =>
EXke7uDtd3_s0fpDa9fzJbwimHhsbdFq8jjq4iofGIM [:db_insert_placeholder_6]
=> -2 [:db_insert_placeholder_7] -> 1327486506 ) in statistics_exit() (line 93
of /data/web//www/modules/statistics/statistics.module)
| Comment | File | Size | Author |
|---|---|---|---|
| statistics-module-error1.jpg | 54.25 KB | h2pm |
Comments
Comment #1
h2pm commentedHad to turn off "Access Log Setting" in Configuration > System > Statistics because these errors are appearing frequently to website visitors.
Comment #2
tecjam commentedYes, I received the exact same error this morning:
PDOException: SQLSTATE[22003]: Numeric value out of range: 1264 Out of range value for column 'timer' at row 1: INSERT INTO {accesslog} (title, path, url, hostname, uid, sid, timer, timestamp) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7); Array ( [:db_insert_placeholder_0] => InTown Guide - Das Städte-Portal [:db_insert_placeholder_1] => node/1 [:db_insert_placeholder_2] => [:db_insert_placeholder_3] => 66.249.67.19 [:db_insert_placeholder_4] => 0 [:db_insert_placeholder_5] => yi1uzwCRVuzZ2pksr1lO7EQfexVucHo72IvV5B-sNHM [:db_insert_placeholder_6] => -694 [:db_insert_placeholder_7] => 1335506409 ) in statistics_exit() (Zeile 93 von /home/www-data/xxxxx.info/modules/statistics/statistics.module).Luckily it only started to happen 30 minuted before I arrived for work, so I could fix it (by turning off the statistics module). So I am uncertain as to why this error still occurred.
Surely there must be a simple fix that can be coded into the stats module to prevent this (by checking the lenght of the 'timer' value maybe before trying to enter it into the db.)
If you check placeholder 7 in the above error message, which should be the timer, it actually contains the timestamp (1335506409) - where as the 'timer' should actually contain the 'Time in milliseconds that the page took to load.'
Strangely though, the timer field has the exact same settings as the timestamp field in the db - namely 'int(10)'. So I would have thought that is would just enter the value.
Here is the part of the stats module that cause the error - line 93 is the -> execute command.
And the timer_read function (includes/bootstrap.inc, line 457) is defined as:
Comment #3
xjmNice detective work @tecjam. Looking at the pasted results, though, it looks like it's not actually submitting the timestamp as the timer, though--that's the next placeholder, for the timestamp field. What it's submitting (that is out of range) is the negative value
-2in the original post, or-694in @tecjam's case.See:
So
timer_read()would return a negative value if$timers['page']['start']were greater than the current timestamp, or if$timers['page']['time']were negative.The fact that the timer is in a global means absolutely anything could mess with this variable if it wanted. Gross. (I personally would use a plain static in something like a
_timer_static_cache().) I did confirm that this global is only used in core in the three timer functions above.I can't figure out how this would happen unless one of the following were true:
Could you check and see if there are any modules installed on your sites that update the
$timerglobal or call any of the functions above?Comment #4
xjmComment #5
jcisio commentedIt might be a bug in microtime. As saying in http://www.php.net/manual/en/function.microtime.php#101628
Comment #6
xjmOh, see this:
http://www.php.net/manual/en/function.microtime.php#108255
So it sounds like we should put in some things to enforce non-negative values, at least, on account of the potential PHP/environment issues.
I'll work on this.
Comment #7
jcisio commented@xjm I'm not sure that's it. I don't think Drupal can be bootstrapped and exit in less than 1 microsecond. Usually it should take a dozens of thousand times more than that.
Comment #8
xjmThe point is that there is a documented instance there of chronological microtime()s resulting in negative values on windows. I'm not saying to use the guy's "solution"; I'm just saying that is shows there are PHP issues.
See also:
https://bugs.php.net/bug.php?id=42659
Would be good if people reporting this issue could confirm what web server/OS/PHP version they are running.
Comment #9
berdirI think if we can confirm this as a PHP bug and not caused by something we are doing wrong then we could simply set negative values to 0 with a reference to the php bug report and be done with it.
Comment #10
jcisio commentedThere are possibly multiple problems:
- microtime() might return incorrect value when called twice in less than 1 microsecond
- microtime() might have problem in scripts running for weeks (Windows only, bug reported in 2007 but has not had many replies)
- microtime() might return incorrect value because server time is update between the two calls because server time is update by ntpd - I think it is the most likely problem.
"Ugly", but simple and useful enough, fix is just set time = 0 if < 0. Complex, but may be better, is to use another function instead of microtime().
Comment #11
xjmI grepped for instances of
microtime()in core, and noticed that Symfony has aStopWatchclass similar to our timer, and Symfony'sStopwatchandStopwatchEventuse the same patterns we do (without any sanity checking that time is not going backwards).All instances of microtime in core:
All use of the
timer_functions:Comment #12
xjmSo the microtime is used for:
Based on the scope of this--it's way more than just a potential problem with funky statistics--I think we probably need more details about the host environments for those receiving this error. So, if you have encountered this error, please reply with:
register_globalsis enabled (it really really should not be).Edit: I also updated the issue summary.
Comment #13
xjmComment #14
xjmRegarding
microtime(), see also:Comment #14.0
xjmUpdated issue summary.
Comment #14.1
xjmUpdated issue summary.
Comment #14.2
xjmUpdated issue summary.
Comment #14.3
xjmUpdated issue summary.
Comment #14.4
xjm.
Comment #15
paulfrottawa commentedNot sure whats going on here when trying to connect to database in installing process. I have re downloaded drupal-7.15 and removed /usr/local/pgsql/data before reiniting: /usr/local/etc/rc.d/postgresql initdb and builded the database I needed.
Here the output:
SQLSTATE[22003]: Numeric value out of range: 7 ERROR: value "1344573360564" is out of range for type integer LINE 1: ...'placeholder-for-uid-1', 'placeholder-for-uid-1', '134457336... ^
Then hitting refresh produces this:
Table variable already exists.
PS: This problem didn't happen on another machine using the same OS & Version
_______________________________________________________-______________________________________
apache22
string "register_globals" not found in httpd.conf
lang/php5.4
database/php5-pdo_pgsql
lang/php5-extensions (
extension=ctype.so
extension=curl.so
extension=dom.so
extension=exif.so
extension=fileinfo.so
extension=filter.so
extension=pdo.so
extension=gd.so
extension=gettext.so
extension=hash.so
extension=iconv.so
extension=json.so
extension=mbstring.so
extension=mcrypt.so
extension=openssl.so
extension=phar.so
extension=posix.so
extension=session.so
extension=simplexml.so
extension=sockets.so
extension=sqlite3.so
extension=tokenizer.so
extension=xml.so
extension=xmlreader.so
extension=xmlwriter.so
extension=bz2.so
extension=mysql.so
extension=mysqli.so
extension=pdo_sqlite.so
extension=pgsql.so
extension=pspell.so
extension=zip.so
extension=zlib.so
extension=pdo_pgsql.so
)
apache22
FreeBSD localhost.localdomain 9.0-RELEASE FreeBSD 9.0-RELEASE #0: Tue Jan 3 07:46:30 UTC 2012 root@farrell.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64
############
I'm going to rebuild apache22 and related php5 ports with portmaster and try again.
no change:
Upgrade apache-2.2.21 to apache-2.2.22_5
Upgrade perl-5.12.4_3 to perl-5.12.4_4
Comment #16
paulfrottawa commentedHow I licked this was with
php53-pdo_pgsql
also had to reinstall php5 to php53 and php5-extensions to php53-extensions.
because the default php5 installs php54.
Hope this helps I'm happy as a clam now.
Comment #17
dsnopekI was running the latest Drupal 8.x from a couple weeks ago and had no problems and now today, after a
git pulland a fresh install, I'm hitting this error when uploading images! (So, not statistics module...)Here is the error I get:
It's setting 'timestamp' to
REQUEST_TIMEwhich is strangely negative: -1187834336Server OS: Debian 7.0
Web server: Apache 2.2.22
Hosting provider: none, I'm running in a VirtualBox VM on my local machine
PHP version: 5.4.4
NTP: Yes, it's configured to use ntp
register_globals: This was removed entirely in PHP 5.4 (http://php.net/manual/en/security.globals.php)
I hope that's enough info! The truly weird thing is that everything worked fine on this same system 3-ish weeks ago during Sprint Weekend...
Comment #18
dsnopekYesterday, I tried disabling NTP with no effect.
Today, I decided I was going to come back to this and try some more things to fix the problem (for example: switch from PHP 5.4 to 5.3 like @paulfrottawa did in #16). But now the problem is mysteriously gone!
REQUEST_TIMEis set to a positive value and I have no problems uploading images...If the problem comes back I'll try and do some more experimenting. I suspect that this has to do with PHP 5.4 or some changes made to the PHP environment by Debian 7.0 (which is still unstable).
Comment #18.0
dsnopekUpdated issue summary.
Comment #19
jhedstromClosing since the issue could not be reproduced 2 years ago, and also, D8 now requires php 5.4 (which seems to have resolved the issue).
Comment #20
cilefen commented#2855282: PDOException: Numeric value out of range (negative timer value)