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

Re: [PATCH v3 03/17] commit-graph: ensure Bloom filters are read with consistent settings

From
Patrick Steinhardt <ps@pks.im>
Date
Oct 17, 2023, 08:45 UTC
Message-ID
<ZS5JkyBuFzW09LXH@tanuki>
In-Reply-To
<2ecc0a2d58432b149d73a3e2abfa948eb1f0aa0b.1696969994.git.me@ttaylorr.com>
On Tue, Oct 10, 2023 at 04:33:26PM -0400, Taylor Blau wrote:
Show 157 quoted lines
> The changed-path Bloom filter mechanism is parameterized by a couple of
> variables, notably the number of bits per hash (typically "m" in Bloom
> filter literature) and the number of hashes themselves (typically "k").
> 
> It is critically important that filters are read with the Bloom filter
> settings that they were written with. Failing to do so would mean that
> each query is liable to compute different fingerprints, meaning that the
> filter itself could return a false negative. This goes against a basic
> assumption of using Bloom filters (that they may return false positives,
> but never false negatives) and can lead to incorrect results.
> 
> We have some existing logic to carry forward existing Bloom filter
> settings from one layer to the next. In `write_commit_graph()`, we have
> something like:
> 
>     if (!(flags & COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS)) {
>         struct commit_graph *g = ctx->r->objects->commit_graph;
> 
>         /* We have changed-paths already. Keep them in the next graph */
>         if (g && g->chunk_bloom_data) {
>             ctx->changed_paths = 1;
>             ctx->bloom_settings = g->bloom_filter_settings;
>         }
>     }
> 
> , which drags forward Bloom filter settings across adjacent layers.
> 
> This doesn't quite address all cases, however, since it is possible for
> intermediate layers to contain no Bloom filters at all. For example,
> suppose we have two layers in a commit-graph chain, say, {G1, G2}. If G1
> contains Bloom filters, but G2 doesn't, a new G3 (whose base graph is
> G2) may be written with arbitrary Bloom filter settings, because we only
> check the immediately adjacent layer's settings for compatibility.
> 
> This behavior has existed since the introduction of changed-path Bloom
> filters. But in practice, this is not such a big deal, since the only
> way up until this point to modify the Bloom filter settings at write
> time is with the undocumented environment variables:
> 
>   - GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY
>   - GIT_TEST_BLOOM_SETTINGS_NUM_HASHES
>   - GIT_TEST_BLOOM_SETTINGS_MAX_CHANGED_PATHS
> 
> (it is still possible to tweak MAX_CHANGED_PATHS between layers, but
> this does not affect reads, so is allowed to differ across multiple
> graph layers).
> 
> But in future commits, we will introduce another parameter to change the
> hash algorithm used to compute Bloom fingerprints itself. This will be
> exposed via a configuration setting, making this foot-gun easier to use.
> 
> To prevent this potential issue, validate that all layers of a split
> commit-graph have compatible settings with the newest layer which
> contains Bloom filters.
> 
> Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
> Original-test-by: SZEDER Gábor <szeder.dev@gmail.com>
> Signed-off-by: Taylor Blau <me@ttaylorr.com>
> ---
>  commit-graph.c       | 25 +++++++++++++++++
>  t/t4216-log-bloom.sh | 64 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 89 insertions(+)
> 
> diff --git a/commit-graph.c b/commit-graph.c
> index 1a56efcf69..ae0902f7f4 100644
> --- a/commit-graph.c
> +++ b/commit-graph.c
> @@ -498,6 +498,30 @@ static int validate_mixed_generation_chain(struct commit_graph *g)
>  	return 0;
>  }
>  
> +static void validate_mixed_bloom_settings(struct commit_graph *g)
> +{
> +	struct bloom_filter_settings *settings = NULL;
> +	for (; g; g = g->base_graph) {
> +		if (!g->bloom_filter_settings)
> +			continue;
> +		if (!settings) {
> +			settings = g->bloom_filter_settings;
> +			continue;
> +		}
> +
> +		if (g->bloom_filter_settings->bits_per_entry != settings->bits_per_entry ||
> +		    g->bloom_filter_settings->num_hashes != settings->num_hashes) {
> +			g->chunk_bloom_indexes = NULL;
> +			g->chunk_bloom_data = NULL;
> +			FREE_AND_NULL(g->bloom_filter_settings);
> +
> +			warning(_("disabling Bloom filters for commit-graph "
> +				  "layer '%s' due to incompatible settings"),
> +				oid_to_hex(&g->oid));
> +		}
> +	}
> +}
> +
>  static int add_graph_to_chain(struct commit_graph *g,
>  			      struct commit_graph *chain,
>  			      struct object_id *oids,
> @@ -614,6 +638,7 @@ struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r,
>  	}
>  
>  	validate_mixed_generation_chain(graph_chain);
> +	validate_mixed_bloom_settings(graph_chain);
>  
>  	free(oids);
>  	fclose(fp);
> diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh
> index 322640feeb..f49a8f2fbf 100755
> --- a/t/t4216-log-bloom.sh
> +++ b/t/t4216-log-bloom.sh
> @@ -420,4 +420,68 @@ test_expect_success 'Bloom generation backfills empty commits' '
>  	)
>  '
>  
> +graph=.git/objects/info/commit-graph
> +graphdir=.git/objects/info/commit-graphs
> +chain=$graphdir/commit-graph-chain
> +
> +test_expect_success 'setup for mixed Bloom setting tests' '
> +	repo=mixed-bloom-settings &&
> +
> +	git init $repo &&
> +	for i in one two three
> +	do
> +		test_commit -C $repo $i file || return 1
> +	done
> +'
> +
> +test_expect_success 'split' '
> +	# Compute Bloom filters with "unusual" settings.
> +	git -C $repo rev-parse one >in &&
> +	GIT_TEST_BLOOM_SETTINGS_NUM_HASHES=3 git -C $repo commit-graph write \
> +		--stdin-commits --changed-paths --split <in &&
> +	layer=$(head -n 1 $repo/$chain) &&
> +
> +	# A commit-graph layer without Bloom filters "hides" the layers
> +	# below ...
> +	git -C $repo rev-parse two >in &&
> +	git -C $repo commit-graph write --stdin-commits --no-changed-paths \
> +		--split=no-merge <in &&
> +
> +	# Another commit-graph layer that has Bloom filters, but with
> +	# standard settings, and is thus incompatible with the base
> +	# layer written above.
> +	git -C $repo rev-parse HEAD >in &&
> +	git -C $repo commit-graph write --stdin-commits --changed-paths \
> +		--split=no-merge <in &&
> +
> +	test_line_count = 3 $repo/$chain &&
> +
> +	# Ensure that incompatible Bloom filters are ignored.
> +	git -C $repo -c core.commitGraph=false log --oneline --no-decorate -- file \
> +		>expect 2>err &&
> +	git -C $repo log --oneline --no-decorate -- file >actual 2>err &&
> +	test_cmp expect actual &&
> +	grep "disabling Bloom filters for commit-graph layer .$layer." err
> +'
Up to this point everything looks sensible to me.
Show 14 quoted lines
> +test_expect_success 'merge graph layers with incompatible Bloom settings' '
> +	# Ensure that incompatible Bloom filters are ignored when
> +	# generating new layers.
> +	git -C $repo commit-graph write --reachable --changed-paths 2>err &&
> +	grep "disabling Bloom filters for commit-graph layer .$layer." err &&
> +
> +	test_path_is_file $repo/$graph &&
> +	test_dir_is_empty $repo/$graphdir &&
> +
> +	# ...and merging existing ones.
> +	git -C $repo -c core.commitGraph=false log --oneline --no-decorate -- file \
> +		>expect 2>err &&
> +	GIT_TRACE2_PERF="$(pwd)/trace.perf" \
> +		git -C $repo log --oneline --no-decorate -- file >actual 2>err &&

