← Back to team overview

maria-developers team mailing list archive

Re: Help with running a transaction inside the server

 

Sergei Golubchik <serg@xxxxxxxxxxxx> writes:

>> Right. In fact, I think perhaps I am doing it in a single statement already:
>> 
>>   mysql_reset_thd_for_next_command(thd, 0);
>>   open_and_lock_tables(thd, &tlist, FALSE, 0);
>>   table->file->ha_write_row(table->record[0]);
>>   table->file->ha_rnd_pos_by_record(table->record[0]));
>>   table->file->ha_delete_row(table->record[0]);
>>   ha_commit_trans(thd, FALSE);
>>   close_thread_tables(thd);
>>   if (!in_transaction)
>>     ha_commit_trans(thd, TRUE);

> Yes, that is all there is to it. A transaction starts automatically on
> the first "transaction-initiating statement" (according to the
> standard).
>
> You don't need to set autocommit. If you do - you won't need the second
> ha_commit_trans(thd, TRUE). You current code, as far as I understand,
> will work correctly, whether autocommit is on or off.

Aha, I see. Ok, thanks a lot for clarifying that!

> In that case, though, your changes may be part of
> the already running transaction - do you want that?

Indeed, the whole point of this is to be part of the running transaction. So
that slave state becomes crash-safe, unlike now where we first commit and then
write to the file relay-log.info - and a crash in the middle makes us
inconsistent, etc.

So the intention with record_gtid() is to add an extra statement to the
transaction being replicated - which updates the slave state. If we crash, we
either recover or rollback the whole transaction, which includes both the
replicated changes and the update of slave status. So we are crash safe
(assuming transactional support in all involved engines, of course).

> Oh, hmm, a problem.
> If you're in the middle of the already running transaction, you probably
> shouldn't commit it after your changes, but wait till the end -
> arbitrary adding commits in the middle of a transaction is no good.

So this is what I try to address with this code:

>>   ha_commit_trans(thd, FALSE);
>>   close_thread_tables(thd);
>>   if (!in_transaction)
>>     ha_commit_trans(thd, TRUE);

My intension was - first commit the "statement" (all=FALSE). Then, if I am
part of a larger transaction, do not commit the transaction, postpone that to
later. But if not part of a transaction, I have to commit it here.

But maybe I misunderstood? I thought a call of ha_commit_trans with all=FALSE
was done/needed after every statement.

Suppose we run this on the master:

    UPDATE t1 SET b=1 WHERE a=1;

This gets written to binlog as two statement events and one XID event:

    Statement: "BEGIN"
    Statement: "UPDATE t1 SET b=1 WHERE a=1"
    Xid-event (meaning commit)

So my thought was that the UPDATE does a ha_commit_trans(thd, FALSE).
In the Xid-event I inject a call to record_gtid, which updates the
mysql.rpl_slave_state table, and I therefore also need ha_commit_trans(thd,
FALSE). Then finally Xid-event does ha_commit_trans(thd, TRUE).

But maybe this is incorrect? I'm not sure ... maybe ha_commit_trans() is only
called at the end of the transaction?

 - Kristian.


Follow ups

References