Nodes, comments and users use a transaction when saving, taxonomy terms don't. Any _save() function with either more than one query, or a hook implementation which could potentially fire another query, should use a transaction. List of possible candidates from that other issue:
taxonomy_term_save()
taxonomy_vocabulary_save()
file_save()
node_type_save
user_role_save
filter_format_save
image_style_save
menu_save
menu_link_save
_menu_router_save
path_save
system_date_format_save
Don't necessarily need to do these all in one go. For API consistency, database consistency and write performance leaving this as critical, although I think it only really is for the $entity_save() functions at least in D7.
Comment | File | Size | Author |
---|---|---|---|
#43 | 776222.patch | 47.55 KB | bfroehle |
#17 | 776222.patch | 46.27 KB | bojanz |
#8 | 776222.patch | 47.69 KB | bojanz |
#6 | 776222.patch | 48.59 KB | bojanz |
Comments
Comment #1
catchNot really a release blocker, but should be 'major' if and when we get that issue priority.
Comment #2
jpmckinney CreditAttribution: jpmckinney commentedComment #3
jpmckinney CreditAttribution: jpmckinney commentedComment #4
jpmckinney CreditAttribution: jpmckinney commentedDidn't intend to change issue tags.
Comment #5
bojanz CreditAttribution: bojanz commentedPatch is coming
Comment #6
bojanz CreditAttribution: bojanz commentedHere's a first attempt at a patch.
Notes:
1) If a query fails in file_save, and we do a rollback, we just return an unchanged file objact. Should we return something else, like a FALSE?
2) In _menu_router_save() we return the $menu even though we don't alter it in any way. Also, it's not documented. Should we drop the return?
3) filter_format_save() uses $return for the same purpose as the other functions use $status. Also, the return is not documented.
3) path_save() has a $status, but doesn't return it, unlike other _save functions. Having it return $status, and document it in the docblock should be good, but I'm not sure if it's in the scope of this patch.
Let me know what the next steps should be, and how much cleanup should be done in the scope of this issue.
Comment #8
bojanz CreditAttribution: bojanz commentedAgh. This should be better.
Comment #9
sun.core CreditAttribution: sun.core commented(and elsewhere) Trailing white-space.
Powered by Dreditor.
Comment #10
bojanz CreditAttribution: bojanz commentedIs this still wanted/needed/on the table?
If yes, I can reroll properly, this was done when I was a much bigger core newb :)
Comment #11
fago#8: 776222.patch queued for re-testing.
Comment #12
fagoFollowing up from #651240: Allow modules to react to changes to an entity.
At least all entity_save() functions should make use of transactions. (Of course others too).
Comment #14
bojanz CreditAttribution: bojanz commentedWill do a reroll this afternoon.
Comment #15
sunsubscribing
Comment #16
bojanz CreditAttribution: bojanz commentedI need a patch for my time management skills.
Here's a reroll.
Comment #17
bojanz CreditAttribution: bojanz commentedHad extra whitespace in one line.
Comment #18
fagopuh, this one is really hard to review as it moves quite some code. It looks good to me though, except for:
rollback() doesn't take any arguments (any more).
Comment #19
bojanz CreditAttribution: bojanz commentedDidn't know that.
Changed.
Comment #21
bojanz CreditAttribution: bojanz commented#19: 776222.patch queued for re-testing.
Comment #23
bojanz CreditAttribution: bojanz commented#19: 776222.patch queued for re-testing.
Comment #24
carlos8f CreditAttribution: carlos8f commentedsubscribing
Comment #25
bojanz CreditAttribution: bojanz commented#19: 776222.patch queued for re-testing.
Comment #27
bojanz CreditAttribution: bojanz commentedRerolled.
Comment #29
bojanz CreditAttribution: bojanz commented#27: 776222.patch queued for re-testing.
Comment #31
bojanz CreditAttribution: bojanz commented#27: 776222.patch queued for re-testing.
Comment #33
bojanz CreditAttribution: bojanz commentedWell, I'm confused.
Comment #34
bfroehle CreditAttribution: bfroehle commentedWell the error in #27 is accurate. With a fresh copy of HEAD and #27 applied, I get the following when trying to install from scratch:
An AJAX HTTP error occurred. HTTP Result Code: 200 Debugging information follows. Path: http://localhost:8888/drupal/install.php?profile=standard&locale=en&id=1&op=do StatusText: OK ResponseText: Home | Drupal @import url("http://localhost:8888/drupal/modules/system/system.theme.css?0"); @import url("http://localhost:8888/drupal/modules/system/system.messages.css?0"); @import url("http://localhost:8888/drupal/modules/system/system.menus.css?0"); @import url("http://localhost:8888/drupal/modules/system/system.base.css?0"); @import url("http://localhost:8888/drupal/modules/comment/comment.css?0"); @import url("http://localhost:8888/drupal/modules/field/theme/field.css?0"); @import url("http://localhost:8888/drupal/modules/node/node.css?0"); @import url("http://localhost:8888/drupal/modules/search/search.css?0"); @import url("http://localhost:8888/drupal/modules/user/user.css?0"); @import url("http://localhost:8888/drupal/modules/system/system.admin.css?0"); @import url("http://localhost:8888/drupal/modules/system/system.maintenance.css?0"); @import url("http://localhost:8888/drupal/themes/seven/reset.css?0"); @import url("http://localhost:8888/drupal/themes/seven/style.css?0"); Home Installation tasksChoose profile(done)Choose language(done)Verify requirements(done)Set up database(done)Install profile(active)Configure siteFinished SQLSTATE[42000]: Syntax error or access violation: 1305 SAVEPOINT savepoint_1 does not exist
This happened after all (or almost all) of the modules in the standard profile had been installed and configured.
Installing the minimal profile works fine.
Analysis:
I split out each file that was changed into it's own git commit and ran git bisect, which pointed to the changes in node.module as the problem.
Comment #35
bojanz CreditAttribution: bojanz commentedIt's strange because it's a reroll of a patch that worked fine a few weeks ago, and it just wraps some queries in transactions....
Comment #36
bfroehle CreditAttribution: bfroehle commentedConsider the following sequence of SQL commands:
This means that we effectively cannot create tables during database transactions.
node.module
triggers this code sequence.node_type_save()
invokescomment_node_type_insert()
which createsfield_data_comment_body
andfield_revision_comment_body
tables in MySQL. The table creation process commits the transaction and so causes the error when we later try to release the savepoint.See #995820: Exception sometimes when saving and #1004332: db_set_active() commits transactions for related problems.
I'm not sure we can use db transactions for any of these _save() hooks which invoke other modules (which may then do anything, like create tables).
Or maybe we can catch DatabaseTransactionNoActiveException (or whatever exception is thrown) and choose to ignore it? (Edit: No, not feasible, as the exception is thrown midway through code execution, of course).
Maybe we can commands which commit transactions (like CREATE), nullify any pending transactions (how?) and reset the transaction depth to 0?
Edit: Corrected the above text to replace
node_save()
withnode_type_save()
, as @catch pointed out.Comment #37
andypostSeems like node creation workflow broken somehow because node_save() should NOT create any tables!
Also we need document this for contrib!!!
Comment #38
bojanz CreditAttribution: bojanz commentedGreat job investigating this, bfroehle!
In my opinion, telling contrib authors "don't create tables from these hooks" is perfectly ok. I mean, who even does that? It's usually a task done during module install.
I'm wondering what caused the install to break.. Maybe a git blame would find the issue that modified the node code?
Unfortunately, I don't have any more time for this issue.
Comment #39
catchYou mean node_type_save() rather than node_save() here right?
Comment #40
andypostis there any description of node_type_save() FSM? we have different states of type creation, suppose each DML could have own transaction but DDL has just a state - same way we could control of final state of operation
Comment #41
bfroehle CreditAttribution: bfroehle commented@catch in #39: Yes, node_type_save(). Comment edited to reflect this.
Comment #42
bfroehle CreditAttribution: bfroehle commentedI created another issue, #1007830: Nested transactions throw exceptions when they got out of scope, to work out the database issues from #34 and #36.
Comment #43
bfroehle CreditAttribution: bfroehle commentedA proof of concept for #1007830: Nested transactions throw exceptions when they got out of scope... The patch in #27 of this issue plus the patch in #11 of that issue. This should help verify that the patch in that issue is reasonable and useful.
Comment #45
bfroehle CreditAttribution: bfroehle commented#27: 776222.patch queued for re-testing.
Comment #46
marcingy CreditAttribution: marcingy commentedMoving to d8.
Comment #47
marcingy CreditAttribution: marcingy commentedAnd needs work as patch likely needs a reroll.
Comment #48
Anonymous (not verified) CreditAttribution: Anonymous commentedsubscribe.
could we add user_role_grant_permissions() ? its kinda nasty right now, pass in the wrong easy-to-typo permission string, and you'll get half what you need, then kaboom.
a transaction and a try-catch would help a lot.
Comment #49
tim.plunketthttp://api.drupal.org/api/search/8/DatabaseStorageController::save has this now, I think that takes care of the entity types.
Downgrading.
Comment #50
sunActually, I think this is fully done in D8 by now (whereas some more granular bits are still being discussed).
Given the amount of changes that were required, I don't think this can be backported.