)]}'
{
  "commit": "7048e74609fbef2c91bfa3a80e3a9c4fc0ac04c9",
  "tree": "a82130d3d198f581804ed524efad3f24eb30af33",
  "parents": [
    "994869e2b5a6fa7c8cea2882ba4a396b4e95bf2c"
  ],
  "author": {
    "name": "Patrick Steinhardt",
    "email": "ps@pks.im",
    "time": "Thu Nov 06 09:52:54 2025 +0100"
  },
  "committer": {
    "name": "Junio C Hamano",
    "email": "gitster@pobox.com",
    "time": "Thu Nov 06 10:54:34 2025 -0800"
  },
  "message": "object: fix performance regression when peeling tags\n\nOur Bencher dashboards [1] have recently alerted us about a bunch of\nperformance regressions when writing references, specifically with the\nreftable backend. There is a 3x regression when writing many refs with\npreexisting refs in the reftable format, and a 10x regression when\nmigrating refs between backends in either of the formats.\n\nBisecting the issue lands us at 6ec4c0b45b (refs: don\u0027t store peeled\nobject IDs for invalid tags, 2025-10-23). The gist of the commit is that\nwe may end up storing peeled objects in both reftables and packed-refs\nfor corrupted tags, where the claimed tagged object type is different\nthan the actual tagged object type. This will then cause us to create\nthe `struct object *` with a wrong type, as well, and obviously nothing\ngood comes out of that.\n\nThe fix for this issue was to introduce a new flag to `peel_object()`\nthat causes us to verify the tagged object\u0027s type before writing it into\nthe refdb -- if the tag is corrupt, we skip writing the peeled value.\nTo verify whether the peeled value is correct we have to look up the\nobject type via the ODB and compare the actual type with the claimed\ntype, and that additional object lookup is costly.\n\nThis also explains why we see the regression only when writing refs with\nthe reftable backend, but we see the regression with both backends when\nmigrating refs:\n\n  - The reftable backend knows to store peeled values in the new table\n    immediately, so it has to try and peel each ref it\u0027s about to write\n    to the transaction. So the performance regression is visible for all\n    writes.\n\n  - The files backend only stores peeled values when writing the\n    packed-refs file, so it wouldn\u0027t hit the performance regression for\n    normal writes. But on ref migrations we know to write all new values\n    into the packed-refs file immediately, and that\u0027s why we see the\n    regression for both backends there.\n\nTaking a step back though reveals an oddity in the new verification\nlogic: we not only verify the _tagged_ object\u0027s type, but we also verify\nthe type of the tag itself. But this isn\u0027t really needed, as we wouldn\u0027t\nhit the bug in such a case anyway, as we only hit the issue with corrupt\ntags claiming an invalid type for the tagged object.\n\nThe consequence of this is that we now started to look up the target\nobject of every single reference we\u0027re about to write, regardless of\nwhether it even is a tag or not. And that is of course quite costly.\n\nFix the issue by only verifying the type of the tagged objects. This\nmeans that we of course still have a performance hit for actual tags.\nBut this only happens for writes anyway, and I\u0027d claim it\u0027s preferable\nto not store corrupted data in the refdb than to be fast here. Rename\nthe flag accordingly to clarify that we only verify the tagged object\u0027s\ntype.\n\nThis fix brings performance back to previous levels:\n\n    Benchmark 1: baseline\n      Time (mean ± σ):      46.0 ms ±   0.4 ms    [User: 40.0 ms, System: 5.7 ms]\n      Range (min … max):    45.0 ms …  47.1 ms    54 runs\n\n    Benchmark 2: regression\n      Time (mean ± σ):     140.2 ms ±   1.3 ms    [User: 77.5 ms, System: 60.5 ms]\n      Range (min … max):   138.0 ms … 142.7 ms    20 runs\n\n    Benchmark 3: fix\n      Time (mean ± σ):      46.2 ms ±   0.4 ms    [User: 40.2 ms, System: 5.7 ms]\n      Range (min … max):    45.0 ms …  47.3 ms    55 runs\n\n    Summary\n      update-ref: baseline\n        1.00 ± 0.01 times faster than fix\n        3.05 ± 0.04 times faster than regression\n\n[1]: https://bencher.dev/perf/git/plots\n\nSigned-off-by: Patrick Steinhardt \u003cps@pks.im\u003e\nSigned-off-by: Junio C Hamano \u003cgitster@pobox.com\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "e72b0ed4360e67e40d661aca8beb7bc3887d84b0",
      "old_mode": 33188,
      "old_path": "object.c",
      "new_id": "b08fc7a163ae69f4e0c386ce0ac72c424f7d4633",
      "new_mode": 33188,
      "new_path": "object.c"
    },
    {
      "type": "modify",
      "old_id": "1499f63d507c32c5c1c2b1d0244beb584439c7d4",
      "old_mode": 33188,
      "old_path": "object.h",
      "new_id": "e9baade1e0ecabef9c90fe526dbd1c3258ca6363",
      "new_mode": 33188,
      "new_path": "object.h"
    },
    {
      "type": "modify",
      "old_id": "d8667c569a18f15ad67270351beb857ffd5b45a7",
      "old_mode": 33188,
      "old_path": "ref-filter.c",
      "new_id": "d7454269e87cd3a1feb2856799a23137fc1855d2",
      "new_mode": 33188,
      "new_path": "ref-filter.c"
    },
    {
      "type": "modify",
      "old_id": "1ab0c5039301647d133b9672898a8262aa8a1568",
      "old_mode": 33188,
      "old_path": "refs/packed-backend.c",
      "new_id": "5aa615011a6db30a4c022fa03b9e997bdb2ac357",
      "new_mode": 33188,
      "new_path": "refs/packed-backend.c"
    },
    {
      "type": "modify",
      "old_id": "6bbfd5618dac16693c735994d29550da73403bf7",
      "old_mode": 33188,
      "old_path": "refs/reftable-backend.c",
      "new_id": "1ac1f6156f256d66b27e6cf0f161117101e00bc1",
      "new_mode": 33188,
      "new_path": "refs/reftable-backend.c"
    }
  ]
}
