A lot of times when you go to Features page, you are "greeted" with the error message produced by the admin module:

user warning: Duplicate entry 'sites/all/modules/admin/theme/slate.info' for key 1 query: INSERT INTO system (name, owner, info, type, filename, status, throttle, bootstrap) VALUES ('slate', 'themes/engines/phptemplate/phptemplate.engine', 'a:8:{s:7:\"regions\";a:5:{s:4:\"left\";s:12:\"Left sidebar\";s:5:\"right\";s:13:\"Right sidebar\";s:7:\"content\";s:7:\"Content\";s:6:\"header\";s:6:\"Header\";s:6:\"footer\";s:6:\"Footer\";}s:11:\"description\";s:0:\"\";s:8:\"features\";a:10:{i:0;s:20:\"comment_user_picture\";i:1;s:7:\"favicon\";i:2;s:7:\"mission\";i:3;s:4:\"logo\";i:4;s:4:\"name\";i:5;s:17:\"node_user_picture\";i:6;s:6:\"search\";i:7;s:6:\"slogan\";i:8;s:13:\"primary_links\";i:9;s:15:\"secondary_links\";}s:11:\"stylesheets\";a:1:{s:3:\"all\";a:1:{i:0;s:9:\"style.css\";}}s:7:\"scripts\";a:1:{i:0;s:9:\"script.js\";}s:10:\"screenshot\";s:14:\"screenshot.png\";s:3:\"php\";s:5:\"4.3.5\";s:4:\"name\";s:5:\"Slate\";}', 'theme', 'sites/all/modules/admin/theme/slate.info', 0, 0, 0) in /opt/clients/dev.openpublishapp.com/installtest/html/sites/all/modules/admin/admin.module on line 594.

The problem is this code:

<?php
    db_query
("DELETE FROM {system} WHERE name = 'slate' AND type = 'theme'");
   
db_query("INSERT INTO {system} (name, owner, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d)", $theme->name, $theme->owner, serialize($theme->info), 'theme', $theme->filename, isset($theme->status) ? $theme->status : 0, 0, 0);
?>

in _admin_theme_rebuild() function.

Long story short: we have a concurrency issue here: SQL Delete and Insert are not locking the system table so an "insert" from a concurrent HTTP request is screwing things up.

Concurrent HTTP requests on an admin page sounds less likely, but actually it's not considering we are talking about the Features page, which uses ajax calls to check status of each feature and fires multiple Ajax requests almost at the same time. So pretty stiff concurrency there. And if you follow the logic of _admin_theme_rebuild() you will see that it uses 'admin_theme_invalidated' variable, which is set to TRUE at least once per HTTP request by admin_system_info_alter() i.e. _admin_theme_rebuild() is called for *each* Drupal request (that contains /admin in its path).

Quick solution is to change the hacky "delete/insert" combination with an SQL Update if record already exists.

Corresponding patch is attached.

Files: 
CommentFileSizeAuthor
#1 admin.module-696304.patch1.54 KBirakli

Comments

StatusFileSize
new1.54 KB

I believe the locking subsytem is in D6 core now, so there might be possibility to use that too.

I think the issue is that Admin is not using the locking framework, but running a private function instead (see #251792).

The patch in #1 did not help. My temporary soltion to this is using INSERT IGNORE ... not sure if this is MySQL specific however.

INSERT IGNORE INTO {system} (name, owner, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d)

Status:Active» Needs work

Changing status, as there is a patch.