From: Michael Haggerty <mhagger@alum.mit.edu>
To: Junio C Hamano <gitster@pobox.com>, Stefan Beller <sbeller@google.com>
Cc: Jeff King <peff@peff.net>,
git@vger.kernel.org, Michael Haggerty <mhagger@alum.mit.edu>
Subject: [PATCH 5/5] ref_transaction_commit(): only keep one lockfile open at a time
Date: Fri, 24 Apr 2015 13:35:49 +0200 [thread overview]
Message-ID: <1429875349-29736-6-git-send-email-mhagger@alum.mit.edu> (raw)
In-Reply-To: <1429875349-29736-1-git-send-email-mhagger@alum.mit.edu>
The old code locked all of the references in the first loop, leaving
all of the lockfiles open until later loops. Since we might be
updating a lot of references, this could cause file descriptor
exhaustion.
But there is no reason to keep the lockfiles open after the first
loop:
* For references being deleted or verified, we don't have to write
anything into the lockfile, so we can close it immediately.
* For references being updated, we can write the new SHA-1 into the
lockfile immediately and close the lockfile without renaming it into
place. In the second loop, the already-written contents can be
renamed into place using commit_ref_update().
To simplify the bookkeeping across loops, add a new REF_NEEDS_COMMIT
bit to update->flags, which keeps track of whether the corresponding
lockfile needs to be committed, as opposed to just unlocked.
This change fixes two tests in t1400.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
refs.c | 68 +++++++++++++++++++++++++++++++++++++--------------
t/t1400-update-ref.sh | 4 +--
2 files changed, 51 insertions(+), 21 deletions(-)
diff --git a/refs.c b/refs.c
index 782e777..2bdd93c 100644
--- a/refs.c
+++ b/refs.c
@@ -36,25 +36,31 @@ static unsigned char refname_disposition[256] = {
* Flag passed to lock_ref_sha1_basic() telling it to tolerate broken
* refs (i.e., because the reference is about to be deleted anyway).
*/
-#define REF_DELETING 0x02
+#define REF_DELETING 0x02
/*
* Used as a flag in ref_update::flags when a loose ref is being
* pruned.
*/
-#define REF_ISPRUNING 0x04
+#define REF_ISPRUNING 0x04
/*
* Used as a flag in ref_update::flags when the reference should be
* updated to new_sha1.
*/
-#define REF_HAVE_NEW 0x08
+#define REF_HAVE_NEW 0x08
/*
* Used as a flag in ref_update::flags when old_sha1 should be
* checked.
*/
-#define REF_HAVE_OLD 0x10
+#define REF_HAVE_OLD 0x10
+
+/*
+ * Used as a flag in ref_update::flags when the lockfile needs to be
+ * committed.
+ */
+#define REF_NEEDS_COMMIT 0x20
/*
* Try to read one refname component from the front of refname.
@@ -3749,7 +3755,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
goto cleanup;
}
- /* Acquire all locks while verifying old values */
+ /*
+ * Acquire all locks, verify old values if provided, check
+ * that new values are valid, and write new values to the
+ * lockfiles, ready to be activated. Only keep one lockfile
+ * open at a time to avoid running out of file descriptors.
+ */
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
@@ -3770,13 +3781,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
update->refname);
goto cleanup;
}
- }
-
- /* Perform updates first so live commits remain referenced */
- for (i = 0; i < n; i++) {
- struct ref_update *update = updates[i];
-
- if ((update->flags & REF_HAVE_NEW) && !is_null_sha1(update->new_sha1)) {
+ if ((update->flags & REF_HAVE_NEW) && !(update->flags & REF_DELETING)) {
int overwriting_symref = ((update->type & REF_ISSYMREF) &&
(update->flags & REF_NODEREF));
@@ -3786,14 +3791,39 @@ int ref_transaction_commit(struct ref_transaction *transaction,
* The reference already has the desired
* value, so we don't need to write it.
*/
- unlock_ref(update->lock);
- update->lock = NULL;
} else if (write_ref_to_lockfile(update->lock,
- update->new_sha1) ||
- commit_ref_update(update->lock,
- update->new_sha1,
- update->msg)) {
- update->lock = NULL; /* freed by the above calls */
+ update->new_sha1)) {
+ update->lock = NULL; /* freed by the above call */
+ strbuf_addf(err, "Cannot update the ref '%s'.",
+ update->refname);
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ } else {
+ update->flags |= REF_NEEDS_COMMIT;
+ }
+ }
+ if (!(update->flags & REF_NEEDS_COMMIT)) {
+ /*
+ * We didn't have to write anything to the lockfile.
+ * Close it to free up the file descriptor:
+ */
+ if (close_ref(update->lock)) {
+ strbuf_addf(err, "Couldn't close %s.lock",
+ update->refname);
+ goto cleanup;
+ }
+ }
+ }
+
+ /* Perform updates first so live commits remain referenced */
+ for (i = 0; i < n; i++) {
+ struct ref_update *update = updates[i];
+
+ if (update->flags & REF_NEEDS_COMMIT) {
+ if (commit_ref_update(update->lock,
+ update->new_sha1,
+ update->msg)) {
+ update->lock = NULL; /* freed by the above call */
strbuf_addf(err, "Cannot update the ref '%s'.",
update->refname);
ret = TRANSACTION_GENERIC_ERROR;
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 7a69f1a..636d3a1 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -1071,7 +1071,7 @@ run_with_limited_open_files () {
test_lazy_prereq ULIMIT_FILE_DESCRIPTORS 'run_with_limited_open_files true'
-test_expect_failure ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches does not burst open file limit' '
+test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches does not burst open file limit' '
(
for i in $(test_seq 33)
do
@@ -1082,7 +1082,7 @@ test_expect_failure ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches
)
'
-test_expect_failure ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches does not burst open file limit' '
+test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches does not burst open file limit' '
(
for i in $(test_seq 33)
do
--
2.1.4
next prev parent reply other threads:[~2015-04-24 11:36 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-24 11:35 [PATCH 0/5] Avoid file descriptor exhaustion in ref_transaction_commit() Michael Haggerty
2015-04-24 11:35 ` [PATCH 1/5] write_ref_to_lockfile(): new function, extracted from write_ref_sha1() Michael Haggerty
2015-04-24 11:35 ` [PATCH 2/5] commit_ref_update(): " Michael Haggerty
2015-04-24 11:35 ` [PATCH 3/5] write_ref_sha1(): inline function at callers Michael Haggerty
2015-04-24 11:35 ` [PATCH 4/5] ref_transaction_commit(): remove the local flags variables Michael Haggerty
2015-04-24 17:30 ` Jeff King
2015-04-24 21:15 ` Michael Haggerty
2015-04-24 21:19 ` Jeff King
2015-04-24 21:51 ` Eric Sunshine
2015-04-24 22:22 ` Michael Haggerty
2015-04-24 11:35 ` Michael Haggerty [this message]
2015-04-25 6:08 ` [PATCH 5/5] ref_transaction_commit(): only keep one lockfile open at a time Michael Haggerty
2015-04-25 6:57 ` Junio C Hamano
2015-04-25 19:21 ` Junio C Hamano
2015-04-28 4:36 ` Jeff King
2015-04-24 17:26 ` [PATCH 0/5] Avoid file descriptor exhaustion in ref_transaction_commit() Jeff King
2015-04-24 19:13 ` Stefan Beller
2015-04-25 19:27 ` Junio C Hamano
2015-04-25 4:28 ` Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1429875349-29736-6-git-send-email-mhagger@alum.mit.edu \
--to=mhagger@alum.mit.edu \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=peff@peff.net \
--cc=sbeller@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).