git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v4 00/10] refs/reftable: reuse iterators when reading refs

From
Patrick Steinhardt <ps@pks.im>
Date
Nov 26, 2024, 06:42 UTC
Message-ID
<20241126-pks-reftable-backend-reuse-iter-v4-0-b17fd27df126@pks.im>
In-Reply-To
<cover.1730732881.git.ps@pks.im>
Hi,

this patch series refactors the reftable backend to reuse reftable iterators when reading random references. This removes the overhead of having to recreate the iterator on every read and thus leads to better performance and less allocation churn. It also gives us the ability to further optimize reads by optimizing re-seeking iterators in the future.

Overall this leads to a 7% speedup when creating many refs in a transaction, which performs many random reads. But this change also positively impacts other usecases.

Changes in v3:
  - Adapt some comments to refer to the "backend" instead of to the
    "stack".
  - Fix indentation of a statement while at it.
  - Explain why callsites don't want to reload the stack.
  - Optimize `prepare_transaction_update()` by not using `backend_for()`
    twice, but instead reload the stack manually.
  - Split out the change that adds `reftable_stack_hash_id()` into a
    separate commit.
  - Link to v2: https://lore.kernel.org/r/cover.1730792627.git.ps@pks.im
Changes in v4:
  - Split up the introduction of `reftable_backend_read_ref()` into two
    commits: one that gets rid of the `struct reftable_ref_store`
    parameter and one that converts the function to accept a `struct
    reftable_backend`.
  - Fix a comment typo.
  - Link to v3: https://lore.kernel.org/r/20241125-pks-reftable-backend-reuse-iter-v3-0-1d7b658e3e9e@pks.im
Thanks!
Patrick
---
Patrick Steinhardt (10):
      refs/reftable: encapsulate reftable stack
      refs/reftable: handle reloading stacks in the reftable backend
      reftable/stack: add accessor for the hash ID
      refs/reftable: figure out hash via `reftable_stack`
      refs/reftable: read references via `struct reftable_backend`
      refs/reftable: refactor reading symbolic refs to use reftable backend
      refs/reftable: refactor reflog expiry to use reftable backend
      reftable/stack: add mechanism to notify callers on reload
      reftable/merged: drain priority queue on reseek
      refs/reftable: reuse iterators when reading refs
 refs/reftable-backend.c          | 409 +++++++++++++++++++++++++--------------
 reftable/merged.c                |   2 +
 reftable/reftable-stack.h        |   3 +
 reftable/reftable-writer.h       |   9 +
 reftable/stack.c                 |   9 +
 t/unit-tests/t-reftable-merged.c |  73 +++++++
 6 files changed, 357 insertions(+), 148 deletions(-)
