Skip to content

Commit 30173b8

Browse files
mhaggergitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 8d4240d commit 30173b8

File tree

4 files changed

+253
-53
lines changed

4 files changed

+253
-53
lines changed

refs.c

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,19 @@ void ref_transaction_free(struct ref_transaction *transaction)
853853
if (!transaction)
854854
return;
855855

856+
switch (transaction->state) {
857+
case REF_TRANSACTION_OPEN:
858+
case REF_TRANSACTION_CLOSED:
859+
/* OK */
860+
break;
861+
case REF_TRANSACTION_PREPARED:
862+
die("BUG: free called on a prepared reference transaction");
863+
break;
864+
default:
865+
die("BUG: unexpected reference transaction state");
866+
break;
867+
}
868+
856869
for (i = 0; i < transaction->nr; i++) {
857870
free(transaction->updates[i]->msg);
858871
free(transaction->updates[i]);
@@ -1689,15 +1702,18 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
16891702
refs_heads_master, logmsg);
16901703
}
16911704

1692-
int ref_transaction_commit(struct ref_transaction *transaction,
1693-
struct strbuf *err)
1705+
int ref_transaction_prepare(struct ref_transaction *transaction,
1706+
struct strbuf *err)
16941707
{
16951708
struct ref_store *refs = transaction->ref_store;
16961709

16971710
switch (transaction->state) {
16981711
case REF_TRANSACTION_OPEN:
16991712
/* Good. */
17001713
break;
1714+
case REF_TRANSACTION_PREPARED:
1715+
die("BUG: prepare called twice on reference transaction");
1716+
break;
17011717
case REF_TRANSACTION_CLOSED:
17021718
die("BUG: prepare called on a closed reference transaction");
17031719
break;
@@ -1712,7 +1728,59 @@ int ref_transaction_commit(struct ref_transaction *transaction,
17121728
return -1;
17131729
}
17141730

1715-
return refs->be->transaction_commit(refs, transaction, err);
1731+
return refs->be->transaction_prepare(refs, transaction, err);
1732+
}
1733+
1734+
int ref_transaction_abort(struct ref_transaction *transaction,
1735+
struct strbuf *err)
1736+
{
1737+
struct ref_store *refs = transaction->ref_store;
1738+
int ret = 0;
1739+
1740+
switch (transaction->state) {
1741+
case REF_TRANSACTION_OPEN:
1742+
/* No need to abort explicitly. */
1743+
break;
1744+
case REF_TRANSACTION_PREPARED:
1745+
ret = refs->be->transaction_abort(refs, transaction, err);
1746+
break;
1747+
case REF_TRANSACTION_CLOSED:
1748+
die("BUG: abort called on a closed reference transaction");
1749+
break;
1750+
default:
1751+
die("BUG: unexpected reference transaction state");
1752+
break;
1753+
}
1754+
1755+
ref_transaction_free(transaction);
1756+
return ret;
1757+
}
1758+
1759+
int ref_transaction_commit(struct ref_transaction *transaction,
1760+
struct strbuf *err)
1761+
{
1762+
struct ref_store *refs = transaction->ref_store;
1763+
int ret;
1764+
1765+
switch (transaction->state) {
1766+
case REF_TRANSACTION_OPEN:
1767+
/* Need to prepare first. */
1768+
ret = ref_transaction_prepare(transaction, err);
1769+
if (ret)
1770+
return ret;
1771+
break;
1772+
case REF_TRANSACTION_PREPARED:
1773+
/* Fall through to finish. */
1774+
break;
1775+
case REF_TRANSACTION_CLOSED:
1776+
die("BUG: commit called on a closed reference transaction");
1777+
break;
1778+
default:
1779+
die("BUG: unexpected reference transaction state");
1780+
break;
1781+
}
1782+
1783+
return refs->be->transaction_finish(refs, transaction, err);
17161784
}
17171785

17181786
int refs_verify_refname_available(struct ref_store *refs,

refs.h

Lines changed: 97 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -143,30 +143,71 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
143143
int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
144144

145145
/*
146-
* A ref_transaction represents a collection of ref updates
147-
* that should succeed or fail together.
146+
* A ref_transaction represents a collection of reference updates that
147+
* should succeed or fail together.
148148
*
149149
* Calling sequence
150150
* ----------------
151+
*
151152
* - Allocate and initialize a `struct ref_transaction` by calling
152153
* `ref_transaction_begin()`.
153154
*
154-
* - List intended ref updates by calling functions like
155-
* `ref_transaction_update()` and `ref_transaction_create()`.
156-
*
157-
* - Call `ref_transaction_commit()` to execute the transaction.
158-
* If this succeeds, the ref updates will have taken place and
159-
* the transaction cannot be rolled back.
160-
*
161-
* - Instead of `ref_transaction_commit`, use
162-
* `initial_ref_transaction_commit()` if the ref database is known
163-
* to be empty (e.g. during clone). This is likely to be much
164-
* faster.
165-
*
166-
* - At any time call `ref_transaction_free()` to discard the
167-
* transaction and free associated resources. In particular,
168-
* this rolls back the transaction if it has not been
169-
* successfully committed.
155+
* - Specify the intended ref updates by calling one or more of the
156+
* following functions:
157+
* - `ref_transaction_update()`
158+
* - `ref_transaction_create()`
159+
* - `ref_transaction_delete()`
160+
* - `ref_transaction_verify()`
161+
*
162+
* - Then either:
163+
*
164+
* - Optionally call `ref_transaction_prepare()` to prepare the
165+
* transaction. This locks all references, checks preconditions,
166+
* etc. but doesn't finalize anything. If this step fails, the
167+
* transaction has been closed and can only be freed. If this step
168+
* succeeds, then `ref_transaction_commit()` is almost certain to
169+
* succeed. However, you can still call `ref_transaction_abort()`
170+
* if you decide not to commit the transaction after all.
171+
*
172+
* - Call `ref_transaction_commit()` to execute the transaction,
173+
* make the changes permanent, and release all locks. If you
174+
* haven't already called `ref_transaction_prepare()`, then
175+
* `ref_transaction_commit()` calls it for you.
176+
*
177+
* Or
178+
*
179+
* - Call `initial_ref_transaction_commit()` if the ref database is
180+
* known to be empty and have no other writers (e.g. during
181+
* clone). This is likely to be much faster than
182+
* `ref_transaction_commit()`. `ref_transaction_prepare()` should
183+
* *not* be called before `initial_ref_transaction_commit()`.
184+
*
185+
* - Then finally, call `ref_transaction_free()` to free the
186+
* `ref_transaction` data structure.
187+
*
188+
* At any time before calling `ref_transaction_commit()`, you can call
189+
* `ref_transaction_abort()` to abort the transaction, rollback any
190+
* locks, and free any associated resources (including the
191+
* `ref_transaction` data structure).
192+
*
193+
* Putting it all together, a complete reference update looks like
194+
*
195+
* struct ref_transaction *transaction;
196+
* struct strbuf err = STRBUF_INIT;
197+
* int ret = 0;
198+
*
199+
* transaction = ref_store_transaction_begin(refs, &err);
200+
* if (!transaction ||
201+
* ref_transaction_update(...) ||
202+
* ref_transaction_create(...) ||
203+
* ...etc... ||
204+
* ref_transaction_commit(transaction, &err)) {
205+
* error("%s", err.buf);
206+
* ret = -1;
207+
* }
208+
* ref_transaction_free(transaction);
209+
* strbuf_release(&err);
210+
* return ret;
170211
*
171212
* Error handling
172213
* --------------
@@ -183,8 +224,9 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
183224
* -------
184225
*
185226
* Note that no locks are taken, and no refs are read, until
186-
* `ref_transaction_commit` is called. So `ref_transaction_verify`
187-
* won't report a verification failure until the commit is attempted.
227+
* `ref_transaction_prepare()` or `ref_transaction_commit()` is
228+
* called. So, for example, `ref_transaction_verify()` won't report a
229+
* verification failure until the commit is attempted.
188230
*/
189231
struct ref_transaction;
190232

@@ -523,19 +565,47 @@ int ref_transaction_verify(struct ref_transaction *transaction,
523565
unsigned int flags,
524566
struct strbuf *err);
525567

526-
/*
527-
* Commit all of the changes that have been queued in transaction, as
528-
* atomically as possible.
529-
*
530-
* Returns 0 for success, or one of the below error codes for errors.
531-
*/
532568
/* Naming conflict (for example, the ref names A and A/B conflict). */
533569
#define TRANSACTION_NAME_CONFLICT -1
534570
/* All other errors. */
535571
#define TRANSACTION_GENERIC_ERROR -2
572+
573+
/*
574+
* Perform the preparatory stages of commiting `transaction`. Acquire
575+
* any needed locks, check preconditions, etc.; basically, do as much
576+
* as possible to ensure that the transaction will be able to go
577+
* through, stopping just short of making any irrevocable or
578+
* user-visible changes. The updates that this function prepares can
579+
* be finished up by calling `ref_transaction_commit()` or rolled back
580+
* by calling `ref_transaction_abort()`.
581+
*
582+
* On success, return 0 and leave the transaction in "prepared" state.
583+
* On failure, abort the transaction, write an error message to `err`,
584+
* and return one of the `TRANSACTION_*` constants.
585+
*
586+
* Callers who don't need such fine-grained control over commiting
587+
* reference transactions should just call `ref_transaction_commit()`.
588+
*/
589+
int ref_transaction_prepare(struct ref_transaction *transaction,
590+
struct strbuf *err);
591+
592+
/*
593+
* Commit all of the changes that have been queued in transaction, as
594+
* atomically as possible. On success, return 0 and leave the
595+
* transaction in "closed" state. On failure, roll back the
596+
* transaction, write an error message to `err`, and return one of the
597+
* `TRANSACTION_*` constants
598+
*/
536599
int ref_transaction_commit(struct ref_transaction *transaction,
537600
struct strbuf *err);
538601

602+
/*
603+
* Abort `transaction`, which has been begun and possibly prepared,
604+
* but not yet committed.
605+
*/
606+
int ref_transaction_abort(struct ref_transaction *transaction,
607+
struct strbuf *err);
608+
539609
/*
540610
* Like ref_transaction_commit(), but optimized for creating
541611
* references when originally initializing a repository (e.g., by "git
@@ -551,7 +621,7 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
551621
struct strbuf *err);
552622

553623
/*
554-
* Free an existing transaction and all associated data.
624+
* Free `*transaction` and all associated data.
555625
*/
556626
void ref_transaction_free(struct ref_transaction *transaction);
557627

refs/files-backend.c

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,22 +2855,19 @@ static void files_transaction_cleanup(struct ref_transaction *transaction)
28552855
transaction->state = REF_TRANSACTION_CLOSED;
28562856
}
28572857

2858-
static int files_transaction_commit(struct ref_store *ref_store,
2859-
struct ref_transaction *transaction,
2860-
struct strbuf *err)
2858+
static int files_transaction_prepare(struct ref_store *ref_store,
2859+
struct ref_transaction *transaction,
2860+
struct strbuf *err)
28612861
{
28622862
struct files_ref_store *refs =
28632863
files_downcast(ref_store, REF_STORE_WRITE,
2864-
"ref_transaction_commit");
2864+
"ref_transaction_prepare");
28652865
size_t i;
28662866
int ret = 0;
2867-
struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
2868-
struct string_list_item *ref_to_delete;
28692867
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
28702868
char *head_ref = NULL;
28712869
int head_type;
28722870
struct object_id head_oid;
2873-
struct strbuf sb = STRBUF_INIT;
28742871

28752872
assert(err);
28762873

@@ -2934,14 +2931,47 @@ static int files_transaction_commit(struct ref_store *ref_store,
29342931
* that new values are valid, and write new values to the
29352932
* lockfiles, ready to be activated. Only keep one lockfile
29362933
* open at a time to avoid running out of file descriptors.
2934+
* Note that lock_ref_for_update() might append more updates
2935+
* to the transaction.
29372936
*/
29382937
for (i = 0; i < transaction->nr; i++) {
29392938
struct ref_update *update = transaction->updates[i];
29402939

29412940
ret = lock_ref_for_update(refs, update, transaction,
29422941
head_ref, &affected_refnames, err);
29432942
if (ret)
2944-
goto cleanup;
2943+
break;
2944+
}
2945+
2946+
cleanup:
2947+
free(head_ref);
2948+
string_list_clear(&affected_refnames, 0);
2949+
2950+
if (ret)
2951+
files_transaction_cleanup(transaction);
2952+
else
2953+
transaction->state = REF_TRANSACTION_PREPARED;
2954+
2955+
return ret;
2956+
}
2957+
2958+
static int files_transaction_finish(struct ref_store *ref_store,
2959+
struct ref_transaction *transaction,
2960+
struct strbuf *err)
2961+
{
2962+
struct files_ref_store *refs =
2963+
files_downcast(ref_store, 0, "ref_transaction_finish");
2964+
size_t i;
2965+
int ret = 0;
2966+
struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
2967+
struct string_list_item *ref_to_delete;
2968+
struct strbuf sb = STRBUF_INIT;
2969+
2970+
assert(err);
2971+
2972+
if (!transaction->nr) {
2973+
transaction->state = REF_TRANSACTION_CLOSED;
2974+
return 0;
29452975
}
29462976

29472977
/* Perform updates first so live commits remain referenced */
@@ -3022,7 +3052,6 @@ static int files_transaction_commit(struct ref_store *ref_store,
30223052

30233053
cleanup:
30243054
files_transaction_cleanup(transaction);
3025-
strbuf_release(&sb);
30263055

30273056
for (i = 0; i < transaction->nr; i++) {
30283057
struct ref_update *update = transaction->updates[i];
@@ -3039,13 +3068,19 @@ static int files_transaction_commit(struct ref_store *ref_store,
30393068
}
30403069
}
30413070

3071+
strbuf_release(&sb);
30423072
string_list_clear(&refs_to_delete, 0);
3043-
free(head_ref);
3044-
string_list_clear(&affected_refnames, 0);
3045-
30463073
return ret;
30473074
}
30483075

3076+
static int files_transaction_abort(struct ref_store *ref_store,
3077+
struct ref_transaction *transaction,
3078+
struct strbuf *err)
3079+
{
3080+
files_transaction_cleanup(transaction);
3081+
return 0;
3082+
}
3083+
30493084
static int ref_present(const char *refname,
30503085
const struct object_id *oid, int flags, void *cb_data)
30513086
{
@@ -3316,7 +3351,9 @@ struct ref_storage_be refs_be_files = {
33163351
"files",
33173352
files_ref_store_create,
33183353
files_init_db,
3319-
files_transaction_commit,
3354+
files_transaction_prepare,
3355+
files_transaction_finish,
3356+
files_transaction_abort,
33203357
files_initial_transaction_commit,
33213358

33223359
files_pack_refs,

0 commit comments

Comments
 (0)