bulk-checkin: remove ODB transaction nesting

ODB transactions support being nested. Only the outermost
{begin,end}_odb_transaction() start and finish a transaction. This
allows internal object write codepaths to be optimized with ODB
transactions without worrying about whether a transaction is already
active. When {begin,end}_odb_transaction() is invoked during an active
transaction, these operations are essentially treated as no-ops. This
can make the interface a bit awkward to use, as calling
end_odb_transaction() does not guarantee that a transaction is actually
ended. Thus, in situations where a transaction needs to be explicitly
flushed, flush_odb_transaction() must be used.

To remove the need for an explicit transaction flush operation via
flush_odb_transaction() and better clarify transaction semantics, drop
the transaction nesting mechanism in favor of begin_odb_transaction()
returning a NULL transaction value to signal it was a no-op, and
end_odb_transaction() behaving as a no-op when a NULL transaction value
is passed. This is safe for existing callers as the transaction value
wired to end_odb_transaction() already comes from
begin_odb_transaction() and thus continues the same no-op behavior when
a transaction is already pending. With this model, passing a pending
transaction to end_odb_transaction() ensures it is committed at that
point in time.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Justin Tobler
2025-09-16 13:29:33 -05:00
committed by Junio C Hamano
parent 4a3422b161
commit f3c1db4b2a
3 changed files with 14 additions and 18 deletions

View File

@@ -33,7 +33,6 @@ struct bulk_checkin_packfile {
struct odb_transaction { struct odb_transaction {
struct object_database *odb; struct object_database *odb;
int nesting;
struct tmp_objdir *objdir; struct tmp_objdir *objdir;
struct bulk_checkin_packfile packfile; struct bulk_checkin_packfile packfile;
}; };
@@ -368,12 +367,11 @@ void fsync_loose_object_bulk_checkin(struct odb_transaction *transaction,
struct odb_transaction *begin_odb_transaction(struct object_database *odb) struct odb_transaction *begin_odb_transaction(struct object_database *odb)
{ {
if (!odb->transaction) { if (odb->transaction)
CALLOC_ARRAY(odb->transaction, 1); return NULL;
odb->transaction->odb = odb;
}
odb->transaction->nesting += 1; CALLOC_ARRAY(odb->transaction, 1);
odb->transaction->odb = odb;
return odb->transaction; return odb->transaction;
} }
@@ -389,14 +387,14 @@ void flush_odb_transaction(struct odb_transaction *transaction)
void end_odb_transaction(struct odb_transaction *transaction) void end_odb_transaction(struct odb_transaction *transaction)
{ {
if (!transaction || transaction->nesting == 0) if (!transaction)
BUG("Unbalanced ODB transaction nesting");
transaction->nesting -= 1;
if (transaction->nesting)
return; return;
/*
* Ensure the transaction ending matches the pending transaction.
*/
ASSERT(transaction == transaction->odb->transaction);
flush_odb_transaction(transaction); flush_odb_transaction(transaction);
transaction->odb->transaction = NULL; transaction->odb->transaction = NULL;
free(transaction); free(transaction);

View File

@@ -38,9 +38,8 @@ int index_blob_bulk_checkin(struct odb_transaction *transaction,
/* /*
* Tell the object database to optimize for adding * Tell the object database to optimize for adding
* multiple objects. end_odb_transaction must be called * multiple objects. end_odb_transaction must be called
* to make new objects visible. Transactions can be nested, * to make new objects visible. If a transaction is already
* and objects are only visible after the outermost transaction * pending, NULL is returned.
* is complete or the transaction is flushed.
*/ */
struct odb_transaction *begin_odb_transaction(struct object_database *odb); struct odb_transaction *begin_odb_transaction(struct object_database *odb);
@@ -53,8 +52,7 @@ void flush_odb_transaction(struct odb_transaction *transaction);
/* /*
* Tell the object database to make any objects from the * Tell the object database to make any objects from the
* current transaction visible if this is the final nested * current transaction visible.
* transaction.
*/ */
void end_odb_transaction(struct odb_transaction *transaction); void end_odb_transaction(struct odb_transaction *transaction);

View File

@@ -1267,7 +1267,7 @@ int index_fd(struct index_state *istate, struct object_id *oid,
struct odb_transaction *transaction; struct odb_transaction *transaction;
transaction = begin_odb_transaction(the_repository->objects); transaction = begin_odb_transaction(the_repository->objects);
ret = index_blob_bulk_checkin(transaction, ret = index_blob_bulk_checkin(the_repository->objects->transaction,
oid, fd, xsize_t(st->st_size), oid, fd, xsize_t(st->st_size),
path, flags); path, flags);
end_odb_transaction(transaction); end_odb_transaction(transaction);