Range-diff versus v3:
 1:  3db0ba3eb5 =  1:  ec0b7e35c8 refs/reftable: encapsulate reftable stack
 2:  556eb8301c !  2:  437304908b refs/reftable: handle reloading stacks in the reftable backend
    @@ refs/reftable-backend.c: static int reftable_be_transaction_prepare(struct ref_s
     +	 * have reloaded it, which may mean that it is stale.
     +	 *
     +	 * On the other hand, reloading that stack without locking it feels
    -+	 * wrong to, as the value of "HEAD" could be modified concurrently at
    ++	 * wrong, too, as the value of "HEAD" could be modified concurrently at
     +	 * any point in time.
     +	 */
     +	ret = backend_for(&be, refs, "HEAD", NULL, 0);
 3:  e966b9acf2 =  3:  0bd9aea884 reftable/stack: add accessor for the hash ID
 -:  ---------- >  4:  d8b801503f refs/reftable: figure out hash via `reftable_stack`
 4:  de9f52a5b2 !  5:  a57210ac46 refs/reftable: read references via `struct reftable_backend`
    @@ Metadata
      ## Commit message ##
         refs/reftable: read references via `struct reftable_backend`
     
    -    Refactor `read_ref_without_reload()` to accept a `struct reftable_stack`
    -    as input instead of accepting a `struct reftable_stack`. This allows us
    -    to implement an additional caching layer when reading refs where we can
    -    reuse reftable iterators.
    +    Refactor `read_ref_without_reload()` to accept `struct reftable_backend`
    +    as parameter instead of `struct reftable_stack`. Rename the function to
    +    `reftable_backend_read_ref()` to clarify its scope and move it close to
    +    other functions operating on `struct reftable_backend`.
    +
    +    This change allows us to implement an additional caching layer when
    +    reading refs where we can reuse reftable iterators.
     
         Signed-off-by: Patrick Steinhardt <ps@pks.im>
     
    @@ refs/reftable-backend.c: static void fill_reftable_log_record(struct reftable_lo
      	log->value.update.tz_offset = sign * atoi(tz_begin);
      }
      
    --static int read_ref_without_reload(struct reftable_ref_store *refs,
    --				   struct reftable_stack *stack,
    +-static int read_ref_without_reload(struct reftable_stack *stack,
     -				   const char *refname,
     -				   struct object_id *oid,
     -				   struct strbuf *referent,
    @@ refs/reftable-backend.c: static void fill_reftable_log_record(struct reftable_lo
     -		strbuf_addstr(referent, ref.value.symref);
     -		*type |= REF_ISSYMREF;
     -	} else if (reftable_ref_record_val1(&ref)) {
    +-		unsigned int hash_id;
    +-
    +-		switch (reftable_stack_hash_id(stack)) {
    +-		case REFTABLE_HASH_SHA1:
    +-			hash_id = GIT_HASH_SHA1;
    +-			break;
    +-		case REFTABLE_HASH_SHA256:
    +-			hash_id = GIT_HASH_SHA256;
    +-			break;
    +-		default:
    +-			BUG("unhandled hash ID %d", reftable_stack_hash_id(stack));
    +-		}
    +-
     -		oidread(oid, reftable_ref_record_val1(&ref),
    --			refs->base.repo->hash_algo);
    +-			&hash_algos[hash_id]);
     -	} else {
     -		/* We got a tombstone, which should not happen. */
     -		BUG("unhandled reference value type %d", ref.value_type);
    @@ refs/reftable-backend.c: static int reftable_be_read_raw_ref(struct ref_store *r
      	if (ret)
      		return ret;
      
    --	ret = read_ref_without_reload(refs, be->stack, refname, oid, referent, type);
    +-	ret = read_ref_without_reload(be->stack, refname, oid, referent, type);
     +	ret = reftable_backend_read_ref(be, refname, oid, referent, type);
      	if (ret < 0)
      		return ret;
    @@ refs/reftable-backend.c: static int reftable_be_transaction_prepare(struct ref_s
      	if (ret)
      		goto done;
      
    --	ret = read_ref_without_reload(refs, be->stack, "HEAD",
    +-	ret = read_ref_without_reload(be->stack, "HEAD",
     -				      &head_oid, &head_referent, &head_type);
     +	ret = reftable_backend_read_ref(be, "HEAD", &head_oid,
     +					&head_referent, &head_type);
    @@ refs/reftable-backend.c: static int reftable_be_transaction_prepare(struct ref_s
      			string_list_insert(&affected_refnames, new_update->refname);
      		}
      
    --		ret = read_ref_without_reload(refs, be->stack, rewritten_ref,
    +-		ret = read_ref_without_reload(be->stack, rewritten_ref,
     -					      &current_oid, &referent, &u->type);
     +		ret = reftable_backend_read_ref(be, rewritten_ref,
     +						&current_oid, &referent, &u->type);
    @@ refs/reftable-backend.c: static int write_copy_table(struct reftable_writer *wri
      		memcpy(logs[logs_nr].value.update.old_hash, old_ref.value.val1, GIT_MAX_RAWSZ);
      		logs_nr++;
      
    --		ret = read_ref_without_reload(arg->refs, arg->stack, "HEAD", &head_oid,
    +-		ret = read_ref_without_reload(arg->stack, "HEAD", &head_oid,
     -					      &head_referent, &head_type);
     +		ret = reftable_backend_read_ref(arg->be, "HEAD", &head_oid,
     +						&head_referent, &head_type);
 5:  6e7db8e3fc =  6:  432f75ef01 refs/reftable: refactor reading symbolic refs to use reftable backend
 6:  687afb781a =  7:  1ca28e00b1 refs/reftable: refactor reflog expiry to use reftable backend
 7:  e1d4bdc3e8 =  8:  a95b67f1f3 reftable/stack: add mechanism to notify callers on reload
 8:  8eccf308bd =  9:  cbaf95ff15 reftable/merged: drain priority queue on reseek
 9:  942957447a = 10:  013f05e08b refs/reftable: reuse iterators when reading refs

--- base-commit: 455ddbf8c6a694968c1089fb6c7ffb1d31d97e9d change-id: 20241125-pks-reftable-backend-reuse-iter-3a2e92428789

Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 47 of 57 in “refs/reftable: reuse iterators when reading refs”
  1. 0/8 refs/reftable: reuse iterators when reading refsPatrick Steinhardt, Nov 4, 2024
  2. 1/8 refs/reftable: encapsulate reftable stackPatrick Steinhardt, Nov 4, 2024
  3. karthik nayakNov 5, 2024
  4. 2/8 refs/reftable: handle reloading stacks in the reftable backendPatrick Steinhardt, Nov 4, 2024
  5. karthik nayakNov 5, 2024
  6. Patrick SteinhardtNov 6, 2024
  7. 3/8 refs/reftable: read references via `struct reftable_backend`Patrick Steinhardt, Nov 4, 2024
  8. karthik nayakNov 5, 2024
  9. 4/8 refs/reftable: refactor reading symbolic refs to use reftable backendPatrick Steinhardt, Nov 4, 2024
  10. 5/8 refs/reftable: refactor reflog expiry to use reftable backendPatrick Steinhardt, Nov 4, 2024
  11. 6/8 reftable/stack: add mechanism to notify callers on reloadPatrick Steinhardt, Nov 4, 2024
  12. 7/8 reftable/merged: drain priority queue on reseekPatrick Steinhardt, Nov 4, 2024
  13. Junio C HamanoNov 5, 2024
  14. Junio C HamanoNov 5, 2024
  15. Patrick SteinhardtNov 5, 2024
  16. 8/8 refs/reftable: reuse iterators when reading refsPatrick Steinhardt, Nov 4, 2024
  17. Junio C HamanoNov 5, 2024
  18. 0/8 refs/reftable: reuse iterators when reading refsPatrick Steinhardt, Nov 5, 2024
  19. 1/8 refs/reftable: encapsulate reftable stackPatrick Steinhardt, Nov 5, 2024
  20. Junio C HamanoNov 12, 2024
  21. 2/8 refs/reftable: handle reloading stacks in the reftable backendPatrick Steinhardt, Nov 5, 2024
  22. Junio C HamanoNov 12, 2024
  23. Patrick SteinhardtNov 12, 2024
  24. 3/8 refs/reftable: read references via `struct reftable_backend`Patrick Steinhardt, Nov 5, 2024
  25. Junio C HamanoNov 12, 2024
  26. Patrick SteinhardtNov 12, 2024
  27. 4/8 refs/reftable: refactor reading symbolic refs to use reftable backendPatrick Steinhardt, Nov 5, 2024
  28. 5/8 refs/reftable: refactor reflog expiry to use reftable backendPatrick Steinhardt, Nov 5, 2024
  29. 6/8 reftable/stack: add mechanism to notify callers on reloadPatrick Steinhardt, Nov 5, 2024
  30. 7/8 reftable/merged: drain priority queue on reseekPatrick Steinhardt, Nov 5, 2024
  31. 8/8 refs/reftable: reuse iterators when reading refsPatrick Steinhardt, Nov 5, 2024
  32. 0/9 refs/reftable: reuse iterators when reading refsPatrick Steinhardt, Nov 25, 2024
  33. 1/9 refs/reftable: encapsulate reftable stackPatrick Steinhardt, Nov 25, 2024
  34. 2/9 refs/reftable: handle reloading stacks in the reftable backendPatrick Steinhardt, Nov 25, 2024
  35. Junio C HamanoNov 26, 2024
  36. 3/9 reftable/stack: add accessor for the hash IDPatrick Steinhardt, Nov 25, 2024
  37. 4/9 refs/reftable: read references via `struct reftable_backend`Patrick Steinhardt, Nov 25, 2024
  38. Junio C HamanoNov 26, 2024
  39. Patrick SteinhardtNov 26, 2024
  40. 5/9 refs/reftable: refactor reading symbolic refs to use reftable backendPatrick Steinhardt, Nov 25, 2024
  41. 6/9 refs/reftable: refactor reflog expiry to use reftable backendPatrick Steinhardt, Nov 25, 2024
  42. 7/9 reftable/stack: add mechanism to notify callers on reloadPatrick Steinhardt, Nov 25, 2024
  43. 8/9 reftable/merged: drain priority queue on reseekPatrick Steinhardt, Nov 25, 2024
  44. 9/9 refs/reftable: reuse iterators when reading refsPatrick Steinhardt, Nov 25, 2024
  45. Christian CouderNov 25, 2024
  46. Patrick SteinhardtNov 25, 2024
  47. 00/10 refs/reftable: reuse iterators when reading refsPatrick Steinhardt, Nov 26, 2024
  48. 01/10 refs/reftable: encapsulate reftable stackPatrick Steinhardt, Nov 26, 2024
  49. 03/10 reftable/stack: add accessor for the hash IDPatrick Steinhardt, Nov 26, 2024
  50. 02/10 refs/reftable: handle reloading stacks in the reftable backendPatrick Steinhardt, Nov 26, 2024
  51. 05/10 refs/reftable: read references via `struct reftable_backend`Patrick Steinhardt, Nov 26, 2024
  52. 04/10 refs/reftable: figure out hash via `reftable_stack`Patrick Steinhardt, Nov 26, 2024
  53. 06/10 refs/reftable: refactor reading symbolic refs to use reftable backendPatrick Steinhardt, Nov 26, 2024
  54. 07/10 refs/reftable: refactor reflog expiry to use reftable backendPatrick Steinhardt, Nov 26, 2024
  55. 08/10 reftable/stack: add mechanism to notify callers on reloadPatrick Steinhardt, Nov 26, 2024
  56. 09/10 reftable/merged: drain priority queue on reseekPatrick Steinhardt, Nov 26, 2024
  57. 10/10 refs/reftable: reuse iterators when reading refsPatrick Steinhardt, Nov 26, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.