ref_transaction_prepare(): new optional step for reference updates

In the future, compound reference stores will sometimes need to modify
references in two different reference stores at the same time, meaning
that a single logical reference transaction might have to be
implemented as two internal sub-transactions. They won't want to call
`ref_transaction_commit()` for the two sub-transactions one after the
other, because that wouldn't be atomic (the first commit could succeed
and the second one fail). Instead, they will want to prepare both
sub-transactions (i.e., obtain any necessary locks and do any
pre-checks), and only if both prepare steps succeed, then commit both
sub-transactions.

Start preparing for that day by adding a new, optional
`ref_transaction_prepare()` step to the reference transaction
sequence, which obtains the locks and does any prechecks, reporting
any errors that occur. Also add a `ref_transaction_abort()` function
that can be used to abort a sub-transaction even if it has already
been prepared.

That is on the side of the public-facing API. On the side of the
`ref_store` VTABLE, get rid of `transaction_commit` and instead add
methods `transaction_prepare`, `transaction_finish`, and
`transaction_abort`. A `ref_transaction_commit()` now basically calls
methods `transaction_prepare` then `transaction_finish`.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Michael Haggerty
2017-05-22 16:17:44 +02:00
committed by Junio C Hamano
parent 8d4240d3c8
commit 30173b8851
4 changed files with 250 additions and 50 deletions

View File

@ -185,17 +185,27 @@ struct ref_update *ref_transaction_add_update(
/*
* Transaction states.
* OPEN: The transaction is in a valid state and can accept new updates.
* An OPEN transaction can be committed.
* CLOSED: A closed transaction is no longer active and no other operations
* than free can be used on it in this state.
* A transaction can either become closed by successfully committing
* an active transaction or if there is a failure while building
* the transaction thus rendering it failed/inactive.
*
* OPEN: The transaction is initialized and new updates can still be
* added to it. An OPEN transaction can be prepared,
* committed, freed, or aborted (freeing and aborting an open
* transaction are equivalent).
*
* PREPARED: ref_transaction_prepare(), which locks all of the
* references involved in the update and checks that the
* update has no errors, has been called successfully for the
* transaction. A PREPARED transaction can be committed or
* aborted.
*
* CLOSED: The transaction is no longer active. A transaction becomes
* CLOSED if there is a failure while building the transaction
* or if a transaction is committed or aborted. A CLOSED
* transaction can only be freed.
*/
enum ref_transaction_state {
REF_TRANSACTION_OPEN = 0,
REF_TRANSACTION_CLOSED = 1
REF_TRANSACTION_OPEN = 0,
REF_TRANSACTION_PREPARED = 1,
REF_TRANSACTION_CLOSED = 2
};
/*
@ -497,6 +507,18 @@ typedef struct ref_store *ref_store_init_fn(const char *gitdir,
typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
typedef int ref_transaction_prepare_fn(struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
typedef int ref_transaction_finish_fn(struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
typedef int ref_transaction_abort_fn(struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
typedef int ref_transaction_commit_fn(struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
@ -600,7 +622,10 @@ struct ref_storage_be {
const char *name;
ref_store_init_fn *init;
ref_init_db_fn *init_db;
ref_transaction_commit_fn *transaction_commit;
ref_transaction_prepare_fn *transaction_prepare;
ref_transaction_finish_fn *transaction_finish;
ref_transaction_abort_fn *transaction_abort;
ref_transaction_commit_fn *initial_transaction_commit;
pack_refs_fn *pack_refs;