But this test is a bit confusing to me, to be honest, also because the comment for the second block here reads funny. We don't really merge anything, do we? We only generate logs and compare that the log with and without the resulting merged commit graph is the same. The actual logic happened before.

> +	test_cmp expect actual && cat err &&
The `cat err` looks like a leftover from debugging.
> +	grep "statistics:{\"filter_not_present\":0" trace.perf &&

Also, why should the filter not be present here? If we merge the commit-graphs with `--changed-paths` I'd have expected that we either carry over bloom filters from preexisting commit graphs if compatible, or otherwise generate them if they are either incompatible or don't exist.

I feel like I'm missing something obvious, so this may be me just missing the bigger picture.

> +	! grep "disabling Bloom filters" err

Can we make this assertion stricter and verify that `err` is empty? I always think that `! grep` is quite a fragile pattern as it is quite prone to becoming stale, e.g. when the error message itself would change.

Patrick
Show 6 quoted lines
> +'
> +
>  test_done
> -- 
> 2.42.0.342.g8bb3a896ee
> 
Previous: Taylor BlauNext: Taylor Blau
Message 57 of 76 in “bloom: changed-path Bloom filters v2”
  1. 00/15 bloom: changed-path Bloom filters v2Taylor Blau, Aug 21, 2023
  2. 01/15 gitformat-commit-graph: describe version 2 of BDATTaylor Blau, Aug 21, 2023
  3. 02/15 t/helper/test-read-graph.c: extract `dump_graph_info()`Taylor Blau, Aug 21, 2023
  4. 03/15 bloom.h: make `load_bloom_filter_from_graph()` publicTaylor Blau, Aug 21, 2023
  5. 04/15 t/helper/test-read-graph: implement `bloom-filters` modeTaylor Blau, Aug 21, 2023
  6. 05/15 t4216: test changed path filters with high bit pathsTaylor Blau, Aug 21, 2023
  7. 06/15 repo-settings: introduce commitgraph.changedPathsVersionTaylor Blau, Aug 21, 2023
  8. 07/15 commit-graph: new filter ver. that fixes murmur3Taylor Blau, Aug 21, 2023
  9. SZEDER GáborAug 26, 2023
  10. Jonathan TanAug 29, 2023
  11. SZEDER GáborAug 30, 2023
  12. Jonathan TanSep 1, 2023
  13. Taylor BlauSep 25, 2023
  14. SZEDER GáborOct 8, 2023
  15. Taylor BlauOct 9, 2023
  16. Taylor BlauOct 9, 2023
  17. Junio C HamanoOct 9, 2023
  18. Taylor BlauOct 10, 2023
  19. 08/15 bloom: annotate filters with hash versionTaylor Blau, Aug 21, 2023
  20. 09/15 bloom: prepare to discard incompatible Bloom filtersTaylor Blau, Aug 21, 2023
  21. 10/15 t/t4216-log-bloom.sh: harden `test_bloom_filters_not_used()`Taylor Blau, Aug 21, 2023
  22. 11/15 commit-graph.c: unconditionally load Bloom filtersTaylor Blau, Aug 21, 2023
  23. 12/15 commit-graph: drop unnecessary `graph_read_bloom_data_context`Taylor Blau, Aug 21, 2023
  24. 13/15 object.h: fix mis-aligned flag bits tableTaylor Blau, Aug 21, 2023
  25. 14/15 commit-graph: reuse existing Bloom filters where possibleTaylor Blau, Aug 21, 2023
  26. 15/15 bloom: introduce `deinit_bloom_filters()`Taylor Blau, Aug 21, 2023
  27. Jonathan TanAug 24, 2023
  28. Jonathan TanAug 25, 2023
  29. Jonathan TanAug 29, 2023
  30. Junio C HamanoAug 29, 2023
  31. 00/15 bloom: changed-path Bloom filters v2Jonathan Tan, Aug 30, 2023
  32. 13/15 object.h: fix mis-aligned flag bits tableJonathan Tan, Aug 30, 2023
  33. 06/15 repo-settings: introduce commitgraph.changedPathsVersionJonathan Tan, Aug 30, 2023
  34. 04/15 t/helper/test-read-graph: implement `bloom-filters` modeJonathan Tan, Aug 30, 2023
  35. 08/15 bloom: annotate filters with hash versionJonathan Tan, Aug 30, 2023
  36. 01/15 gitformat-commit-graph: describe version 2 of BDATJonathan Tan, Aug 30, 2023
  37. 11/15 commit-graph.c: unconditionally load Bloom filtersJonathan Tan, Aug 30, 2023
  38. 10/15 t/t4216-log-bloom.sh: harden `test_bloom_filters_not_used()`Jonathan Tan, Aug 30, 2023
  39. 09/15 bloom: prepare to discard incompatible Bloom filtersJonathan Tan, Aug 30, 2023
  40. 12/15 commit-graph: drop unnecessary `graph_read_bloom_data_context`Jonathan Tan, Aug 30, 2023
  41. 07/15 commit-graph: new filter ver. that fixes murmur3Jonathan Tan, Aug 30, 2023
  42. 02/15 t/helper/test-read-graph.c: extract `dump_graph_info()`Jonathan Tan, Aug 30, 2023
  43. 05/15 t4216: test changed path filters with high bit pathsJonathan Tan, Aug 30, 2023
  44. 14/15 commit-graph: reuse existing Bloom filters where possibleJonathan Tan, Aug 30, 2023
  45. 03/15 bloom.h: make `load_bloom_filter_from_graph()` publicJonathan Tan, Aug 30, 2023
  46. 15/15 bloom: introduce `deinit_bloom_filters()`Jonathan Tan, Aug 30, 2023
  47. Junio C HamanoAug 30, 2023
  48. 00/17 bloom: changed-path Bloom filters v2 (& sundries)Taylor Blau, Oct 10, 2023
  49. 01/17 t/t4216-log-bloom.sh: harden `test_bloom_filters_not_used()`Taylor Blau, Oct 10, 2023
  50. 02/17 revision.c: consult Bloom filters for root commitsTaylor Blau, Oct 10, 2023
  51. 04/17 gitformat-commit-graph: describe version 2 of BDATTaylor Blau, Oct 10, 2023
  52. 05/17 t/helper/test-read-graph.c: extract `dump_graph_info()`Taylor Blau, Oct 10, 2023
  53. Patrick SteinhardtOct 17, 2023
  54. Taylor BlauOct 18, 2023
  55. Junio C HamanoOct 18, 2023
  56. 03/17 commit-graph: ensure Bloom filters are read with consistent settingsTaylor Blau, Oct 10, 2023
  57. Patrick SteinhardtOct 17, 2023
  58. 06/17 bloom.h: make `load_bloom_filter_from_graph()` publicTaylor Blau, Oct 10, 2023
  59. 07/17 t/helper/test-read-graph: implement `bloom-filters` modeTaylor Blau, Oct 10, 2023
  60. 09/17 repo-settings: introduce commitgraph.changedPathsVersionTaylor Blau, Oct 10, 2023
  61. 15/17 object.h: fix mis-aligned flag bits tableTaylor Blau, Oct 10, 2023
  62. 12/17 bloom: prepare to discard incompatible Bloom filtersTaylor Blau, Oct 10, 2023
  63. 13/17 commit-graph.c: unconditionally load Bloom filtersTaylor Blau, Oct 10, 2023
  64. Patrick SteinhardtOct 17, 2023
  65. 11/17 bloom: annotate filters with hash versionTaylor Blau, Oct 10, 2023
  66. 14/17 commit-graph: drop unnecessary `graph_read_bloom_data_context`Taylor Blau, Oct 10, 2023
  67. 10/17 commit-graph: new filter ver. that fixes murmur3Taylor Blau, Oct 10, 2023
  68. Patrick SteinhardtOct 17, 2023
  69. Taylor BlauOct 18, 2023
  70. 08/17 t4216: test changed path filters with high bit pathsTaylor Blau, Oct 10, 2023
  71. Patrick SteinhardtOct 17, 2023
  72. Taylor BlauOct 18, 2023
  73. 16/17 commit-graph: reuse existing Bloom filters where possibleTaylor Blau, Oct 10, 2023
  74. 17/17 bloom: introduce `deinit_bloom_filters()`Taylor Blau, Oct 10, 2023
  75. Patrick SteinhardtOct 17, 2023
  76. Taylor BlauOct 18, 2023

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.