|
[
Permalink
| « Hide
]
Eloy Lafuente (stronk7) added a comment - 22/Oct/09 11:40 PM - edited
-1 (note the vote isn't against the implementation but against the the concept, nested transactions, like oil and water!) :-P
my +1 for single level transactions allowed only in some layers:
If we use it in external API we can not use it in enrol and auth any more, because they are supposed to use the external API too. If we decide to use my patch it can be done later in the stable 2.x because it does not change API. If we want Tim's patch it has to be done ASAP. +1 for the tests, -1 for the implementation.
I prefer Tim's implementation (with an added warning on nested transactions) because
Eloy, I'm leaving this to you because you are the best database expert I know. Can you think about all the angles proposed here and make a recommendation so we can get this in ASAP?
Here it's my opinion:
A) About the concept.
I think it's easy to fulfil these 4 points. B) About the implementation.
And that's all. If anybody is against any of these 8 points, please comment ASAP. Here it's how the logical transactions should (must) be used: try { $DB->start_transaction(); // Execute a bunch of SQLs here // trying to keep out other things like files and friends as possible $DB->commit_transaction(); } catch (dml_exception $e) { $DB->rollback_transaction(); throw $e } catch (ddl_exception $e) { // Just in case we want to play with transactions at install/upgrade $DB->rollback_transaction(); throw $e } catch (exception $e) { // Only if you want to catch other exceptions // Do whatever you want, unrelated to transactions 100% } That's all. With the 12 points agreed I'll say +1. Surprisingly I have to agree with Eloy again
begin_transaction(), commit_transaction() and rollback_transaction() are my favourites
That all sounds good for me, except I wonder if we can do something a bit easier for programmers, like some wrapper function, or something. Just to avoid always having to catch at least 2 and often 3 different exceptions.
If php did proper grown up programming constraints , we could do something like $DB->transaction_wrapper( { /* block containing all the statements we need */ }); And that could handle the catching/ throwing of the first 2, but we can't easily do that in php, and any workarounds are probably ugly as hell. If anyone has any better ideas, that would be awesome. i also mildly think {begin,commit,rollback}_transaction are too long... and I would use begin(), commit() and rollback() personally.
I think that in majority of cases you would just use one catch block that rolls back and rethrows the exception.
Thank you for thinking about this Eloy.
On naming, I propose $DB->start_transaction(); I still don't understand why people object to the token bit, especially as I have already implemented it, and it makes the link between matching start and end transaction clearer, but I also don't care if you throw that away. Except that, using a token object that will automatically be __destructed might mean that you could avoid having to write the } catch (dml_exception $e) { $DB->rollback_transaction(); throw $e } catch (ddl_exception $e) { // Just in case we want to play with transactions at install/upgrade $DB->rollback_transaction(); throw $e bit in a million different places, which would be nice. Hmm. At the very least can we make a common superclass of dml_exception and ddl_exception, to save one catch clause. oh, why would you want to differentiate between different exceptions? you have to rollback in any case, right?
We could also take the exception as mandatory parameter in rollback method: try { $DB->begin_transaction(); // Execute a bunch of SQLs here // trying to keep out other things like files and friends as possible $DB->commit_transaction(); } catch (Exception $e) { $DB->rollback_transaction($e); } grrrrr I agree with Penny, it's a big pain even compared to my transaction_wrapper for instance (and I think it could be better than that).
Do we even need rollback feature at all? I know there was some discussion about this. But since rollback is not possible for people without transaction support. IMO, fixing the database (if required) should be the responsibility of whoever catches an exception (and it can be any exception, not just a db one). My proposal would be something like, For ordinary code: $token = $DB->start_transaction();
// ...
$DB->finish_transaction($token);
(token is kind of optional, I like it if these things are checked though as it is more likely to detect errors at right point of code). In the unusual case that it is necessary to catch an exception and handle it (ie not leave the database in the broken transaction): try { // ... } catch(Exception $e) { // Handle it, and... $db->clear_errors(); // Will do rollback if necessary } For most cases where exceptions are only displayed as errors, it should be correct and OK to use the automatic rollback facility (that applies for any transaction which is not committed) Also, to assist developers, code should enforce this. Consider this situation: try { // Database code throws a dml/ddl exception } catch(Exception $e) { // oops I didn't clear errors } // Now I do more database code, which will work in mysql! This code will 'work' in MySQL with the broken table model, but not when inside a transaction on a db that supports transactions. As a solution, I propose that whenever a dml/ddl exception is thrown, the database should automatically go into 'there are errors' state. In that state, any further call to a database function except clear_errors automatically throws exception straight away, until you call clear_errors. Anyhow maybe there was something in the previous discussion which made this approach impossible, but if not, it certainly seems a lot nicer for coders than eloy's source above eta: Hah! I didn't look at tim's design, apparently that had token in too? neat. I am afraid developers will always find ways to abuse transactions and exceptions, I think we can easily review all such places using simple grep search in core code once a month or two. Tuesday reviews are another good opportunity to inform devs about wrong coding style.
The simple use without try catch and rollback is also possible, we do not need tokens for that - any exception propagates to the default exception handler which rollbacks the transaction if active. Tim's patch is recommended reading, sp the 'token' bit
Petr said: that is just not true. Any exception handler that gets declared can receive and "handle" the exception. And it is not obvious – definitely not easily greppable – how our callstack will end up (for an example, see the wild things print_error() ends up calling in HEAD these days). So we have to be insanely careful with this. To be frank, all the complexity we can see here, and the complexity we'll allow to be embedded in the code with no visible indicators makes me side with Eloy, and say first stage: nested transactions should not work, and an exception thrown. If we come across something that absolutely needs nested transactions, we can come back to this discussion and – with the benefit of having a specific case – see what' s the best approach. >> any exception propagates to the default exception handler
>that is just not true. No, it is true. I wrote the exception handler we have now, it is not finished yet, I hoped it would be done together with the pagelib rewrite Tim did recently. Mandatory steps in each exception handler are: If you want more information in the ex handler the most important part is where was the first level of transaction started and the type of exception that caused the problem, again you do not need tokens for this. I already found one part that absolutely needs the transactions - the external API functions that modify multiple records. There is no standards compliant way to return incomplete results such as 1 of 3 records successfulyl modified, so instead at present we roll back and report failure. The problem is we can not use exceptions anywhere else in core code, but also we can not use exceptions in plugins like enrol or auth because they are supposed to use the external API Petr >>> any exception propagates to the default exception handler
>> that is just not true. > No, it is true. Maybe I haven't been clear – "default exception handler" means anyone can override it: try { foo(); // which will do many complex things // including setting up a transaction // and exec'ing SQL that fails // but the caller is naive so... } catch ($e) { // yeah, foo fails sometimes, log it // and keep running... error_log("foo() barfed again, sigh") } as you see, there is no 'throw($e)' in there. > I already found one part that absolutely needs the transactions Definitely. But I am saying nested transactions. Does your code absolutely need nested transactions? Can we defer implementing them (and put instead code that generates a warning), given that they are a recipe for disaster? OR implementing them on a if (defined('DB_NESTED_TRANSACTIONS_YES_I_WANT_TO_SUFFER')) {
for the adventurous? hmm, yes exceptions are very easy to abuse
If anybody blindly catches exceptions and ignores the problems the code will definitely fail and db would be borked. We did not use transactions yet much, so no we do not need nested exceptions, but the fact that external API is going to use them actually prevents anybody else from using them in majority of cases. PS I like Sam's idea that once an exception has been thrown, $DB object refuses to continue executing queries. This helps with Mysql's ... specialness.
Also since Tim has already written tokens, I don't see what the problem is keeping them - it helps debug errors much easier (eg you tried to close a transaction called pony, while you still had an open horse transaction) - helps us see the nesting errors. if we use the tokens we can not use the simple case without try catch because the inner transactions will not be closed and it would be printing debug warnings when any exception thrown
I do not like the idea that we should somehow cripple DML when rollbacking, because we use DML also for DB sessions - it would undo all changes done to sessions before start of the transaction the more I think about this the more problems I see:
I was reviewing the file API a bit too, the DB rollback should work fine there (includes file deletes) - the only problem might be orphaned file contents in file pool. I think it should be mandatory to send exception as parameter when rolling back, because otherwise we will not have enough information when logging rollback problems hmm, you are not supposed to be nesting transactions every day and if you do it should be relatively simple code, if not you are most probably doing something wrong > Also since Tim has already written tokens the fact that some patch exists is not an argument for me, we will have to live with the decision here for many years I am afraid - I would prefer to "waste" several days now in order to make good decision To sum it up, Tim is proposing: // option A $transaction = $DB->start_transaction('optional_imlazy'); try { // Do stuff ... $transaction->commit(); } catch (Exception $ex) { $transaction->rollback($ex); } I was proposing more traditional approach: // option B $DB->start_transaction(); try { // Do stuff ... $DB->commit_transaction(); } catch (Exception $ex) { $DB->rollback_transaction($ex); } // or simply expecting rollback is handled by default exception handler or another transaction layer $DB->start_transaction(); // Do stuff ... $DB->commit_transaction(); Both of these options support nesting, both of them can work in one level only, option A can give more debugging options (this benefit is lost if we allow simple syntax without rollback), option B is more backwards compatible In any case this discussion is great - now most core developers perfectly understand transactions and we touched the topic of exceptions too, yay!!
thanks, Moodle community rocks :-D Oki, thanks for input. Some comments:
In any case, I don't see why your example, Martin, is problematic at all (sure I'm missing something): 1) The foo() contains the top-level transaction usage. So, if it fails, it will mark (and really rollback DB, as it's level 1) the transaction as failed and throw dml exception. Then your "default catch handler" will error_log(). No problem.
Also, if we want to avoid the try/catch blocks for normal usage then we must guarantee that any dml/ddl exception sets the transaction status (if in-transaction only, obviously) to "rollback-able" so, once again, the default exception handler, in charge of closing all the levels will perform the final (and real) rollback, or alternatively, the outer in-transaction finish will, if for any cause somebody has broken the exception chain. In any case, remember that only one (dml) exception must be caught in my alternative. And I find it more "explicit". IMO a small pay for people wanting to use those non-existent "nested transactions". Perhaps your alternative leaves too much relying in the magic dark of the default exception handler, when it should do that magic, agree, but only as an exceptional measure (effectively roll-backing but throwing one "transaction exception"). Normal behaviour should be to have things finished/aborted in top level of the transaction and not in the default exception handler. Ciao Please, round 1 iteration 2 comments now, after that, I'll remake the original points to start round 2 (hopefully final). Thanks everybody. I really like Sams proposal to fail $DB stuff until clear_errors() is called.
But I am sure there are going to be code paths with crap data which we are unlikely to test - are the side effects of failing all future database interactions at that stage going to be more catastrophic than allowing things to continue? Perhaps it would make sense to only enable this in developer debug mode? Great to see this discussion!
Hi,
just attaching one simple diagram showing all the chaining levels we have when handling exceptions and transactions at the same time. I've tried to explain the (my) ideal behaviour on each level thinking how the system will work under different situations (use cases). Feel free to comment /add use cases where something could fail badly. Please use the "nomenclature" above and explain the flow leading to problems. Hope it helps, ciao looks like you ahve switched the default exception handler and footer code, any uncaught exception skips normal footer - there is that ugly hack that tries to close all containers if header already printed and prints another footer and stops.
Hmmm, the open transactions have to be detected in: we have discussed this a bit more wit h Eloy, we both understand it the same way, it is just me who did not understand the picture much
Yes, yes. Not all the "boxes" above are always involved.
So, normal (correct) operation will involve using only green/yellow areas (if everything is properly try-catch on each transaction). And then, if anything goes wrong, "footer" will detect it (for requests having "footer", obviously) and will delegate the process (back) to the default handler. Still within <html>. And finally shutdown will detect it for the rest of situations, producing debugging and delegating the task of rollback() to connection dispose. Useful for requests not being <html> based that haven't being processed by the default exception handler. Pretty covered IMO. Just trying to imagine situations were the diagram above wouldn't work. So I think we are agreeing Petr (yay!). Just the graph isn't as self-explanatory as I had imagined, lol. Ciao Oki here there are the latest conclusions for the "nested transactions" thing, trying to join as many ideas as possible from previous discussions:
General principles:
The API:
The Flow:
And that's all. Please, comment, specially the QUESTION above if we are using the "short" coding alternative. Or anything else you want to add/object/array :-D Ciao oh, Eloy we need to know the exception when rolling back, we can just
$tr->abort_transaction($ex); which would automatically rethrow the exception the example is not correct because in majority of cases we are going to catch (Exception $e) iho
Two comments
1 - We don't all agree it is a good idea to have nested transactions. I vote yes for transactions, not to nesting unless we can get a good example. Eloy, you started saying we should keep them separate – have you got a good case where you really need nested tx? 2 - You correctly state that "If one transaction (at any level) has been marked for commit() it will be possible to change that status to rollback() in any outer level." A really nasty result of this is that your code will say $db->commit() but you have no idea whether the commit actually succeeds. This #2 is rather problematic if you need to change any state that is not in our DB. And I think it means it is a misdesign to do nested transactions. If we do not do nested transactions, we can say: on DBs that support transactions, $db->commit() means COMMIT. This is an important feature when sync'ing with external systems for example. COMMIT guarantees it is done, for real. If we go the nested way, it will never mean anything, even on DBs that are ACID. For example: with nested tx, how can you delete a file? $db->begin() $db->execute( DELETE whatever row has the ref to the file ) $db->commit() if ($db->count (references to that file) === 0) { unlink(/path/to/file) } return true; In the above example, if the calling code has an unrelated error and issues a rollback, we've corrupted our state. you sure can handle files now with nested DB transactions - because our files line in the files table now
Comments:
General: 1) First of all, agree with ML (as previously said). Nested transactions are a fallacy. Comments: 1) Changed API names as requested (knowing that they don't really perform the commit/rollback) Ciao Petr, this is a problem specifically with the Files implementation. Do read my (pseudocode) example carefully, you will see the problem.
In general, our move to transactions should get us in the right path towards (in some far future) using transactions properly, and requiring a transactional DB. Nested transactions defeats this completely. I wrote it somewhere else already, I already used the transactions in extenal API in functions that take array as parameter (for example create users) and the execution somhow fails in the middle of the loop. What should we do then? Our function description expects only one type of return or exception, obvious solution was to rollback and return error instead of creating just half of the entries.
Problem is that this new external API should be used in local customisations, custom Enrol and Auth plugins, etc - but if we have just one level we can not use transactions in these parts at all. I understand your example above, we should be imho moving away from file storage in dataroot, only files that are temporary or caches should be stored there - there should not be many rollback problems there I hope. Another problem I highlighted above are external events, they are imo much bigger problem then the files. Another version:
General principles:
The API:
or $transaction = $DB->start_transaction();
// Perform some $DB stuff
$DB->commit($transaction);
NOTE: To decide one of the alternatives.
or try { $transaction = $DB->start_transaction($optional_name = null); // Perform some $DB stuff $DB->commit($transaction); } catch (exception $e) { $DB->rollback($transaction, $e); } NOTE: To decide one of the alternatives. The Flow:
To explain/decide: the QUESTION and NOTEs above Ciao About the syntax to decide. Simple: First 5 first opinions commented here will be taken into account. No more claims allowed later. ciao
+1 for $transaction->commit()
voting for the 1st syntax, the one using the same approach that recordsets
Yep, the consistency with $rs = $DB->get_recordset_sql() sounds good. +1 for $transaction->commit()
3 votes = will be using the like recordsets syntax. Reworking the specs once more
Another version:
General principles:
The API:
The Flow:
Last iteration, I hope. Please review once more. Hi Petr,
I am not talking about dataroot. I am talking about how to code the internal implementation of our file storage. If we use nested transactions, we never know of the $db->begin(); DELETE .FROM ... ; $db->commit(); happens or not. Unless we poke the $db object to ask "are we in a tx already?" and error out if we are. Any code that has any kind of side-effect has to refuse to work if nested tx are in use. our internal implementation of file storage survives the DB transactions, we are not deleting immediately the orphaned file contents, and if you add new file content the worst case is it will be slowly accumulating there, most probably you will retry the attempt later and the file would be used then
Yeah, FileAPI (100% luckily won't be really exposed to problems like that). But others could. So perhaps we should prevent transactions at all when dealing with external systems or alternative storage places. Say... mailouts for example.
well, the new notifications framework should definitely deal with this somehow (this includes mailing)
+1 for Eloy's last spec, because I like his smile.
So the last version, after some chats is here:
Another version: General principles:
The API:
The Flow:
Related tasks:
Changes from previous version:
I am ok with the proposed transaction API, and exceptions approach.
But I am not convinced at all about the nesting:
Please please please, either skip nested tx or at the very least make moodle gargle very loud and ugly complaints when nested transactions are used. Hi Martin,
as commented repeatedly I agree with you I don't buy the "nested transactions" concept. 100% And agree the commit() becomes maybe_commit(), in fact it becomes do_nothing(), because final decision of real commit becomes delayed until top level is closed. That was my initial pov about to remove the "commit" and "rollback" words from the API, but finally was agreed to have them there. It's just a matter of knowing what they mean. About uses, well, I can see the point about, for example, making the creation of one course, as one transaction (it involves creating various records in various tables) and any failure in the process being nicely reverted sounds ok (normal 1-level transaction). But problems arise when I'm trying to use that create_course() function in web services, or in restore or bulk creation of courses... where I also want to handle transactions. For example, I want to make the whole restore process to run in a transaction, it creates zillions of records and any failure is really an (almost invisible luckily) mess. But, with real 1-level transactions I'll be forbidden to use create_course(), while with the "delegated transactions" (uhm, perhaps whe should name them that way!) I'll be able to do so and restore will have the control. In any case, note that we never will rely in transaction status (no matter if they are 1-level real ones or delegated/nested ones). Mainly because not all our DBs support them. It's ONLY one measure to reduce the mess in DB and not to eliminate it! Only to reduce! It's only one anti-mess-feature for DBs supporting them. And, as said, this principle is the same, no matter if we are using 1-level or n-level ones. I think we need to be 300% careful before start using them and just then we'll discover real problems (if they exist). But, in the other side, all reports I've found from ADOdb, Mahara, OU... comment they are working ok (and I'd add: "if under control"). That's the key, preventing any use related to "external subsystems" will help, for sure. Finally, 100% agree about your idea of "moodle gargle very loud and ugly complaints when nested transactions are used". Not because of complaints, but to let the developer know he's executing code that have transactions within it. So, perhaps I'd add one new debug parameter, enabled by default, call it "show nested transactions stacks", so, in DEBUG_DEVELOPER mode, it will be showing the whole transactions stack when the inner one is commit/rollback. Definitively that will help. So, having the feature will lead us to be able to use it, surely as 1-level only in most places initially (islands), but with the possibility of allowing caller code to take the control in the future (delegated). Uhm... the most I think the most I like the "delegated" concept. Ciao New specs introducing the "delegate" concept everywhere:
General principles:
The API:
(note I've abused the "delegated" concept in the examples above, just to enforce it) The Flow:
Related tasks:
Changes from previous version:
by the way, yay proposed API.
martin L - examples for this sort of thing are really, really easy. it's a coding pattern (which you would no doubt call an antipattern)... function does_something() {
// makes 2 db changes, eg insert record and update another to point to it
}
so clearly function does_something should have a transaction because it would leave the database in a corrupt state if only one of those two db changes happens. now let's say it can be called on its own by end-user code, or else: function does_something_complicated() {
does_something();
does_something_else();
}
Again, this operation contains two tasks both of which make db changes. You don't want to leave the database in a half-finished state so it is important that it's in a transaction... etc... Really the nested transactions are not so much nested transactions (because that's impossible). The API is a way of saying 'I require that there is a single transaction covering these statements' (which might be a transaction just for them, if you called does_something() directly, or a larger-scale transaction, if you called does_something_complicated().) I agree there are problems when transactions cover actions with external side-effects that cannot be rolled back but I think these should be manageable. Perhaps there are some API extensions that might assist this (I am speculating not recommending necessarily): $DB->require_no_transaction(); Calls print_error if there is a transaction. ALTERNATIVE API: $transaction = $DB->start_top_level_transaction(); (or start_transaction(true) or whatever) Same as start_transaction but throws error if there is a transaction already underway $DB->add_rollback_handler($callback, $param); $DB->add_commit_handler($callback, $param); Gives error if there is no transaction. This could be used for things like unlink() where you don't mind if it hangs around and does it only after all db activity is successful. Obviously it would not address the problem in something like cron sending out emails or anything that needs to do a large quantity of work, for that case require_no_transaction would be more appropriate. And it cannot of course solve the problem where you really want both things to be guaranteed on each other, i.e. if the unlink fails then you want the database not to have gone ahead. Also: I don't think we should add debug information that's on by default for nested transactions. If doing something like that then there should be a way of preventing it in code otherwise it becomes a meaningless warning - even if original developer sees warning and checks it is okay, other developers would see the warning and not know whether it's a problem or not. For example it might be possible to specifically tell a transaction that it is OK if it has parent transactions (thus turning off the warning). yeah, Sam, I was thinking about to be able to differentiate outer "transactions" from the rest some minutes ago (using it to mark places than cannot be delegated and are mandatory top level). But finally didn't wrote about that to keep the "getter" uniform.
But I like a lot the: $DB->delegated_transaction_allowed(true/false); (or similar) to be able to mark places where transactions cannot be used. I like that a lot!! Ciao Oki, so we are talking about to have (only using the long API to show all methods):
try { $transaction = $DB->start_delegated_transaction(); // Perform some $DB stuff $transaction->delegated_commit(); } catch (exception $e) { $transaction->delegated_rollback($e); } or, alternatively: try { $transaction = $DB-> start_delegated_transaction(); // Perform some $DB stuff $transaction->allow_commit(); // To enforce the "this might be a real commit or no. I'm just authorising it" } catch (exception $e) { $transaction->rollback($e); // To enforce the "This is going to be reverted (rollback). I've decided" } And also, have the possibility to specify: $DB->transaction_required(); (in some better way, of course) I like the 'alternatively' version
+1 for transaction_required / forbidden what does transaction_allowed do? disables (reset) the required/forbidden status, i.e. developer is free to use or no transactions.
And I' would keep the "delegated" concept in the getter. Edited: Naaah! required hasn't sense, just use them. Only forbidden/allowed has. grrr Another option for the start method (yes I am talking about naming again) would be
$transactionref = $DB->require_transaction(); i.e. you are not 'start' - ing a new transaction, you are just requiring that one has been started (either now or before). and what you get back isn't a 'transaction' object but some kind of reference referring to a transaction which is managed by $DB. I thought required/forbidden would be 'instant' methods, to be used like this (w/ my name) if you want to ensure that your transaction is a 'real' one: $DB->transaction_forbidden();
$transactionref = $DB->require_transaction();
...
$transactionref->allow_commit();
// no need for allow method
however in this case it might be better to have a different method $transactionref = $DB->start_transaction(); // throws error if there is already one ... $transactionref->commit(); // instead of 'allow_commit', allowed only if you used start_transaction If it wasn't clear, the idea was, it's actually the same code, but different function names (exactly same behaviour but with check for no previous transaction) so that you have API for either a 'nested' style transaction or a 'Martin L' style dictatorial transaction anyhow I think this is edge stuff, overall eloy's last looks good to me. Hi Sam,
I think it's better to use always the same: $DB-> start_delegated_transaction() to get one transaction (real or delegated), it doesn't matter. And then, have the $DB->transaction_forbidden(); instant check apart, because they aren't always related (the check and the creation of one real - level1, transaction). For example, parts of code exposed to problems like the commented by ML, that we know can be causing problems under transactions, will be enclosed simply by $DB->transaction_forbidden() and $DB->transaction_allowed() and the code, obviously won't be trying to create any transaction, as far as it's know to cause problems, but we'll have the code "isolated" from being used in transactions. Summarising, check + getter together aren't always the expected use. Let's keep them separated. Ciao I like the idea of separating "hard", must-be-top-level transactions from "soft", "delegated" transactions.
I don't care too much about the name itself, I like anything that reserves "begin()", "rollback()" and "transaction()" for the hard, real database transactions. Principle of least suprise for newcomers, and hwen reading the code things make sense. Maybe "soft" is a good alternative for a name? $DB->start_soft_transaction() Moved current specs to: http://docs.moodle.org/en/Development:DB_layer_2.0_delegated_transactions
Let's continue discussing in forum, as Tim has re-started it at http://moodle.org/mod/forum/discuss.php?d=135847#p596749 Ciao sending new unfinished patch - mostly missing comments, should work fine, unittests included
I hope it fulfils all requirements ciao Eloy, could you please review my latest patch? Please concentrate on the implementation, not the actual names of methods, we can change them later
updated after feedback from Eloy
some more TODOs for tim in patch
I guess http://tracker.moodle.org/secure/attachment/18803/delegated_transactions_20091104_3.patch
My +1
Petr just three comments: 1) In the mysqli driver, rollback_transaction() method, there is one old call to parent::rollback_sql(). Must be out.
And that's all, as said, my +1 Then it will be a matter of:
Ciao 1/ patch committed
2/ transactions are now forbidden when sending messages 3/ instant events are queued when in transaction - this is slow, but should work 100% for external systems dependent on event notifications going to work more on unit tests and docs
committed detection of missing commit|rollback
last todo:
the DB checker should already detect old uses of begin_sql)) and friends, right?
I think it already looks for "begin_sql()" but not for ">begin_sql()"... adding it right now.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||