{
 "number": 26966,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/26966",
 "title": "index: initial sync speedup, parallelize process",
 "author": "furszy",
 "author_association": "MEMBER",
 "created_at": "2023-01-25T13:36:23Z",
 "updated_at": "2026-09-06T00:22:09Z",
 "age_days": 1331,
 "draft": true,
 "labels": [
  "UTXO Db and Indexes",
  "Needs rebase"
 ],
 "milestone": null,
 "base": "master",
 "head_sha": "c5dad86523e9715340851fa03e2ee470b0c35080",
 "head_ref": "2022_parallelize_blockfilter_index_2",
 "head_repo": "furszy/bitcoin-core",
 "head_history": [
  {
   "t": "2023-01-25T14:36:05Z",
   "sha": "ee71639af38ed0ca21ea09e0cda9818088f8a53c"
  },
  {
   "t": "2023-01-25T15:20:39Z",
   "sha": "deb2e6ea4b7ccca18ec3254f356e4f5f722e7c92"
  },
  {
   "t": "2023-01-25T16:29:14Z",
   "sha": "d8b0f5e246ca4600388a7375575423145f9fe3e8"
  },
  {
   "t": "2023-01-28T15:04:28Z",
   "sha": "65dc850806ad35cb7778e9b82f03521981279376"
  },
  {
   "t": "2023-01-28T15:40:27Z",
   "sha": "09cc56aca0fe9231d5beaa4bef2948c25359a8a5"
  },
  {
   "t": "2023-01-30T16:15:03Z",
   "sha": "43e6237cd58be888a867dc7a4a7f2d35615ae640"
  },
  {
   "t": "2023-01-30T20:30:55Z",
   "sha": "b2b62f513a401dda1d60412189193840fb57aa3a"
  },
  {
   "t": "2023-01-30T20:55:07Z",
   "sha": "4e1fba191fe532b2b43ce18964579482fda2eddd"
  },
  {
   "t": "2023-02-21T13:56:43Z",
   "sha": "1da78b2a4e0fe8d5d0d290c8453dabc22b04c8a6"
  },
  {
   "t": "2023-02-21T14:01:44Z",
   "sha": "f264ed562e786abbf8636d57bdd933915d70826c"
  },
  {
   "t": "2023-02-22T22:45:29Z",
   "sha": "e57e27ab2e62c9d758bef374f1250c5f7b1d5195"
  },
  {
   "t": "2023-02-27T22:37:47Z",
   "sha": "b18df4d0e826d1a3c1d08dd1addeecc39ff13afa"
  },
  {
   "t": "2023-02-27T23:57:02Z",
   "sha": "876afc27e4bd69940587790fb66d51d949284004"
  },
  {
   "t": "2023-05-11T20:50:10Z",
   "sha": "7b0fda74e28a4c2eb96373617431b5b136ac5fb7"
  },
  {
   "t": "2023-06-21T15:48:53Z",
   "sha": "a6602df24936b108da6911edc27db647dc69a6ec"
  },
  {
   "t": "2023-06-30T16:39:54Z",
   "sha": "608ba0a5fc7adbc44173ce42b0ad42f71a6006ef"
  },
  {
   "t": "2023-08-09T14:23:49Z",
   "sha": "642050d9eafc917e9d4468f94d696c7fdb7c660f"
  },
  {
   "t": "2023-08-29T13:19:44Z",
   "sha": "9bffd743d1d08c8e16a86214aabf5ecb92e4c1b3"
  },
  {
   "t": "2023-09-04T21:30:51Z",
   "sha": "fce1e63d3bcb13500afb4988b6b64e789a1704b6"
  },
  {
   "t": "2023-09-12T13:10:11Z",
   "sha": "80cb63b28c38646468ab498198b2af6074d97da3"
  },
  {
   "t": "2023-10-18T14:23:41Z",
   "sha": "937c89670d96faa393dbad2f3226189b8154b91d"
  },
  {
   "t": "2023-10-27T14:59:54Z",
   "sha": "4027f6e6020cb1ab9ab4edf778841a82d1769a87"
  },
  {
   "t": "2023-10-27T15:44:32Z",
   "sha": "373b0bb077296624b7aeb5108673ae8f8b00b20d"
  },
  {
   "t": "2024-03-20T22:07:21Z",
   "sha": "aa0da5a82a8e9910cad6c8e5e861bea0f4d5941d"
  },
  {
   "t": "2024-03-21T17:22:34Z",
   "sha": "fd79112a1cec4cf993200bd89e6fd19957bb608e"
  },
  {
   "t": "2024-03-23T13:08:13Z",
   "sha": "eff91c8656cd49b5b626925f5ec281b06f4eba28"
  },
  {
   "t": "2024-04-07T17:30:09Z",
   "sha": "57e37aecfdacb6f911dd57c4c1fb680a7660633d"
  },
  {
   "t": "2024-05-01T14:44:51Z",
   "sha": "4ceaf6253eada351a20a82ba69a3aee636726527"
  },
  {
   "t": "2024-06-26T14:57:36Z",
   "sha": "bfc8dce122474ddf6e5814731fa816cbdf1f7c7b"
  },
  {
   "t": "2024-08-14T19:32:24Z",
   "sha": "e798711ab0f3dd973ab359f54c6f79581d79e2c3"
  },
  {
   "t": "2024-08-14T19:33:02Z",
   "sha": "513fbc6b1fae951ea292bb3c8a32d104773995f0"
  },
  {
   "t": "2024-10-01T19:12:52Z",
   "sha": "81dcee9464fc0f2b4a4fe547f312b71b405010b2"
  },
  {
   "t": "2025-02-25T18:57:49Z",
   "sha": "349b09983d994cb46faeed12b123ae2269c6c516"
  },
  {
   "t": "2025-06-05T20:00:39Z",
   "sha": "55f23cddc345a818191b86479a6328001d32dc26"
  },
  {
   "t": "2025-06-05T23:16:12Z",
   "sha": "1ccb9f6959e28aabd06e891cdf0b30ee99add2f3"
  },
  {
   "t": "2025-06-06T01:34:08Z",
   "sha": "f3f5e4cb071497f7c9fccf5e81edc5c476333098"
  },
  {
   "t": "2025-06-13T00:20:55Z",
   "sha": "53bc8b9663fd51fa43eb568aee8a38936cc7c6e8"
  },
  {
   "t": "2025-06-20T16:42:23Z",
   "sha": "69ac2c05e04ff66428426d00fb2db4672d85726c"
  },
  {
   "t": "2025-06-23T19:08:20Z",
   "sha": "d14fa644acf03919a74909b52916fd4dff4aa88e"
  },
  {
   "t": "2025-06-23T19:14:01Z",
   "sha": "f4041cb91ff965fa08ac36ddb130f67e2d1671b0"
  },
  {
   "t": "2025-06-23T21:59:44Z",
   "sha": "a13b48d67622659529fabdec6e2c96e31aa86263"
  },
  {
   "t": "2025-06-24T02:54:14Z",
   "sha": "6acaa7919d2fdfdecb8c626e6f9fe3447f5fd11b"
  },
  {
   "t": "2025-06-24T13:58:02Z",
   "sha": "5fea91eae012a062b540b02fffb872e81b584eaa"
  },
  {
   "t": "2025-06-24T21:12:30Z",
   "sha": "a3e7d97b89821239bbba3e2c434a9a6405911865"
  },
  {
   "t": "2025-06-25T14:12:23Z",
   "sha": "e4bdca5f74bf504882a6bc517192c426d4ad2cf8"
  },
  {
   "t": "2025-06-25T14:14:07Z",
   "sha": "d405976ed05ec4b99b18151ae5b1021a53531be0"
  },
  {
   "t": "2025-06-25T16:15:15Z",
   "sha": "59984a1d31aca2a523a8e8aafb3cb26500d8e990"
  },
  {
   "t": "2025-06-26T13:42:23Z",
   "sha": "ef3e47d4623bcfacdfe87f556520fcb42d3f43e5"
  },
  {
   "t": "2025-06-26T15:08:05Z",
   "sha": "c1b55be50f7c6dffde95d927b5d89e437b13ac79"
  },
  {
   "t": "2025-06-26T17:53:08Z",
   "sha": "89f5fc80c2957c89a6f42f5bde11c9fb0f9abc3a"
  },
  {
   "t": "2025-06-26T21:05:50Z",
   "sha": "f6b7da2493488c0e48ad9acf569ef3bdaf6b07b3"
  },
  {
   "t": "2025-07-08T02:28:38Z",
   "sha": "e8e1e1471d08a8c9f22d3f0a68bab732649c9a52"
  },
  {
   "t": "2025-07-14T21:31:35Z",
   "sha": "cd55c0317a69aae8fb69c0467b3582381e41b259"
  },
  {
   "t": "2025-07-15T13:11:48Z",
   "sha": "f1b4dd18bbc4232177be31bfdd373975887c9187"
  },
  {
   "t": "2025-07-15T17:36:20Z",
   "sha": "2a6161adf0a92a64aac391deb9146e23ec99e47d"
  },
  {
   "t": "2025-07-23T18:17:52Z",
   "sha": "068537ae61309a61e3bc350dc747d75cf2207517"
  },
  {
   "t": "2025-07-25T21:35:18Z",
   "sha": "9bba5ef0cc5b6890eba2be3a6ed429c4fea5a28f"
  },
  {
   "t": "2025-08-07T22:43:19Z",
   "sha": "b349fbafadbc9b5af0a35b4c282e142994a5a1d7"
  },
  {
   "t": "2025-08-08T01:37:32Z",
   "sha": "647f6f0f4432a113a66bb3a423afe1ba68a842c9"
  },
  {
   "t": "2025-08-08T02:33:06Z",
   "sha": "f6a52c6686b4d14aea2b05ca0413ad664bcf72e6"
  },
  {
   "t": "2025-08-12T20:25:14Z",
   "sha": "49efddfae7e46637be2c2efc3938ef1a0fecb1e3"
  },
  {
   "t": "2025-08-20T09:26:20Z",
   "sha": "69792b6d3ee4b7320c608d5b23056d3e8b998247"
  },
  {
   "t": "2025-08-30T17:46:04Z",
   "sha": "84fec2c2e1b43783dc4049e2cb69ebd9035083a1"
  },
  {
   "t": "2025-08-30T17:47:16Z",
   "sha": "3d123f76428b745f57c77519b07ebce442f82c06"
  },
  {
   "t": "2025-09-01T13:17:19Z",
   "sha": "7fccd9fbae85d5a1d0109128ef37c641ba32287e"
  },
  {
   "t": "2025-09-17T20:09:37Z",
   "sha": "46c6bfe5485bd28f76ff23c4a92f70f2a972d681"
  },
  {
   "t": "2025-09-17T20:43:05Z",
   "sha": "8eafb7b2bca9680e4467cd9122042c5d84a85522"
  },
  {
   "t": "2025-09-18T01:31:33Z",
   "sha": "9dbea51ab511fc9823a87cd522c1d705a3436bad"
  },
  {
   "t": "2025-10-07T21:07:50Z",
   "sha": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95"
  },
  {
   "t": "2025-10-08T14:42:42Z",
   "sha": "5a992bce07ea1f038bfaa18aa6f774e7d179075a"
  },
  {
   "t": "2025-10-08T15:39:32Z",
   "sha": "202b7cc81a71444297dc3cfb4be461cac995d944"
  },
  {
   "t": "2025-10-08T15:52:13Z",
   "sha": "9270fdded923ddea7e89f72fed5184fecb462efa"
  },
  {
   "t": "2025-10-08T16:04:41Z",
   "sha": "3c4c3cbe09061088d2649b6bfadacaeed49a6ed6"
  },
  {
   "t": "2025-10-08T20:12:58Z",
   "sha": "234f65b6a733a96242381e657ae9978d22b49a8f"
  },
  {
   "t": "2025-10-08T20:38:45Z",
   "sha": "f856b8780a90a636012c4137884a26b79cd646a9"
  },
  {
   "t": "2025-10-08T20:55:28Z",
   "sha": "1abd4afe69920c9caca01eda3ab3964823f9ecd6"
  },
  {
   "t": "2025-10-08T21:37:44Z",
   "sha": "80e63b71d828276bb18ec1eaa6038ff298db6f21"
  },
  {
   "t": "2025-10-09T18:26:09Z",
   "sha": "6a81272c2e7e5852187cbaa2f5d9dd5c95dc2745"
  },
  {
   "t": "2025-10-09T18:32:51Z",
   "sha": "da73d3dc6cec170e3c9e459d15960af196a124f9"
  },
  {
   "t": "2025-10-10T18:50:05Z",
   "sha": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095"
  },
  {
   "t": "2025-10-13T15:34:31Z",
   "sha": "f13033233d3f2953f179612ac0582b653ac629da"
  },
  {
   "t": "2025-10-13T17:47:03Z",
   "sha": "afd759a12ff8ee971fbc951d3008eb94073ff29f"
  },
  {
   "t": "2025-10-13T19:53:43Z",
   "sha": "536f7b0acf376f05d18834a35436a6654e3b83ac"
  },
  {
   "t": "2025-10-14T01:31:11Z",
   "sha": "201a90f03431708933040b3ba56aecd951db845e"
  },
  {
   "t": "2025-10-14T14:27:39Z",
   "sha": "21479ee0d62f734a1aa2c431f4f0ccb289e89f2c"
  },
  {
   "t": "2025-10-14T20:04:27Z",
   "sha": "fcdd89bdb8b9b89727f3817059b45ee2ed832ecc"
  },
  {
   "t": "2025-10-14T20:10:01Z",
   "sha": "f7cf59e4fb373eaaa83e650850e66d0df5ac44cd"
  },
  {
   "t": "2025-10-15T14:23:55Z",
   "sha": "3dede1f3005957264729713b1fcace65795e5818"
  },
  {
   "t": "2025-10-15T16:58:06Z",
   "sha": "831dec831572249f838344d099cb38ab9555dc91"
  },
  {
   "t": "2025-10-17T18:42:45Z",
   "sha": "c38a5db0ebfe05749789d0230420cb52dc773adb"
  },
  {
   "t": "2025-10-24T05:34:17Z",
   "sha": "0ad89bf7a6b2e3161a153308c14029c43b0e06a3"
  },
  {
   "t": "2026-02-02T00:21:36Z",
   "sha": "01a96ee859a9ba80a6f198a6b0c0c5443821d130"
  },
  {
   "t": "2026-02-02T05:04:38Z",
   "sha": "c5dad86523e9715340851fa03e2ee470b0c35080"
  }
 ],
 "additions": 1031,
 "deletions": 153,
 "changed_files": 14,
 "commit_count": 11,
 "size_bucket": "XL",
 "mergeable_state": "dirty",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "w0xlt",
      "url": "https://github.com/bitcoin/bitcoin/pull/26966#pullrequestreview-1275543891"
     },
     {
      "login": "sedited",
      "url": "https://github.com/bitcoin/bitcoin/pull/26966#pullrequestreview-1749211886"
     },
     {
      "login": "ismaelsadeeq",
      "url": "https://github.com/bitcoin/bitcoin/pull/26966#pullrequestreview-3039751954"
     },
     {
      "login": "mzumsande",
      "url": "https://github.com/bitcoin/bitcoin/pull/26966#pullrequestreview-3325595624"
     }
    ],
    "approach_ack": [
     {
      "login": "ryanofsky",
      "url": "https://github.com/bitcoin/bitcoin/pull/26966#pullrequestreview-2754283048"
     }
    ],
    "approach_nack": [
     {
      "login": "l0rinc",
      "url": "https://github.com/bitcoin/bitcoin/pull/26966#pullrequestreview-2965376357"
     }
    ],
    "stale_ack": [
     {
      "login": "pinheadmz",
      "url": "https://github.com/bitcoin/bitcoin/pull/26966#pullrequestreview-3067959910"
     }
    ]
   },
   "conflicts": [
    {
     "number": 34489,
     "title": "index: batch db writes during initial sync",
     "author": "furszy"
    },
    {
     "number": 34440,
     "title": "Refactor CChain methods to use references, tests",
     "author": "optout21"
    },
    {
     "number": 33966,
     "title": "refactor: disentangle miner startup defaults from runtime options",
     "author": "Sjors"
    },
    {
     "number": 33689,
     "title": "http: replace WorkQueue and single threads handling for ThreadPool",
     "author": "furszy"
    },
    {
     "number": 33306,
     "title": "index: Force database compaction in coinstatsindex",
     "author": "fjahr"
    },
    {
     "number": 29770,
     "title": "index: Check all necessary block data is available before starting to sync",
     "author": "fjahr"
    },
    {
     "number": 17580,
     "title": "refactor: Add ALLOW_LIST flags and enforce usage in CheckArgFlags",
     "author": "ryanofsky"
    }
   ]
  }
 },
 "acks_parsed": {
  "w0xlt": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2023-01-30T16:42:24Z",
   "stale": false
  },
  "sedited": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2023-11-25T23:01:29Z",
   "stale": false
  },
  "ryanofsky": {
   "kind": "approach_ack",
   "hash": "349b09983d994cb46faeed12b123ae2269c6c516",
   "t": "2025-04-09T21:50:38Z",
   "stale": false
  },
  "ismaelsadeeq": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2025-07-21T21:03:51Z",
   "stale": false
  },
  "pinheadmz": {
   "kind": "ack",
   "hash": "9bba5ef0cc5b6890eba2be3a6ed429c4fea5a28f",
   "t": "2025-07-29T14:53:00Z",
   "stale": true
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 1,
  "concept_ack": 3,
  "approach_ack": 1,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 3,
  "changes_requested": 1,
  "distinct_reviewers": [
   "Eunovo",
   "Sjors",
   "andrewtoth",
   "brunoerg",
   "ismaelsadeeq",
   "l0rinc",
   "maflcko",
   "mzumsande",
   "pinheadmz",
   "ryanofsky",
   "sedited",
   "w0xlt",
   "yancyribbens"
  ]
 },
 "signals": {
  "needs_rebase": true,
  "ci_failed": false,
  "mergeable_state": "dirty",
  "last_author_activity": "2026-02-02T05:04:38Z",
  "last_reviewer_activity": "2025-10-15T19:14:59Z",
  "last_reviewer": "l0rinc",
  "author_silent_days": 227,
  "waiting_on_author_days": 0,
  "days_since_update": 11
 },
 "refs": {
  "mentioned": [
   24230,
   24539,
   26951,
   27006,
   28955,
   31132,
   32694,
   32948,
   33689,
   34489,
   234857,
   236845,
   237681
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 34489,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "index: batch db writes during initial sync"
   },
   {
    "number": 24230,
    "type": "pull",
    "state": "open",
    "merged": false,
    "merged_at": null,
    "title": "indexes: Stop using node internal types and locking cs_main, improve sync logic"
   },
   {
    "number": 24539,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-02-20",
    "title": "Add a \\\"tx output spender\\\" index"
   },
   {
    "number": 26951,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "wallet: improve rescan performance 640%"
   },
   {
    "number": 27006,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "reduce cs_main scope, guard block index 'nFile' under a local mutex"
   },
   {
    "number": 28955,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2024-03-20",
    "title": "index: block filters sync, reduce disk read operations by caching last header"
   },
   {
    "number": 31132,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "validation: fetch block inputs on parallel threads"
   },
   {
    "number": 32694,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2025-06-12",
    "title": "index: move disk read lookups to base class"
   },
   {
    "number": 32948,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2025-07-14",
    "title": "refactor: cleanup index logging"
   },
   {
    "number": 33689,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-02-11",
    "title": "http: replace WorkQueue and single threads handling for ThreadPool"
   },
   {
    "number": 234857,
    "type": null,
    "state": null,
    "merged": null,
    "merged_at": null,
    "title": null
   },
   {
    "number": 236845,
    "type": null,
    "state": null,
    "merged": null,
    "merged_at": null,
    "title": null
   },
   {
    "number": 237681,
    "type": null,
    "state": null,
    "merged": null,
    "merged_at": null,
    "title": null
   }
  ],
  "conflicts": [
   34489,
   34440,
   33966,
   33689,
   33306,
   29770,
   17580
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/index/base.cpp",
  "src/index/base.h",
  "src/index/blockfilterindex.cpp",
  "src/index/blockfilterindex.h",
  "src/index/txindex.h",
  "src/init.cpp",
  "src/sync.h",
  "src/test/blockfilter_index_tests.cpp",
  "src/test/threadpool_tests.cpp",
  "src/util/threadpool.h"
 ],
 "body": "Work is currently happening on #34489.\n\n------------------------------------------------\n\nThe current procedure for building the block filter index involves processing filters one at a time;\nReading blocks, undo data, and previous headers from disk sequentially.\n\nThis PR introduces a new mechanism to perform the work concurrently. Dividing the filters\ngeneration workload among a pool of workers that can be configured by the user,\nsignificantly increasing the speed of the index construction process.\n\nThe same concurrent processing model has been applied to the transactions index as well.\n\nThe newly introduced init flag `-indexworkers=<n>` enables the concurrent sync\nbehavior.\nWhere \"n\" is the number of worker threads that will be spawned at startup to create ranges\nof block filters during the initial sync process. Destroying the workers pool once the\ninitial sync completes.\nNote: by default, the parallelized sync process is not enabled.\n\nNow the juicy part:\nIn my computer, with the node in debug mode and on IBD, with `-indexworkers=4`, the\nblock filter index generation took less than an hour. While, in master, the sync took more than 7 hours.\n\nImportant Note:\nAs the access to the block data on disk is protected by `cs_main`, this new feature runs substantially\nfaster when the node is not in IBD.\n\n#### Testing Notes:\n\n1. Sync your node without any index.\n2. Restart the node with one of the indexes (`-blockfilterindex` or `-txindex`) and `-connect=0` (to sync only the index, without running the net/validation threads. Since threads won't be competing for `cs_main`, this will give you a more accurate result).\n\n   You\u2019ll see a \"[index name] is enabled at height [height]\" log entry once it finishes. Then it\u2019s just a matter of subtracting the index startup time from the \"index synced\" log time.\n\n  Keep in mind that threads are shared among all indexes you start. So if you run both indexes at the same time, your benchmark results cannot be compared against single-index runs.\n\n#### Fuzz Test Coverage Report\nCoverage diff for the fuzz test can be found on https://brunoerg.xyz/bitcoin-core-coverage/26966/coverage_report/",
 "commits": [
  {
   "sha": "59d88389abfdea26029e83a574bf8470f1ccd846",
   "date": "2026-02-02T03:49:13Z",
   "message": "index: split block processing into process and post-process phases\n\nNo behavior changes.\n\nThis separation lays the groundwork for parallelizing the block data\ndigest phase, while preserving sequential consistency for indexes\nthat requires it before dumping the data to disk.\nAnd it will also allow us to batch database and file writes across\nmultiple blocks.\n\nEssentially, it introduces a two-phase block processing mechanism using\n'CustomProcessBlock()' and 'CustomPostProcessBlocks()':\n\n1) 'CustomProcessBlock()' will be in charge of digesting block data and\n   returning a custom result object per index.\n   The goal is to encapsulate all procedures that can safely run\n   concurrently here.\n\n2) 'CustomPostProcessBlocks()' receives all digested objects in order,\n   allowing the index to perform any subsequent steps that require sequentiality.\n   For example, the block filter index requires sequentiality in the headers-chain\n   construction, as each record depends on its predecessor hash.\n\nImportant note: at present, the code executes entirely synchronously."
  },
  {
   "sha": "2f4b456e854834973a37407f5f277c3ba673ea30",
   "date": "2026-02-02T03:49:15Z",
   "message": "index: add support for processing batches of blocks\n\nNo behavior changes. The index still processes one block at a time.\nThe code was refactored to allow handling multiple blocks on the\nprocessing phase.\n\nIntroducing the concept of a \"Task\" which merely represents a range\nof blocks and their resulting digests.\n\nIn the upcoming commits, we will take advantage of this to process batches\nof blocks concurrently."
  },
  {
   "sha": "f3e621ca61f174f04d8638de97f95ce0cbbc0fbc",
   "date": "2026-02-02T03:49:15Z",
   "message": "index: support processing blocks out of order\n\nNo behavior changes.\n\nThis allows indexes to signal that they can process blocks out of order,\nwhich is faster than strict forward ordering."
  },
  {
   "sha": "770b45d31cbe4d496bebc332448978a7e292a164",
   "date": "2026-02-02T03:49:15Z",
   "message": "Index: introduce SyncContext - move logging & last locator timers\n\nMove progress related timers (logging and locator write) into a shared\nSyncContext struct with atomic members. This does not change behavior\nbut prepares the code for safe multi-threaded execution in the next\ncommit."
  },
  {
   "sha": "aff4476f0ef6ee34c0e3fbfa7608c32fe7a58d6a",
   "date": "2026-02-02T03:49:15Z",
   "message": "index: encapsulate Task processing code into RunTask\n\nNo-behavior changes.\n\nThis refactor moves the existing processing logic from BaseIndex::Sync\ninto BaseIndex::RunTask, simplifying the main sync loop and laying\ngroundwork for parallelization of block processing in the upcoming commit.\nThe goal is to have BaseIndex::RunTask running concurrently."
  },
  {
   "sha": "fd32e394e0c953aabba8da943e1e1ef8516f262c",
   "date": "2026-02-02T03:49:15Z",
   "message": "util: introduce general purpose thread pool"
  },
  {
   "sha": "b57f8f2df35c6075fee488c3f9d4443b0fbfe67b",
   "date": "2026-02-02T03:49:15Z",
   "message": "init: provide thread pool to indexes\n\nAnd add option to customize thread pool workers count"
  },
  {
   "sha": "b99c6909cbd617d6627971750b8ef49f2b3935c2",
   "date": "2026-02-02T03:49:25Z",
   "message": "index: implement index parallel sync\n\nThis introduces parallel sync for the indexes initial sync,\ndistributing the workload across multiple threads.\n\nWhen enabled, the chain is divided into fixed-size block ranges called\n\"tasks\". Worker threads consume tasks concurrently, calling to the\nCustomProcessBlock over their assigned range, storing results in a\nshared context while opportunistically batching and dumping the collected\ninformation to disk sequentially (when needed).\n\nSince large reorgs are improbable during initial sync (headers-chain PoW\ndictates the valid chain), reorgs are detected only before syncing begins\nand once it completes. Any new blocks connected during the process are\ncaught up sequentially at the end. This, together with the fact that we\nno longer depend on an intermediate \"index best block\" that might be out\nof sync with the index m_best_block_index, allows us to remove the\nindex_reorg_crash test, as it is no longer possible to call Rewind on a\nblock that is not the index tip."
  },
  {
   "sha": "4b93e8c51e88e9e210efd7aed3954cffa1d79f1b",
   "date": "2026-02-02T03:49:27Z",
   "message": "index: enable block filter index parallel sync\n\nIt also adds coverage for initial sync from a particular block.\nMimicking a node restart."
  },
  {
   "sha": "2ba13efb898b49fd989d3e647f77568dbfc81a2e",
   "date": "2026-02-02T03:49:27Z",
   "message": "txindex: enable parallel sync"
  },
  {
   "sha": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "date": "2026-02-02T03:49:27Z",
   "message": "fuzz: add test case for threadpool\n\nCo-authored-by: furszy <matiasfurszyfer@protonmail.com>"
  }
 ],
 "timeline": [
  {
   "t": "2023-01-25T14:36:05Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "ee71639af38ed0ca21ea09e0cda9818088f8a53c"
  },
  {
   "t": "2023-01-25T15:20:39Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "deb2e6ea4b7ccca18ec3254f356e4f5f722e7c92"
  },
  {
   "t": "2023-01-25T16:29:14Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "d8b0f5e246ca4600388a7375575423145f9fe3e8"
  },
  {
   "t": "2023-01-26T14:38:27Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Cool, will take it for a spin..."
  },
  {
   "t": "2023-01-28T15:04:28Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "65dc850806ad35cb7778e9b82f03521981279376"
  },
  {
   "t": "2023-01-28T15:18:54Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Cool @Sjors, just pushed a small update. Found a little bug.\n\nGoing to add an important note to the PR description (because otherwise testing results will vary a lot):\n\nAs the access to the block data on disk is protected by `cs_main`, this new feature runs substantially\nfaster when the node is not in IBD.\n(where \"substantially\" here means full index sync, with 5 workers, in less than 20 minutes in my computer)."
  },
  {
   "t": "2023-01-28T15:40:27Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "09cc56aca0fe9231d5beaa4bef2948c25359a8a5"
  },
  {
   "t": "2023-01-29T22:49:24Z",
   "kind": "review",
   "who": "mzumsande",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "09cc56aca0fe9231d5beaa4bef2948c25359a8a5",
   "text": "It's probably much easier suggested than done, but did you attempt to implement parallelization in a more general way so that other indices could benefit from it as well? On first glance, `txindex`, and the indices suggested in PRs  (#24539, #26951) seem to be parallelizable as well (not `coinstatsindex` though)."
  },
  {
   "t": "2023-01-30T13:37:06Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nYeah, that is part of the plan. I started with the block filter index because it requires an special treatment that `txindex` parallelization does not require (block/undo data reading and block filters creation can be parallelized but writing must be done sequentially due the need to link filter headers to their predecessors to create the filters-chain on disk).\n\nMy idea was to start reviewing this one, so the process gets as clean as possible, and then move forward with the generalization step. It's usually more natural to abstract processes when the specific cases are well-defined."
  },
  {
   "t": "2023-01-30T16:15:03Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "43e6237cd58be888a867dc7a4a7f2d35615ae640"
  },
  {
   "t": "2023-01-30T16:42:24Z",
   "kind": "review",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "43e6237cd58be888a867dc7a4a7f2d35615ae640",
   "text": "Concept ACK\n\nPerhaps it could have separate parallelization and index logic. So it could be reused in other indexes."
  },
  {
   "t": "2023-01-30T20:30:55Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "b2b62f513a401dda1d60412189193840fb57aa3a"
  },
  {
   "t": "2023-01-30T20:55:07Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "4e1fba191fe532b2b43ce18964579482fda2eddd"
  },
  {
   "t": "2023-02-07T13:47:32Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Building the index on (AMD Ryzen 7950X, blocks stored on SSD):\n* master @ fe86616bb4ad0c4296d34299bc2e2f0fca1fe936: 35'15\" (mostly 1 alternating CPU thread)\n* this PR (rebased)\n  * n=8: 5'20\" (uses about 8 of 32 CPU threads as expected)\n  * n=32: 4'26\" (pleasantly close to 100% CPU usage with a dip every 10 seconds, but it drops to only 1 CPU in the last minute or two)\n\nI made sure to not load any wallets and disabled other indexes.\n\nI didn't test if the index was correct.\n\nI wonder if, for users without this index, it would be faster to generate the index, rescan the wallet and then delete it again. Combined with #26951 you would only have to generate filters up to the age of the wallet (IIUC, cc @pstratem).\n\nNote to self for future benchmarks: `-txindex` takes 1:07'14\", `-coinstatsindex` takes 3.5 hours."
  },
  {
   "t": "2023-02-07T14:18:36Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Great results @Sjors!.\n\nCould also give it a run rebased on top #27006.\nOn master, the index initial sync is slower when the node is in IBD because the index thread has to compete for access to block data on disk through `cs_main` acquisition.\n\n[quoted text omitted]\nThe PR contain a test verifying it.\n\n----------------------------------------------\n\nSide note:\nI'm working on generalizing the parallelization flow so other indexes, like the txindex and #26951 can make use of it too."
  },
  {
   "t": "2023-02-21T13:56:43Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "1da78b2a4e0fe8d5d0d290c8453dabc22b04c8a6"
  },
  {
   "t": "2023-02-21T14:01:44Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "f264ed562e786abbf8636d57bdd933915d70826c"
  },
  {
   "t": "2023-02-22T22:45:29Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "e57e27ab2e62c9d758bef374f1250c5f7b1d5195"
  },
  {
   "t": "2023-02-27T22:37:47Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "b18df4d0e826d1a3c1d08dd1addeecc39ff13afa"
  },
  {
   "t": "2023-02-27T23:57:02Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "876afc27e4bd69940587790fb66d51d949284004"
  },
  {
   "t": "2023-02-28T12:40:55Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "PR updated, most of it implementation has changed.\n\nThe news are:\n1) Decreased ThreadSync `cs_main` lock contention.\n2) Removed `CBlockIndex` access from the child indexes internals.\n3) Implemented generic workers pool.\n4) Introduced a last header cache for the Block Filter index. Avoiding disk reads on every new processed block.\n5) Enabled parallel sync on the tx index.\n\nImportant Note:\nThe introduced workers pool spawned by the `-indexworkers` init arg is shared among all the enabled indexes that support parallel sync.\n\nThe implementation uses `std::any` mainly to simplify the patch-set, the base class template form of it requires a larger set of changes.\n\nSide note: in a first glance and without going too far over the coinstats index implementation, I would say that it could also be parallelized. But will leave it for a follow-up to not continue expanding the PR size.\n\nFuture (doesn't need to be included here, just mentioning so the path is clear):\nI'm working on decoupling the initial sync logic into a separate structure, so indexes subscribe to events instead of reading blocks from disk by themselves."
  },
  {
   "t": "2023-05-11T20:50:10Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "7b0fda74e28a4c2eb96373617431b5b136ac5fb7"
  },
  {
   "t": "2023-06-21T15:48:53Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "a6602df24936b108da6911edc27db647dc69a6ec"
  },
  {
   "t": "2023-06-30T16:39:54Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "608ba0a5fc7adbc44173ce42b0ad42f71a6006ef"
  },
  {
   "t": "2023-08-09T14:23:49Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "642050d9eafc917e9d4468f94d696c7fdb7c660f"
  },
  {
   "t": "2023-08-29T13:19:44Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "9bffd743d1d08c8e16a86214aabf5ecb92e4c1b3"
  },
  {
   "t": "2023-09-04T21:30:51Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "fce1e63d3bcb13500afb4988b6b64e789a1704b6"
  },
  {
   "t": "2023-09-12T13:10:11Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "80cb63b28c38646468ab498198b2af6074d97da3"
  },
  {
   "t": "2023-10-18T14:23:41Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "937c89670d96faa393dbad2f3226189b8154b91d"
  },
  {
   "t": "2023-10-25T09:16:14Z",
   "kind": "comment",
   "who": "maflcko",
   "assoc": "MEMBER",
   "text": "CI is still red. Also, how would this work with AU?"
  },
  {
   "t": "2023-10-27T14:59:54Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "4027f6e6020cb1ab9ab4edf778841a82d1769a87"
  },
  {
   "t": "2023-10-27T15:44:32Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "373b0bb077296624b7aeb5108673ae8f8b00b20d"
  },
  {
   "t": "2023-11-07T11:09:23Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nThat PR currently does not cleanly cherry-pick on top of this PR, not can I (trivially) rebase this PR on top top of it. Happy to try if you can make a branch.\n\nI just tried it again, deleting the blockfilterindex and rebuilding it. My impression is that it's going slower than before and I'm not seeing much CPU activity.\n\nI also noticed the shutdown takes a very long time, with the indexer (?) threads sticking around for many minutes:"
  },
  {
   "t": "2023-11-24T12:58:03Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/index/blockfilterindex.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "NIt: Might take this opportunity to add the <uint256.h> header?"
  },
  {
   "t": "2023-11-24T15:18:11Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "NIt: `// In case it hasn't been stoppped.`"
  },
  {
   "t": "2023-11-24T15:19:04Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "Nit: Adjust copyright date."
  },
  {
   "t": "2023-11-24T15:32:09Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "If I run IWYU locally, it reports the following headers as missing:\n```\n#include <cstddef>            // for size_t\n#include <algorithm>           // for max\n#include <atomic>              // for atomic\n#include <functional>          // for function\n#include <memory>              // for make_shared\n#include <stdexcept>           // for runtime_error\n#include <utility>             // for move, swap\n#include <vector>              // for vector\n```"
  },
  {
   "t": "2023-11-24T22:37:07Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "Just a comment: It would be nice if we could re-use the `CThreadInterrupt` interface here, but I don't think it's easily possible."
  },
  {
   "t": "2023-11-24T22:41:50Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "Why is this added?"
  },
  {
   "t": "2023-11-25T12:37:05Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "I don't think it would be necessary for the tasks themselves, since you already demonstrate in the tests how to retrieve exceptions, but I think some kind of exception handling and infomation logging similar to the one of `util::TraceThread` would still be nice. Did you choose not to use `TraceThread` on purpose?"
  },
  {
   "t": "2023-11-25T12:44:25Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "Could use this test to stress test the pool a bit with more tasks?"
  },
  {
   "t": "2023-11-25T12:45:50Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "I think a test where each task is assigned slightly different data would be nice. Something like:\n```c++\n+        std::atomic<int> par_sum{0};\n+        for (int i = 0; i < num_tasks; i++) {\n+            threadPool.Submit([&par_sum,i]() {\n+                par_sum += i;\n             });\n         }\n+        int sync_sum{0};\n+        for (int i = 0; i < num_tasks; i++) {\n+            sync_sum += i;\n+        }\n```"
  },
  {
   "t": "2023-11-25T12:54:33Z",
   "kind": "comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "text": "This all looks pretty promising. I left some feedback before continuing with the latter half of the PR."
  },
  {
   "t": "2023-11-25T16:22:29Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "`static constexpr int16_t INDEX_WORKERS_COUNT{0}` (and similarly below)?"
  },
  {
   "t": "2023-11-25T16:46:03Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit 7b2d4d471ae251d4c22184dda26b73993a65eff8: Could the `AllowParallelSync` method be moved to this commit?"
  },
  {
   "t": "2023-11-25T17:25:51Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "Make `max_blocks_to_sync`, `tip_height`, and `remaining_blocks` `const`."
  },
  {
   "t": "2023-11-25T17:28:24Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "Nitty nit: Keep the order of `workers_count` and `work_chunk` consistent?"
  },
  {
   "t": "2023-11-25T22:38:59Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "Would `so we also process blocks in this thread until all workers finish.` be more accurate?"
  },
  {
   "t": "2023-11-25T22:52:11Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/test/blockfilter_index_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "In commit de69506c090f530f34d78478db39898bd4e2cbba: I think the test introduction should be squashed into the following commit, or do you want to demonstrate something here first?"
  },
  {
   "t": "2023-11-25T23:01:29Z",
   "kind": "review",
   "who": "sedited",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "373b0bb077296624b7aeb5108673ae8f8b00b20d",
   "text": "Concept ACK.\n\nDone with my first pass, still want to think some of the approaches here over a bit."
  },
  {
   "t": "2023-11-26T16:08:46Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "Since the constructor and `Start` are always called in succession in this patch set, you could make `ThreadPool` more RAII styled if the `Start` method were removed and instead placed in the constructor. Could also make sense to do the same with the destructor and `Stop`."
  },
  {
   "t": "2024-02-05T15:22:24Z",
   "kind": "review",
   "who": "furszy",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "373b0bb077296624b7aeb5108673ae8f8b00b20d",
   "text": "Focus is on #28955, which contains a good number of commits decoupled from this PR.\nWill come back here after it."
  },
  {
   "t": "2024-03-20T22:07:21Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "aa0da5a82a8e9910cad6c8e5e861bea0f4d5941d"
  },
  {
   "t": "2024-03-21T17:22:34Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "fd79112a1cec4cf993200bd89e6fd19957bb608e"
  },
  {
   "t": "2024-03-21T17:44:10Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Running it again, let's see how quick it is...\n\nSince I might set `indexworkers=32` in my config file, which is great for the initial sync and if it needs to do a big catchup. But does it cause much overhead when it's up to date? Maybe the threads should spin down if there's not much work.\n\nWhat happens when there are multiple `readBlockFromDisk` calls around the same time? I don't see any (obvious) thread locking happening in `CAutoFile`. Since I keep block files on a spinning disk (`-blocksdir`), I wonder if that potentially slows things down - compared to fetching one file in a single uninterrupted operation.\n\nSo far (block 200K) my spinning disk is making a ton of noise and CPU activity is negligible.\n\n---\n\nIt took 4 hours and 23 minutes. That's an improvement over the 5 hours 46 minutes without: https://github.com/bitcoin/bitcoin/pull/28955#issuecomment-1961511162\n\nNote that last year I tested with SSD - which resulted in a 16x improvement https://github.com/bitcoin/bitcoin/pull/26966#issuecomment-1420800151. This time I used a spinning disk. CPU activity was negligible all the way."
  },
  {
   "t": "2024-03-21T17:54:05Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nYeah sure. The thread pool can be destructed once all indexes initial sync finish (once the index is synced, it starts receiving blocks through the validation signals and does not use the initial sync workers anymore).\nI'm currently tackling theCharlatan's feedback, and thinking about some improvements. Will add this change too on the next push."
  },
  {
   "t": "2024-03-22T18:03:01Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/blockfilterindex.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1404328365,
   "text": "hmm sorry, I missed to add it on #28955."
  },
  {
   "t": "2024-03-22T18:03:06Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1404459012,
   "text": "done as suggested. Thanks."
  },
  {
   "t": "2024-03-22T18:03:21Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1404460151,
   "text": "done as suggested. Thanks."
  },
  {
   "t": "2024-03-22T18:19:25Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 1404471253,
   "text": "done as suggested. Thanks."
  },
  {
   "t": "2024-03-22T18:19:43Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1404663869,
   "text": "Hmm, it shouldn't be that hard, let me see."
  },
  {
   "t": "2024-03-22T18:27:51Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1404664671,
   "text": "[quoted text omitted]\n\nBecause I initially thought on pushing extra work into the thread pool queue so then the originator thread, once it finishes calculating the different tasks, can take the workload on its active wait. But I ended up implementing it differently to re-use the same piece of code for the single-thread approach (Can check it looking for where it says \"// Otherwise, this is an active-wait, so we process blocks until all workers finish.\")\nStill, I think that having a method to process tasks manually make sense for a general thread pool implementation. But could remove it if you are strong on it."
  },
  {
   "t": "2024-03-22T18:37:07Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 1404871876,
   "text": "[quoted text omitted]\n\nI probably wasn't aware of `TraceThread` when this was implemented. Changing it.. Thanks!"
  },
  {
   "t": "2024-03-22T18:55:02Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 1404873978,
   "text": "[quoted text omitted]\n\nWhat about creating a fuzzing test instead? I'm not sure about the benefits of adding more tasks here. I can only foresee other developers complaining about the increased unit test times."
  },
  {
   "t": "2024-03-23T11:48:22Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1404874302,
   "text": "Sure. Thats the Gauss sum :)."
  },
  {
   "t": "2024-03-23T12:45:00Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1405161198,
   "text": "sure"
  },
  {
   "t": "2024-03-23T12:53:22Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1405168955,
   "text": "[quoted text omitted]\n\nSure."
  },
  {
   "t": "2024-03-23T12:54:01Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1405180029,
   "text": "Done as suggested."
  },
  {
   "t": "2024-03-23T12:55:44Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1405180661,
   "text": "[quoted text omitted]\n\nWhat do you mean? Set `workers_count` first, then `work_chunk` always in the same order?"
  },
  {
   "t": "2024-03-23T12:56:39Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1405250581,
   "text": "Sure. Done as suggested. Thanks."
  },
  {
   "t": "2024-03-23T13:01:30Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/blockfilter_index_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 1405253107,
   "text": "[quoted text omitted]\n\nhmm, will squash them. I don't remember why I split them (2 years ago)."
  },
  {
   "t": "2024-03-23T13:08:13Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "eff91c8656cd49b5b626925f5ec281b06f4eba28"
  },
  {
   "t": "2024-03-23T13:34:19Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Thanks for the in-depth review theCharlatan! Most comments were tackled. And thanks for testing Sjors!\n\nNow that we've reached this point (after #28955 merge), I'm rethinking and polishing the design. I'm not totally convinced about the current implementation anymore. We've grown a lot since this was implemented two years ago.\nOther than that, Sjors had a nice idea that I want to try out (or at least design this in a way so that the idea can be implemented in isolation in the future) --> Instead of dividing the work based on block ranges, it can be divided based on block file ranges. This would minimize the needle movement on spinning disks."
  },
  {
   "t": "2024-03-25T09:06:58Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nIt's possible that this can be achieved with block ranges too. But you have to make sure only one block range is read at any given time. I.e. the other threads should wait while a disk read is in progress. I suspect the problem lies in having 32 threads trying to read different things at the same time, and then operating system goes and fetches a few kilobytes, a few kilobytes there, etc. Though I haven't measured this."
  },
  {
   "t": "2024-04-07T17:30:09Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "57e37aecfdacb6f911dd57c4c1fb680a7660633d"
  },
  {
   "t": "2024-05-01T14:44:51Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "4ceaf6253eada351a20a82ba69a3aee636726527"
  },
  {
   "t": "2024-06-26T14:57:36Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "bfc8dce122474ddf6e5814731fa816cbdf1f7c7b"
  },
  {
   "t": "2024-08-14T19:32:24Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "e798711ab0f3dd973ab359f54c6f79581d79e2c3"
  },
  {
   "t": "2024-08-14T19:33:02Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "513fbc6b1fae951ea292bb3c8a32d104773995f0"
  },
  {
   "t": "2024-10-01T19:12:52Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "81dcee9464fc0f2b4a4fe547f312b71b405010b2"
  },
  {
   "t": "2024-10-23T14:54:39Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "If we receive an interrupt, we don't also want to wait for the work queue to be empty right?"
  },
  {
   "t": "2024-10-23T15:02:40Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "in_reply_to": null,
   "text": "Can we modify this to be more generic and return a type? And add logic to collect all returned values into a shared vector which can then be atomically swapped out by an observer? Possibly not in this PR, but if this will be split out into a generic thread pool."
  },
  {
   "t": "2024-10-26T13:00:24Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1404664671,
   "text": "This would make sense if we wanted to reuse this for CCheckQueue. Then, we could just loop and `ProcessTask` on the main thread once when we call `Wait`."
  },
  {
   "t": "2024-10-27T16:52:55Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1812965559,
   "text": "I tried this and it causes memory errors, since the remaining futures will have a dangling ref to `m_condition`."
  },
  {
   "t": "2024-11-18T15:37:35Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "tidy doesn't like this\n```suggestion\n    ThreadPool() = default;\n```"
  },
  {
   "t": "2024-11-18T18:11:52Z",
   "kind": "comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "text": "I'm using the ThreadPool here in https://github.com/bitcoin/bitcoin/pull/31132 as a cherry-picked commit, modulo changing `ThreadPool() {}` to `ThreadPool() = default;`. Perhaps we could pull this out to a separate PR since it would be useful for both changes.\n\nOne request for the ThreadPool would be to track in flight tasks being executed. That way we could write tests that ensure that all tasks have been completed before continuing, even if we don't have access to the futures."
  },
  {
   "t": "2025-02-25T18:57:49Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "349b09983d994cb46faeed12b123ae2269c6c516"
  },
  {
   "t": "2025-02-26T22:37:20Z",
   "kind": "review_comment",
   "who": "yancyribbens",
   "assoc": "CONTRIBUTOR",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "```suggestion\n    // 2) Maintain all busy threads except one.\n```"
  },
  {
   "t": "2025-04-09T17:52:51Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"index: remove CBlockIndex access from node internals\" (c4723fb9857c624ac0e1e034dc6c29d7821f5b4a)\n\nIt seems like this should be returning, not breaking. Would be good to change or clarify with a comment"
  },
  {
   "t": "2025-04-09T18:24:10Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"index: remove CBlockIndex access from node internals\" (c4723fb9857c624ac0e1e034dc6c29d7821f5b4a)\n\nCoud add another `// error logged internally` comment here since it now looks like this failure is being ignored."
  },
  {
   "t": "2025-04-09T18:26:21Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/index/blockfilterindex.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"index: remove CBlockIndex access from node internals\" (c4723fb9857c624ac0e1e034dc6c29d7821f5b4a)\n\nI'm not sure this is safe. It looks like if block height is 0 this is taking a reference to a temporary object that will go out scope.\n\nIn any case I think this could be simplified to `const CBlockUndo& block_undo{*Assert(block.undo_data)}` because it looks like pointer will be set above even if height is 0."
  },
  {
   "t": "2025-04-09T19:52:39Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"util: introduce general purpose thread pool\" (a382501d798c02a4dc042bc0641ce7b99c2db40f)\n\nI don't think it makes sense for `m_interrupt` to use the `CThreadInterrupt` class because the ThreadPool class already has it's own mutex and condition variable and it would be wasteful to introduce more. I think could just replace `CThreadInterrupt` with bool here, and replace `m_interrupt();` with `m_interrupt = true` and replace `m_interrupt.reset()` with `m_interrupt = false` while holding the mutex."
  },
  {
   "t": "2025-04-09T19:56:20Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/sync.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"util: introduce general purpose thread pool\" (a382501d798c02a4dc042bc0641ce7b99c2db40f)\n\nThis seems ok, but it's is only used one place where `WITH_REVERSE_LOCK` is not much simpler than plain `REVERSE_LOCK`. Could consider dropping it."
  },
  {
   "t": "2025-04-09T19:58:14Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1812965559,
   "text": "re: https://github.com/bitcoin/bitcoin/pull/26966#discussion_r1818140297\n\nIn commit \"util: introduce general purpose thread pool\" (a382501d798c02a4dc042bc0641ce7b99c2db40f)\n\n[quoted text omitted]\nTo be clear, you tried dropping the `m_work_queue.empty()` condition and that didn't work, so current code is correct? If so we could probably mark this thread resolved (if I am not misinterpreting)."
  },
  {
   "t": "2025-04-09T20:08:34Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"util: introduce general purpose thread pool\" (a382501d798c02a4dc042bc0641ce7b99c2db40f)\n\nNot important, but would suggest simplifying naming and just calling these members:\n\n```c++\nMutex m_mutex;\nstd::condition_variable m_cv;\n```\n\nThe `cs_` prefix is an older convention that comes from windows code, and there is as long as this class is going to have one mutex there isn't really a reason to use a more complicated name."
  },
  {
   "t": "2025-04-09T20:23:31Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"index: implement index parallel sync\" (bc0e5211e8a914e80e68e9c700b846a4cc3ef95b)\n\nThis doesn't seem like a great use of shared_ptr because makes the shutdown sequence more complicated than it needs to be. I think it would be clearer if instead of taking `std::shared_ptr<ThreadPool>` references they just used `ThreadPool&` and a new `std::unique_ptr<ThreadPool> m_index_threads` member was added to `NodeContext`. This way the threads could be stopped with an explicit `reset()` call instead of shutting down more unpredictably when the last index is destroyed."
  },
  {
   "t": "2025-04-09T20:56:12Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"index: implement index parallel sync\" (bc0e5211e8a914e80e68e9c700b846a4cc3ef95b)\n\nI found this hard to follow:\n\n```\nconst int max_blocks_to_sync = m_tasks_per_worker * m_thread_pool->WorkersCount() + m_tasks_per_worker; // extra 'm_tasks_per_worker' due the active-wait.\nwork_chunk = remaining_blocks > max_blocks_to_sync ? m_tasks_per_worker : remaining_blocks / (m_thread_pool->WorkersCount() + 1);\nworkers_count = m_thread_pool->WorkersCount();\n```\n\nwould suggest dropping the `max_blocks_to_sync` variable and simplifying to:\n\n```c++\nworkers_count = m_thread_pool->WorkersCount();\nwork_chunk = std::min(m_tasks_per_worker, remaining_blocks / (workers_count + 1));\n```"
  },
  {
   "t": "2025-04-09T20:58:36Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"index: implement index parallel sync\" (bc0e5211e8a914e80e68e9c700b846a4cc3ef95b)\n\nIMO, this would be clearer if it were called blocks_per_worker or blocks_per_chunk instead of tasks_per_worker. Not knowing that a task is a block made the code harder to understand when initially reading it."
  },
  {
   "t": "2025-04-09T21:24:01Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In commit \"index: implement index parallel sync\" (bc0e5211e8a914e80e68e9c700b846a4cc3ef95b)\n\nWould be really helpful to have a comment saying how this works at a high level. Would suggest something like \"If parallel sync is enabled, use `WorkersCount()+1` threads (including the current thread) to each process block ranges of up to `m_tasks_per_worker` blocks. The blocks in each range are processed in sequence by calling the index's `CustomProcessBlock` method which returns `std::any` values that are collected into vectors. As the threads finish their work, the `std::any` values are processed in order by calling the index's `CustomPostProcessBlocks` method, and the process repeats until no blocks are remaining to be processed and post-processed.\"\n\nI guess at a high level this seems reasonable, but perhaps too rigid. Like if there are 3 threads processing block ranges 1-10, 11-20, 21-30, and the first 2 threads finish while the third thread is slow. Why should the loop need to wait for the third thread before beginning to process blocks 31-50 and there are two idle threads doing nothing?\n\nIt seems like this idleness could be avoided by moving the `CustomPostProcessBlocks` calls into the worker threads. So that whenever each worker thread finishes processing blocks, it then opportunistically calls `CustomPostProcessBlocks` to post-process any blocks that are available (given the ordering constraint for post-processing). This way all the worker threads would continuously have work to do, and I suspect the resulting code might be simpler too since there would just be a single phase of work, not alternating Processing/PostProcessing phases."
  },
  {
   "t": "2025-04-09T21:50:38Z",
   "kind": "review",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "349b09983d994cb46faeed12b123ae2269c6c516",
   "text": "Approach ACK 349b09983d994cb46faeed12b123ae2269c6c516 and I reviewed most of the code. Seems like a nice design and good approach. Plan to finish reviewing later."
  },
  {
   "t": "2025-04-09T22:12:33Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1812965559,
   "text": "Err, I still think we would want to abandon the work queue if we are interrupted instead of waiting for it to finish, no? My naive approach of not waiting for the queue to be empty does not work though."
  },
  {
   "t": "2025-06-05T17:41:04Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2035856366,
   "text": "[quoted text omitted]\n\nyeah, good catch!.\nI think at the end it doesn't matter much because `ProcessBlock` aborts the node during a failure but still, this would have logged an extra line \"index enabled at height <height>\" during shutdown."
  },
  {
   "t": "2025-06-05T17:48:00Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2035900118,
   "text": "[quoted text omitted]\n\nDone as suggested."
  },
  {
   "t": "2025-06-05T17:49:43Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/blockfilterindex.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2035903040,
   "text": "[quoted text omitted]\n\nyeah, great. Done as suggested."
  },
  {
   "t": "2025-06-05T18:16:02Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2036050607,
   "text": "[quoted text omitted]\n\nsure, done as suggested. Thanks!"
  },
  {
   "t": "2025-06-05T18:22:01Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/sync.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2036055308,
   "text": "[quoted text omitted]\n\nSure. I think I did it this way to be very explicit about the code that will be executed without the lock, but yeah, we could achieve the same outcome with another set of brackets too."
  },
  {
   "t": "2025-06-05T18:23:19Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2036071200,
   "text": "[quoted text omitted]\n\nk sure. Done as suggested."
  },
  {
   "t": "2025-06-05T19:05:59Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2036091278,
   "text": "[quoted text omitted]\n\nSounds good. Done as suggested. Thanks!"
  },
  {
   "t": "2025-06-05T19:51:15Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2036131025,
   "text": "[quoted text omitted]\n\nGood idea. Done as suggested."
  },
  {
   "t": "2025-06-05T19:59:18Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2036133823,
   "text": "[quoted text omitted]\n\nsure. Done as suggested."
  },
  {
   "t": "2025-06-05T20:00:39Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "55f23cddc345a818191b86479a6328001d32dc26"
  },
  {
   "t": "2025-06-05T20:02:58Z",
   "kind": "review",
   "who": "furszy",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "55f23cddc345a818191b86479a6328001d32dc26",
   "text": "Thanks for the review, andrewtoth and ryanofsky!\nAddressed most of the suggestions, but not all yet. Will finish the rest soon."
  },
  {
   "t": "2025-06-05T23:16:12Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "1ccb9f6959e28aabd06e891cdf0b30ee99add2f3"
  },
  {
   "t": "2025-06-06T01:34:08Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "f3f5e4cb071497f7c9fccf5e81edc5c476333098"
  },
  {
   "t": "2025-06-06T21:04:37Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Decoupled part of this work inside #32694 - combining it with part of #24230."
  },
  {
   "t": "2025-06-13T00:20:55Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "53bc8b9663fd51fa43eb568aee8a38936cc7c6e8"
  },
  {
   "t": "2025-06-20T16:42:23Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "69ac2c05e04ff66428426d00fb2db4672d85726c"
  },
  {
   "t": "2025-06-23T16:36:05Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1846807424,
   "text": "done as suggested"
  },
  {
   "t": "2025-06-23T19:08:20Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "d14fa644acf03919a74909b52916fd4dff4aa88e"
  },
  {
   "t": "2025-06-23T19:10:31Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2036163105,
   "text": "[quoted text omitted]\n\nSure done!\n\n[quoted text omitted]\nSpent a few days implementing this suggestion. It started out small but turned into a larger change than I initially expected. That said, I liked the direction and felt it was worth the extra effort. The process runs faster now.\nLet me know what you think.\n\nDesign-wise, I kept everything within the `Sync` method, but could also encapsulate it into a separate class if preferred."
  },
  {
   "t": "2025-06-23T19:14:01Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "f4041cb91ff965fa08ac36ddb130f67e2d1671b0"
  },
  {
   "t": "2025-06-23T19:15:09Z",
   "kind": "review",
   "who": "furszy",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "d14fa644acf03919a74909b52916fd4dff4aa88e",
   "text": "Updated based on the feedback. Thanks!\nI believe I\u2019ve addressed all the comments, but let me know if I missed anything."
  },
  {
   "t": "2025-06-23T21:59:44Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "a13b48d67622659529fabdec6e2c96e31aa86263"
  },
  {
   "t": "2025-06-24T02:54:14Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "6acaa7919d2fdfdecb8c626e6f9fe3447f5fd11b"
  },
  {
   "t": "2025-06-24T13:58:02Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "5fea91eae012a062b540b02fffb872e81b584eaa"
  },
  {
   "t": "2025-06-24T21:12:30Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "a3e7d97b89821239bbba3e2c434a9a6405911865"
  },
  {
   "t": "2025-06-25T14:12:23Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "e4bdca5f74bf504882a6bc517192c426d4ad2cf8"
  },
  {
   "t": "2025-06-25T14:14:07Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "d405976ed05ec4b99b18151ae5b1021a53531be0"
  },
  {
   "t": "2025-06-25T14:14:58Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2166085260,
   "text": "Done. Please stop bullying my poor English :p"
  },
  {
   "t": "2025-06-25T16:15:15Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "59984a1d31aca2a523a8e8aafb3cb26500d8e990"
  },
  {
   "t": "2025-06-26T13:42:23Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "ef3e47d4623bcfacdfe87f556520fcb42d3f43e5"
  },
  {
   "t": "2025-06-26T15:08:05Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "c1b55be50f7c6dffde95d927b5d89e437b13ac79"
  },
  {
   "t": "2025-06-26T17:53:08Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "89f5fc80c2957c89a6f42f5bde11c9fb0f9abc3a"
  },
  {
   "t": "2025-06-26T21:05:50Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "f6b7da2493488c0e48ad9acf569ef3bdaf6b07b3"
  },
  {
   "t": "2025-06-27T19:14:13Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 1812965559,
   "text": "Hmm, sorry for the very very late response @andrewtoth. I missed this message completely.\n\n[quoted text omitted]\nYeah, I don\u2019t think that\u2019s safe. Other threads might be waiting on the tasks' futures to complete, so exiting without notifying them would leave them blocked forever.\nWhat we could do (and I think you mentioned this elsewhere) is keep track of all the tasks' promises internally and fail them with an interruption error/exception if the thread pool gets interrupted. In this way we also avoid lingering objects."
  },
  {
   "t": "2025-06-27T19:19:30Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "in_reply_to": 1812986201,
   "text": "[quoted text omitted]\n\nWe could keep track of the task futures' promises, if that's what you're referring to. In other words, this void() function is just a wrapper that executes a generic function which sets the result inside the caller\u2019s future."
  },
  {
   "t": "2025-07-08T02:28:38Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "e8e1e1471d08a8c9f22d3f0a68bab732649c9a52"
  },
  {
   "t": "2025-07-14T17:28:11Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Sorry, #32948 probably caused some conflicts."
  },
  {
   "t": "2025-07-14T21:31:35Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "cd55c0317a69aae8fb69c0467b3582381e41b259"
  },
  {
   "t": "2025-07-14T21:32:20Z",
   "kind": "review",
   "who": "furszy",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "cd55c0317a69aae8fb69c0467b3582381e41b259",
   "text": "[quoted text omitted]\n\nAuch. Rebased."
  },
  {
   "t": "2025-07-15T13:11:48Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "f1b4dd18bbc4232177be31bfdd373975887c9187"
  },
  {
   "t": "2025-07-15T17:36:20Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "2a6161adf0a92a64aac391deb9146e23ec99e47d"
  },
  {
   "t": "2025-07-17T08:48:50Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "I tried to use this to boost the silent payment indexer, but I don't know what I'm doing :-) https://github.com/Sjors/bitcoin/pull/96"
  },
  {
   "t": "2025-07-17T13:48:06Z",
   "kind": "review",
   "who": "furszy",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "2a6161adf0a92a64aac391deb9146e23ec99e47d",
   "text": "[quoted text omitted]\n\n@Sjors, see https://github.com/furszy/bitcoin-core/commits/2025_bip352_blind_fix.\nNote: I only spent a few minutes with it and the test seem to pass (I'm partially afk these days). Let me know how it goes and could check it in detail next week.\nThe first commit there (413ce51bf10326a6c56bd4250f9a9f19fde44ed4) is merely a code improvement + cleanup, because you don't need to re-read the undo data inside the child class anymore since #32694.\nAnd the last commit (b8883fa1fd76fbf3c3d3c10f702bea24abb42dc9) enables it by overriding `CustomProcessBlock()` (same as it was implemented for the tx index parallelization e8add40fbfbda9955f9b1cf998732ca03787eb72)."
  },
  {
   "t": "2025-07-21T20:44:15Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In \"util: introduce general purpose thread pool\" 7d984c3b2dca085ef7f49d21568b06c7b89c7807\n\nHmm I can imagine that this can be blocking when you want to stop instantly; but I guess there is a reason why you did not return here and then empty the work queue in Stop.\n```suggestion\n                // If stopped and no work left, exit worker.\n```"
  },
  {
   "t": "2025-07-21T20:45:29Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In \"util: introduce general purpose thread pool\" 7d984c3b2dca085ef7f49d21568b06c7b89c7807\n\nI think this and other verbose comment can be removed, it is quite obvious.\n\nWhat might need comment is Submit due to the abstraction there."
  },
  {
   "t": "2025-07-21T20:52:17Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "In \"util: introduce general purpose thread pool\" 7d984c3b2dca085ef7f49d21568b06c7b89c7807\n\nAlso verify that no work queue size is 0."
  },
  {
   "t": "2025-07-21T20:55:41Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In \" init: provide thread pool to indexes \" 82fa9d29653b445118fc2d03e2ced520a5d4c7dc\n\nThis message should be more verbose users should know the limit"
  },
  {
   "t": "2025-07-21T20:56:19Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "In \" init: provide thread pool to indexes \" 82fa9d29653b445118fc2d03e2ced520a5d4c7dc\n\nDefine the max and then use it here and in checking the bounds"
  },
  {
   "t": "2025-07-21T20:56:46Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "In \" init: provide thread pool to indexes \" 82fa9d29653b445118fc2d03e2ced520a5d4c7dc\n\nWhy 0?"
  },
  {
   "t": "2025-07-21T21:03:51Z",
   "kind": "review",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "2a6161adf0a92a64aac391deb9146e23ec99e47d",
   "text": "Concept ACK, did a quick pass through this.\n\nWill do in-depth review soon\n\n[quoted text omitted]\nWhat is the step to reproduce your result?\n\nSide note to self could be useful to try benchmarking this using [benchkit](https://github.com/bitcoin-dev-tools/benchkit) so that anyone can reproduce using the yaml config?"
  },
  {
   "t": "2025-07-22T21:06:31Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2220282853,
   "text": "[quoted text omitted]\n\nYes. We need to fulfill all promises so there are no dangling futures waiting for the worker to finish executing the task.\nIn the future, we could avoid this by tracking all the promises and triggering a \"shutdown\" exception during stop."
  },
  {
   "t": "2025-07-22T21:12:43Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2220319189,
   "text": "[quoted text omitted]\n\nParallel sync is disabled by default. We\u2019re currently not tracking the number of threads spawned by Core, so I chose not to make assumptions here (don't want indexes threads competing with net/validation ones). It\u2019s safer to let users specify the appropriate number for their setup. In the future, we could improve this by adding thread tracking object/mechanism and picking up the best number for their machine."
  },
  {
   "t": "2025-07-22T21:54:49Z",
   "kind": "review_comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2220319189,
   "text": "Makes sense, I think we can make the bounds not just an arbitrary number but tied it to the cores of the machine.\n\nThat will prevent footgun whereby user will spawn more threads than the machine can handle leading to degraded performance due to lots of context switching."
  },
  {
   "t": "2025-07-23T17:58:24Z",
   "kind": "review",
   "who": "furszy",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "2a6161adf0a92a64aac391deb9146e23ec99e47d",
   "text": "[quoted text omitted]\n\n1. Sync your node without any index.\n2. Restart the node with block filter or txindex enabled and let it run (you could also set `-connect=0` to sync only the index, without running the net/validation threads. Since threads won't be competing for `cs_main`, this will give you a more accurate result).\n\nYou\u2019ll see a \"[index name] is enabled at height [height]\" log entry once it finishes. Then it\u2019s just a matter of subtracting the index startup time from the \"index synced\" log time.\n\nUnfortunately, there\u2019s no \"stop at block\" option like we have for chain sync, so this process is a bit more manual and has some variances depending on where/how you run it. But you will see an overall significant speedup anyway.\n\nNote: I should update my results in the PR description. Those were compiled three years ago on a small VPS, and we've introduced many changes since then.\n\n[quoted text omitted]\nThat would be nice. I'm not sure it supports stopping after a specific log is written. Maybe @willcl-ark could enlighten us here."
  },
  {
   "t": "2025-07-23T18:00:08Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2220319189,
   "text": "Absolutely"
  },
  {
   "t": "2025-07-23T18:10:51Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2220317691,
   "text": "[quoted text omitted]\n\nSince the maximum number of threads for indexes should depend on how many threads Core has at runtime and the number of available processors, I don't think hardcoding it here is the best approach. I agree with adding it on the error side: https://github.com/bitcoin/bitcoin/pull/26966#discussion_r2220315413"
  },
  {
   "t": "2025-07-23T18:12:06Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2220304297,
   "text": "[quoted text omitted]\n\nsure. Done."
  },
  {
   "t": "2025-07-23T18:17:21Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2220285854,
   "text": "Done"
  },
  {
   "t": "2025-07-23T18:17:52Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "068537ae61309a61e3bc350dc747d75cf2207517"
  },
  {
   "t": "2025-07-23T18:18:23Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2220315413,
   "text": "done as suggested"
  },
  {
   "t": "2025-07-24T15:12:00Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "d2138bef76ae7c25679571f9c07ecb44e99d4ecc\n\nWhy not just `+1` in each task like you do in the next test block?"
  },
  {
   "t": "2025-07-24T15:14:02Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "d2138bef76ae7c25679571f9c07ecb44e99d4ecc\n\nwould there be any benefit here to incrementing the counter at the end of each blocking task and check that they executed properly when unblocked?"
  },
  {
   "t": "2025-07-24T15:21:37Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "d2138bef76ae7c25679571f9c07ecb44e99d4ecc\n\nis this sleep to give tasks a chance to execute if the blocking breaks?"
  },
  {
   "t": "2025-07-24T15:22:23Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "d2138bef76ae7c25679571f9c07ecb44e99d4ecc\n\nCould anything be gained by checking the counter after each `ProcessTask()`?"
  },
  {
   "t": "2025-07-24T15:42:31Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "b36010261921adc74e61bdcbc91ba6b7778bad9a\n\n\"must be in-between 1 and...\" but you allow `0`\n\nalso nit, could drop the \"in-\" and say \"number between 1 and ...\""
  },
  {
   "t": "2025-07-24T15:43:43Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2220317691,
   "text": "b36010261921adc74e61bdcbc91ba6b7778bad9a\n\nWonder if it should be specified that threadpool is **shared** among all indexers (that support multithreading)"
  },
  {
   "t": "2025-07-24T16:35:48Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "in_reply_to": null,
   "text": "76aa1c2da9635b99e2fb8792b24b53f320b90624\n\nWhats the benefit of defining this as a lambda instead of just moving the code inside `func_worker`? It doesn't seem to be called anywhere else ...?"
  },
  {
   "t": "2025-07-24T19:09:53Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "76aa1c2da9635b99e2fb8792b24b53f320b90624\n\nDid you mean `CustomPostProcessBlocks()` here?"
  },
  {
   "t": "2025-07-25T17:11:10Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/index/blockfilterindex.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "77a14b37aaf5b1c1a5dbe28e900a3824e5732f3f\n\nThe corresponding line in the serial/legacy processing path asserts the block data (I don't care much about that) but I do think you should keep the variable for the filter type if ever one day a new filter index is added:\n\n```cpp\nBlockFilter filter(m_filter_type, *Assert(block.data), *Assert(block.undo_data));\n```"
  },
  {
   "t": "2025-07-25T17:15:28Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "831dec831572249f838344d099cb38ab9555dc91",
   "in_reply_to": null,
   "text": "76aa1c2da9635b99e2fb8792b24b53f320b90624\n\nI wonder if this condition should be a bit more attention-getting, since it *should* never execute right? Either log something or `Assume()` for the benefit of future index developers?"
  },
  {
   "t": "2025-07-25T17:19:22Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "d2138bef76ae7c25679571f9c07ecb44e99d4ecc\n\nI'd like to see `ThreadPool` reused in the codebase (for example, as http workers) which makes me think the class should also have a custom name property for logging and process monitoring. (e.g. `index_worker_thread_1` and `http_worker_thread_1`)"
  },
  {
   "t": "2025-07-25T20:00:40Z",
   "kind": "review",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "068537ae61309a61e3bc350dc747d75cf2207517",
   "text": "code review ACK 068537ae61309a61e3bc350dc747d75cf2207517\n\nBuilt and tested on macos/arm64 as well as Debian/x86. Left several questions and suggestions. I really like ThreadPool as a util and look forward to using it for http workers as well, to share the code.\n\nI'm currently rebuilding block filter and tx indexes with this branch to compare against master, which after 48 hours was only about 70% complete...\n\nShow Signature\n\n```\n-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\nACK 068537ae61309a61e3bc350dc747d75cf2207517\n-----BEGIN PGP SIGNATURE-----\n\niQIzBAEBCAAdFiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmiD4qAACgkQ5+KYS2KJ\nyToG8g//d72j0Pq/TmDjOA0nbCrqrBIKLIvHiTD8DIBCvCb2Ul+NnZDfvFAbuR9s\nnkArmxEUPRPBSkKBYMqJHUt+JF4zsLyttkryJW1vFCHwLi49an82B2MKSNNyOfs+\nPUufS9ro5FNDNax66jVdjD1/CrNRYAt/AQ/K3FSo7FNG5dbpO2n09ZBXWAHqwZfU\n3bf7p3Ug0JBlEe7/JMz1Wbu7wDV9E0lINarr/n5dnVQZTBLHaCvabSYtrEix/BRG\nku2MjexrbZdR5PY1xKQvJYkOkndZDkLQVJMC9BT9GeCYBdGRaGXadZQ3AwNlzobz\nJVSMQO2Ngv/Ow8IQWrAs705Moqzu680PjdodxTSrj0QU/D4cNJAW390QzwT7evrw\npAVipwM4oomc4ZMfWX4pq8AwlZ+GywEIMa34UbO0pbpGnlIEUPRUBb7l7ODzcCcb\n/cp9l9xpXwWH8+1yY9uRUnh9AHRlVsNdTJeCq3kTU8W1CYcmCOaImwjMJU3tM9Aj\nvgFzVQam4KYJTD+WjhKkFAb7M0uTF6LqaM5ChfSaTR1zXuVQ7OX0sYM8R0B2QLJ8\nuxNzNF/KnTJZYaOc0i/b4JHP3MZWgoO3GcT64woj1GqHGjLj2YOAKYSfpNZfWaL+\nkdwRnzC4qgMKunoqtv4sX9Ht0Hk7qyDfPnEog/AT4wnSbiP/HZs=\n=VG2A\n-----END PGP SIGNATURE-----\n```\n\npinheadmz's public key is [on openpgp.org](https://keys.openpgp.org/vks/v1/by-fingerprint/E61773CD6E01040E2F1BD78CE7E2984B6289C93A)"
  },
  {
   "t": "2025-07-25T20:46:23Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2228814663,
   "text": "[quoted text omitted]\n\nHmm, good question. I was probably not only testing that all tasks were executed, but also that each of them was executed only once (if they were all doing the same, it would be hard to know if they were all executed). Or.. maybe I just wanted to mention Gauss somewhere in our code.\nI did this one 3 years ago so.. it is hard to know. But it doesn't hurt to have it."
  },
  {
   "t": "2025-07-25T20:56:38Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2228819659,
   "text": "[quoted text omitted]\n\nIt's subtle but we're doing the same with the wait here. The `blocking_tasks` vector contains the futures whose promises are set only when the worker finishes executing the task (meaning it has run all its code)."
  },
  {
   "t": "2025-07-25T21:01:45Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2228839630,
   "text": "[quoted text omitted]\n\nGood eye.\nIIRC, I added it to wait until the workers actually get blocked. Otherwise, the thread pool queue size would be greater than the expected value (because it did not consumed the blocking tasks).\nStill, we could remove this by adding some `ready_promises`, as did in test 2. A bit more code but less fragile."
  },
  {
   "t": "2025-07-25T21:05:26Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2228841403,
   "text": "[quoted text omitted]\n\ndon't think so. Could maybe improve it by making the task return something and checking the futures' promises. But I'm not totally convinced it will add much value."
  },
  {
   "t": "2025-07-25T21:06:09Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2228891058,
   "text": "Good catch!, fixed."
  },
  {
   "t": "2025-07-25T21:12:05Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "in_reply_to": 2229009128,
   "text": "[quoted text omitted]\n\nIt was like that at first, but I found it harder to follow and wanted to simplify `func_worker` as much as possible.\nYou can try moving it back in and will surely see what I'm referring to."
  },
  {
   "t": "2025-07-25T21:16:21Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/blockfilterindex.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2231639166,
   "text": "good catch!, fixed."
  },
  {
   "t": "2025-07-25T21:25:41Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "831dec831572249f838344d099cb38ab9555dc91",
   "in_reply_to": 2231646277,
   "text": "[quoted text omitted]\n\nHmm, or we could remove this line and call `CustomAppend` by default. Then it would be up to the child index to decide whether it needs sequential ordering during parallel sync or not. This way, the tx and BIP352 indexes would require fewer lines of code."
  },
  {
   "t": "2025-07-25T21:35:18Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "9bba5ef0cc5b6890eba2be3a6ed429c4fea5a28f"
  },
  {
   "t": "2025-07-25T21:35:30Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2231653627,
   "text": "done!"
  },
  {
   "t": "2025-07-25T21:36:31Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2229349665,
   "text": "yes, fixed."
  },
  {
   "t": "2025-07-27T11:01:26Z",
   "kind": "comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "text": "Did a rough benchmark test. Froze a full node at height `906551` by restarting with `-noconnect` and also `-txindex -blockfilterindex`. First test was from master, after 48 hours I aborted the process after reaching only:\n\n```\n$ bitcoin-cli getindexinfo\n{\n  \"txindex\": {\n    \"synced\": false,\n    \"best_block_height\": 770289\n  },\n  \"basic block filter index\": {\n    \"synced\": true,\n    \"best_block_height\": 906551\n  }\n}\n```\n\nRunning this PR (restarting with empty `indexes/`), both indexing operations were complete after about 16 hours:\n```\n2025-07-25T18:12:46Z txindex thread start\n2025-07-25T18:12:46Z basic block filter index thread start\n...\n2025-07-25T19:48:16Z basic block filter index thread exit\n2025-07-26T10:14:21Z txindex thread exit\n\n```\n\n```\n{\n  \"txindex\": {\n    \"synced\": true,\n    \"best_block_height\": 906551\n  },\n  \"basic block filter index\": {\n    \"synced\": true,\n    \"best_block_height\": 906551\n  }\n}\n\n```"
  },
  {
   "t": "2025-07-29T14:50:53Z",
   "kind": "review_comment",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "This is bike shedding now so ok to ignore -- but when we set thread names on Linux they are truncated to 15 characters. So I dunno I guess \"thread\" and \"worker\" are redundant in the name of a thread?\n\nhttps://github.com/bitcoin/bitcoin/blob/2f410ad78c767e37f083d03114f6661b73647af3/src/util/threadnames.cpp#L22-L27"
  },
  {
   "t": "2025-07-29T14:53:00Z",
   "kind": "review",
   "who": "pinheadmz",
   "assoc": "MEMBER",
   "state": "APPROVED",
   "commit": "9bba5ef0cc5b6890eba2be3a6ed429c4fea5a28f",
   "text": "re-ACK 9bba5ef0cc5b6890eba2be3a6ed429c4fea5a28f\n\nChanges since last review are minimal responses to my own review suggestions. Built on macos/arm64 and debian/x86. Ran functional and unit tests, ran with `-indexworkers=16` on mainnet fullnode with >900000 blocks\n\nShow Signature\n\n```\n-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\nACK 9bba5ef0cc5b6890eba2be3a6ed429c4fea5a28f\n-----BEGIN PGP SIGNATURE-----\n\niQIzBAEBCAAdFiEE5hdzzW4BBA4vG9eM5+KYS2KJyToFAmiI394ACgkQ5+KYS2KJ\nyTruCxAAwQ1Q4syFsBPG0fS1KaKSV7LKLl5oJNzr5RPP7w/NliMB3KDXI8lhQnhr\nej6NNs7ovm05O99QrjFomGyg5oYO+rcECPoXp5lrwsOQBWJk10ZNgDIKdh2ChWr0\nsn2o8DCsGpHfkXZ9qg8ynStt5ScCv/1bopb2jvaFjWHdy/5LREJ62XuJZoah5O74\nkSSWyj1Nxi9oVNKctXifFp0WwqOqofft2kgDWghRPw67SERuZGqcyzb89zKLPLxr\niyMIWd8LY36isVj7XZBNVz1+jQnr77ldR15Uar/CKlrEoNC/tKs/NBE0eXAHrKhs\n4iuKdtX470yazbKGAm6Ei2wQbYa4w5319QDa6pVYlqo30ZMlW/AvVMC//Uc1/v3a\nfCW2ootvbbzz0lBPXg5yIB8wvaCuDkYYjMGL6tnYg1T4ChlS7P2y7IbfWryAjOKt\nUN1kJNkMoXzSrHu2nZaDmIv3hFpQBaDUH0BlT+unUg8hUioE1VqpIw/+rmcCj7eA\nIhKJ0NnJZnh92y5Y+9b+lf+ix6TiF0ZqGT/h+5Yv7Vj8YVXP006HpKvpx2Y84ii+\n5IZag6OviHoKt056Gn2iixQFSbw6hVMOw8JnF5FC3iZR28roEkJlY2WtOUF6h/Se\nUJTC1VMQuxRm+AIYLwC8w1RCRMoZDRKlOKBjOY5beFCIOARef3w=\n=Zxbl\n-----END PGP SIGNATURE-----\n```\n\npinheadmz's public key is [on openpgp.org](https://keys.openpgp.org/vks/v1/by-fingerprint/E61773CD6E01040E2F1BD78CE7E2984B6289C93A)"
  },
  {
   "t": "2025-07-29T15:41:50Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2220317691,
   "text": "[quoted text omitted]\n\nDone as suggested."
  },
  {
   "t": "2025-08-07T22:43:19Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "b349fbafadbc9b5af0a35b4c282e142994a5a1d7"
  },
  {
   "t": "2025-08-08T01:37:32Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "647f6f0f4432a113a66bb3a423afe1ba68a842c9"
  },
  {
   "t": "2025-08-08T02:33:06Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "f6a52c6686b4d14aea2b05ca0413ad664bcf72e6"
  },
  {
   "t": "2025-08-12T11:19:15Z",
   "kind": "comment",
   "who": "ismaelsadeeq",
   "assoc": "MEMBER",
   "text": "Fwiw I encountered a segfault previously while testing on master on previous PR HEAD https://github.com/bitcoin/bitcoin/commit/9bba5ef0cc5b6890eba2be3a6ed429c4fea5a28f\n\nI ran the node on mainnet with `-noconnect` `-txindex`  `-blockfilterindex` `-indexworkers=4`\n![Screenshot 2025-08-05 at 09 42 43](https://github.com/user-attachments/assets/313b7ebc-3d8f-49ce-a42a-3a757366a085)\n\nAlthough I was not able to later reproduce the crash it seems to be happen intermittently cc @furszy"
  },
  {
   "t": "2025-08-12T20:25:14Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "49efddfae7e46637be2c2efc3938ef1a0fecb1e3"
  },
  {
   "t": "2025-08-12T20:34:46Z",
   "kind": "review",
   "who": "furszy",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "49efddfae7e46637be2c2efc3938ef1a0fecb1e3",
   "text": "[quoted text omitted]\n\nThanks for the report! Check it now if you can. There was a very subtle bug on which the worker threads might have accessed an index `Sync()` local variable post-destruction."
  },
  {
   "t": "2025-08-20T09:26:20Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "69792b6d3ee4b7320c608d5b23056d3e8b998247"
  },
  {
   "t": "2025-08-30T17:46:04Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "84fec2c2e1b43783dc4049e2cb69ebd9035083a1"
  },
  {
   "t": "2025-08-30T17:47:16Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "3d123f76428b745f57c77519b07ebce442f82c06"
  },
  {
   "t": "2025-09-01T13:17:19Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "7fccd9fbae85d5a1d0109128ef37c641ba32287e"
  },
  {
   "t": "2025-09-01T13:20:28Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2240120761,
   "text": "[quoted text omitted]\n\nSure, done. Now the previous \"indexes_threadpool_worker_[num]\" will be \"indexes_pool_[num]\"."
  },
  {
   "t": "2025-09-16T15:19:22Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 1404873978,
   "text": "I made a small fuzz test now. If you think it is useful, and want to add it:\n\n```c++\n// Copyright (c) 2025-present The Bitcoin Core developers\n// Distributed under the MIT software license, see the accompanying\n// file COPYING or http://www.opensource.org/licenses/mit-license.php.\n\n#include <util/threadpool.h>\n\n#include <test/fuzz/FuzzedDataProvider.h>\n#include <test/fuzz/fuzz.h>\n\nnamespace {\n\nstruct ExpectedException : std::runtime_error {\n    using std::runtime_error::runtime_error;\n};\n\nstruct MaybeThrowTask {\n    bool m_should_throw{false};\n\n    explicit MaybeThrowTask(const bool should_throw) : m_should_throw{should_throw}\n    {\n    }\n\n    void operator()() const\n    {\n        if (m_should_throw) throw ExpectedException(\"fail\");\n    }\n};\n\nstruct CounterTask {\n    std::atomic_uint32_t& m_counter;\n\n    explicit CounterTask(std::atomic_uint32_t& counter) : m_counter{counter}\n    {\n    }\n\n    void operator()() const\n    {\n        m_counter.fetch_add(1);\n    }\n};\n\n} // namespace\n\nFUZZ_TARGET(threadpool)\n{\n    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());\n\n    const uint32_t num_tasks = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, 1024);\n    const uint32_t num_workers = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 16);\n    ThreadPool pool{\"fuzz_pool\"};\n\n    std::atomic_uint32_t task_counter{0};\n    uint32_t expected_task_counter{0};\n    std::vector<std::future<void>> futures;\n    futures.reserve(num_tasks);\n    pool.Start(num_workers);\n    assert(pool.WorkersCount() == num_workers);\n    assert(pool.WorkQueueSize() == 0);\n\n    for (uint32_t i = 0; i < num_tasks; ++i) {\n        if (fuzzed_data_provider.ConsumeBool()) {\n            futures.emplace_back(pool.Submit(MaybeThrowTask{fuzzed_data_provider.ConsumeBool()}));\n        } else {\n            futures.emplace_back(pool.Submit(CounterTask{task_counter}));\n            ++expected_task_counter;\n        }\n        if (fuzzed_data_provider.ConsumeBool()) {\n            try {\n                futures.back().get();\n            } catch (const ExpectedException&) {}\n            futures.pop_back();\n        }\n    }\n\n    while (!futures.empty()) {\n        for (size_t i{0}; i < futures.size();) {\n            if (futures[i].wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) {\n                try {\n                    futures[i].get();\n                } catch (const ExpectedException&) {}\n                futures[i] = std::move(futures.back());\n                futures.pop_back();\n            } else {\n                ++i;\n            }\n        }\n    }\n\n    assert(pool.WorkQueueSize() == 0);\n    assert(task_counter == expected_task_counter);\n}\n```"
  },
  {
   "t": "2025-09-16T20:15:33Z",
   "kind": "review_comment",
   "who": "sedited",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 1404873978,
   "text": "It seems to have found something:\n```\n#234857\tREDUCE cov: 862 ft: 5138 corp: 265/28Kb lim: 1689 exec/s: 303 rss: 854Mb L: 1178/1640 MS: 4 ChangeByte-EraseBytes-InsertByte-InsertByte-\n#236845\tNEW    cov: 862 ft: 5140 corp: 266/29Kb lim: 1699 exec/s: 303 rss: 854Mb L: 1652/1652 MS: 3 CopyPart-CMP-CopyPart- DE: \"N10__cxxabiv\"-\n#237681\tREDUCE cov: 862 ft: 5140 corp: 266/29Kb lim: 1699 exec/s: 303 rss: 855Mb L: 23/1652 MS: 2 EraseBytes-PersAutoDict- DE: \"St9exception\"-\nALARM: working on the last Unit for 1608 seconds\n       and the timeout value is 1200 (use -timeout=N to change)\nMS: 4 CopyPart-CrossOver-ChangeByte-CopyPart-; base unit: 1dc3473815a9593c855cd52b02ae3f2ef0914d77\n0xf,0xd6,0xf,0x8,\n\\017\\326\\017\\010\nartifact_prefix='./'; Test unit written to ./timeout-8968fff41e23f31c0e704cd01b7706b86fccdc0b\nBase64: D9YPCA==\n==1785321== ERROR: libFuzzer: timeout after 1608 seconds\n    #0 0x5c3b117e9d65 in __sanitizer_print_stack_trace (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1c24d65) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #1 0x5c3b1174387c in fuzzer::PrintStackTrace() (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1b7e87c) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #2 0x5c3b1172987b in fuzzer::Fuzzer::AlarmCallback() (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1b6487b) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #3 0x7e01d464532f  (/lib/x86_64-linux-gnu/libc.so.6+0x4532f) (BuildId: 282c2c16e7b6600b0b22ea0c99010d2795752b5f)\n    #4 0x7e01d4698d70 in __futex_abstimed_wait_common64 nptl/futex-internal.c:57:12\n    #5 0x7e01d4698d70 in __futex_abstimed_wait_common nptl/futex-internal.c:87:9\n    #6 0x7e01d4698d70 in __GI___futex_abstimed_wait_cancelable64 nptl/futex-internal.c:139:10\n    #7 0x7e01d469e7a2 in __pthread_clockjoin_ex nptl/pthread_join_common.c:102:14\n    #8 0x5c3b117c4c91 in pthread_join (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1bffc91) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #9 0x7e01d4cece32 in __gthread_join /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/include/x86_64-linux-gnu/bits/gthr-default.h:682:33\n    #10 0x7e01d4cece32 in std::thread::join() /build/gcc-14-ig5ci0/gcc-14-14.2.0/build/x86_64-linux-gnu/libstdc++-v3/src/c++11/../../../../../src/libstdc++-v3/src/c++11/thread.cc:134:27\n    #11 0x5c3b11ecc4cf in ThreadPool::Stop() /home/drgrid/bitcoin/build_fuzz/src/test/fuzz/./util/threadpool.h:87:20\n    #12 0x5c3b11ec5b69 in ThreadPool::~ThreadPool() /home/drgrid/bitcoin/build_fuzz/src/test/fuzz/./util/threadpool.h:67:9\n    #13 0x5c3b11ec38b5 in threadpool_fuzz_target(std::span<unsigned char const, 18446744073709551615ul>) /home/drgrid/bitcoin/build_fuzz/src/test/fuzz/./test/fuzz/threadpool.cpp:91:1\n    #14 0x5c3b1217d77e in std::function<void (std::span<unsigned char const, 18446744073709551615ul>)>::operator()(std::span<unsigned char const, 18446744073709551615ul>) const /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/bits/std_function.h:591:9\n    #15 0x5c3b1217d77e in test_one_input(std::span<unsigned char const, 18446744073709551615ul>) /home/drgrid/bitcoin/build_fuzz/src/test/fuzz/util/./test/fuzz/fuzz.cpp:88:5\n    #16 0x5c3b1217d77e in LLVMFuzzerTestOneInput /home/drgrid/bitcoin/build_fuzz/src/test/fuzz/util/./test/fuzz/fuzz.cpp:216:5\n    #17 0x5c3b1172aed4 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1b65ed4) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #18 0x5c3b1172a5c9 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool, bool*) (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1b655c9) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #19 0x5c3b1172bdb5 in fuzzer::Fuzzer::MutateAndTestOne() (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1b66db5) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #20 0x5c3b1172c915 in fuzzer::Fuzzer::Loop(std::vector<fuzzer::SizedFile, std::allocator<fuzzer::SizedFile>>&) (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1b67915) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #21 0x5c3b11719bef in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1b54bef) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #22 0x5c3b11744276 in main (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1b7f276) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n    #23 0x7e01d462a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16\n    #24 0x7e01d462a28a in __libc_start_main csu/../csu/libc-start.c:360:3\n    #25 0x5c3b1170ebd4 in _start (/home/drgrid/bitcoin/build_fuzz/bin/fuzz+0x1b49bd4) (BuildId: f39378c068291e70b8b8401725aeb1fb5f64a63c)\n\nSUMMARY: libFuzzer: timeout\n```"
  },
  {
   "t": "2025-09-16T21:02:54Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 1404873978,
   "text": "Very nice!\nPlayed a bit with the code, pushed some (dirty and untested) extensions here: https://github.com/furszy/bitcoin-core/commits/2022_parallelize_blockfilter_index_2_fuzz/\n\n[quoted text omitted]\nwill check it out. Thanks!. I was having fun with the code first :)."
  },
  {
   "t": "2025-09-17T11:29:44Z",
   "kind": "review_comment",
   "who": "brunoerg",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 1404873978,
   "text": "@TheCharlatan I tried to run this target and got 0 exec/s which is pretty bad. I was expecting a bad performance due to the number of tasks and the pool overhead btw."
  },
  {
   "t": "2025-09-17T20:09:37Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "46c6bfe5485bd28f76ff23c4a92f70f2a972d681"
  },
  {
   "t": "2025-09-17T20:43:05Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "8eafb7b2bca9680e4467cd9122042c5d84a85522"
  },
  {
   "t": "2025-09-18T01:31:33Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad"
  },
  {
   "t": "2025-10-06T17:09:00Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "If we `wait()` or `get()` on the returned future, does that imply a release/acquire memory ordering or a relaxed memory ordering? I can't seem to find out what this means for other non-atomic memory that was written on the worker thread before the task is completed."
  },
  {
   "t": "2025-10-06T19:01:45Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2407510165,
   "text": "[quoted text omitted]\n\nFor the returned value, `wait()`/`get()` provide release/acquire semantics. All updates performed by the task should be visible after `get()` returns.\nNow, if the task modifies other objects that might be accessed concurrently by threads that don\u2019t wait on the future, I'm pretty sure you need to explicitly synchronize access to those objects.\n\nMaybe show an example of how you\u2019re using it, and we could reason about it."
  },
  {
   "t": "2025-10-06T19:10:40Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2407510165,
   "text": "Sure, so I tried updating #31132 to use the thread pool here. It makes the change simpler if we were to already have this thread pool merged. However, the threads write to non-synchronized shared vectors, which the main thread will read. We need to make sure the write happens before the read. I initially stored the futures and wait on them like this https://github.com/andrewtoth/bitcoin/commit/c256f1b457cbd5b900aa34703eb5853d2449bcde#diff-3eff97d24109f473292b38a6495c5b51e8bda9f4bff9b691cf255968ba80d85dR151-R164. Since there is no happens before guarantee though, I think we still need a completion semaphore that each thread releases instead."
  },
  {
   "t": "2025-10-06T19:22:45Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2407510165,
   "text": "[quoted text omitted]\n\nHmm so in that case, then the diff I linked above is correct? The main thread is the one waiting on the future, and the non-synchronized vector is modified only by the worker thread. So the change to the vector should be visible on the main thread.\n\n[quoted text omitted]\nSo you mean not for the returned value, but for any thread that calls `get()/wait()`? Having the return value be visible is sufficient with just a relaxed memory ordering.\n\nSo I'm confused whether other memory modified by the thread will be visible (release/acquire), or just the returned value (relaxed)."
  },
  {
   "t": "2025-10-06T20:22:02Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2407510165,
   "text": "[quoted text omitted]\n\nHave you tried something like (haven't tried it yet, will give it a run tomorrow):\n\n```diff\ndiff --git a/src/inputfetcher.h b/src/inputfetcher.h\n--- a/src/inputfetcher.h\t(revision c256f1b457cbd5b900aa34703eb5853d2449bcde)\n+++ b/src/inputfetcher.h\t(date 1759781954442)\n@@ -43,15 +43,6 @@\n      */\n     std::atomic<size_t> m_input_counter{0};\n\n-    /**\n-     * The vector of vectors of outpoint:coin pairs.\n-     * Each thread writes the coins it fetches to the vector at its thread\n-     * index. This way multiple threads can write concurrently to different\n-     * vectors in a thread safe way. After all threads are finished, the main\n-     * thread can loop through all vectors and write the coins to the cache.\n-     */\n-    std::vector<std::vector<std::pair<COutPoint, Coin>>> m_coins{};\n-\n     /**\n      * The set of txids of all txs in the block being fetched.\n      * This is used to filter out inputs that are created in the block,\n@@ -69,15 +60,15 @@\n     const size_t m_worker_thread_count;\n     ThreadPool m_thread_pool{\"inputfetcher\"};\n\n-    void Work(size_t thread_index) noexcept\n+    std::vector<std::pair<COutPoint, Coin>> Work(size_t thread_index) noexcept\n     {\n         const auto inputs_count{m_inputs.size()};\n-        auto& coins{m_coins[thread_index]};\n+        std::vector<std::pair<COutPoint, Coin>> coins;\n         try {\n             while (true) {\n                 const auto input_index{m_input_counter.fetch_add(1, std::memory_order_relaxed)};\n                 if (input_index >= inputs_count) {\n-                    return;\n+                    return {};\n                 }\n                 const auto [tx_index, vin_index] = m_inputs[input_index];\n                 const auto& outpoint{m_block->vtx[tx_index]->vin[vin_index].prevout};\n@@ -96,7 +87,7 @@\n                     // Missing an input. This block will fail validation.\n                     // Skip remaining inputs.\n                     m_input_counter.store(inputs_count, std::memory_order_relaxed);\n-                    return;\n+                    return {};\n                 }\n             }\n         } catch (const std::runtime_error&) {\n@@ -104,6 +95,8 @@\n             // Skip remaining inputs.\n             m_input_counter.store(inputs_count, std::memory_order_relaxed);\n         }\n+\n+        return coins;\n     }\n\n public:\n@@ -116,10 +109,6 @@\n             return;\n         }\n         m_thread_pool.Start(worker_thread_count);\n-        m_coins.reserve(worker_thread_count + 1);\n-        for (size_t n{0}; n < worker_thread_count + 1; ++n) {\n-            m_coins.emplace_back();\n-        }\n     }\n\n     //! Fetch all block inputs from db, and insert into cache.\n@@ -148,25 +137,30 @@\n\n         // Set the input counter and wake threads.\n         m_input_counter.store(0, std::memory_order_relaxed);\n-        std::vector<std::future<void>> futures;\n+        std::vector<std::future<std::vector<std::pair<COutPoint, Coin>>>> futures;\n         futures.reserve(m_worker_thread_count);\n         for (size_t n{0}; n < m_worker_thread_count; ++n) {\n             futures.emplace_back(m_thread_pool.Submit([this, n]() {\n-                Work(n);\n+                return Work(n);\n             }));\n         }\n\n         // Have the main thread work too while we wait for other threads\n-        Work(m_worker_thread_count);\n+        std::vector<std::vector<std::pair<COutPoint, Coin>>> coins;\n+        coins.reserve(m_worker_thread_count + 1);\n+        coins[m_worker_thread_count] = Work(m_worker_thread_count);\n\n         // Wait for all worker threads to complete\n-        for (const auto& future : futures) {\n-            future.wait();\n+        for (size_t i = 0; i < futures.size(); i++) {\n+            coins[i] = futures[i].get();\n         }\n\n         // At this point all threads are done writing to m_coins, so we can\n         // safely read from it and insert the fetched coins into the cache.\n-        for (auto& thread_coins : m_coins) {\n+        for (auto& thread_coins : coins) {\n+            if (thread_coins.empty()) {\n+                // TODO: report failure..\n+            }\n             for (auto&& [outpoint, coin] : thread_coins) {\n                 cache.EmplaceCoinInternalDANGER(std::move(outpoint),\n                                                 std::move(coin),\n\n```"
  },
  {
   "t": "2025-10-06T20:24:03Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2407510165,
   "text": "Nice!"
  },
  {
   "t": "2025-10-06T21:58:04Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2407510165,
   "text": "Let me know how it goes :)"
  },
  {
   "t": "2025-10-07T12:32:16Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2407510165,
   "text": "It went well thanks https://github.com/bitcoin/bitcoin/commit/0030dc5ba46da402d36edd5cb32492397deeae7f\nSorry for hijacking the PR :)"
  },
  {
   "t": "2025-10-07T13:56:20Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2407510165,
   "text": "[quoted text omitted]\n\nhehe np, I owe you a few reviews anyway."
  },
  {
   "t": "2025-10-07T16:11:56Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": null,
   "text": "Any reason we can't use `memory_order_relaxed` on loads and stores of `m_interrupt`?"
  },
  {
   "t": "2025-10-07T20:17:18Z",
   "kind": "review_comment",
   "who": "mzumsande",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": null,
   "text": "I tested this for signet on two computers, and while parallel sync (5 threads) was a great speedup on an SSD, I also observed a slowdown on a HDD compared to master (by a factor 2).\nPresumably that's because reading the blocks from disk is the main bottleneck on a HDD, and with parallel indexing there is a lot of jumping back and forth, increasing seek time.\nShould it be mentioned in the `-indexworkers` help that it is not advisable to use this option on a HDD?"
  },
  {
   "t": "2025-10-07T20:18:10Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": 2411161631,
   "text": "[quoted text omitted]\n\nI recall briefly thinking about it, but going down that path would mean guarding all `m_interrupt` accesses with `m_mutex` too. And that seemed like an unnecessary overhead for methods like `Submit` that should be performing a lightweight interruption check before submission."
  },
  {
   "t": "2025-10-07T20:28:58Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": 2411161631,
   "text": "[quoted text omitted]\n\nHmm I don't see why we would need to do that? If we wanted to make m_interrupt a non-atomic bool we would need to do that. But if it's atomic we can use relaxed since it's just a flag and not acting as a fence to any other non-atomic memory?"
  },
  {
   "t": "2025-10-07T21:00:35Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": 2411161631,
   "text": "[quoted text omitted]\n\nHmm, but what about the flag updates visibility?\n\nIf all `m_interrupt` loads were relaxed, `Submit()` could read a stale false value even after `Stop()` set it to true, right?.\nThis would cause the task to be enqueued during shutdown, which could end up with a lingering task that never gets executed, which will hang the caller on the future's `get()`/`wait()` call forever and stall the shutdown procedure?"
  },
  {
   "t": "2025-10-07T21:07:50Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95"
  },
  {
   "t": "2025-10-07T21:08:08Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "9dbea51ab511fc9823a87cd522c1d705a3436bad",
   "in_reply_to": 2411794397,
   "text": "Updated, thanks for testing!"
  },
  {
   "t": "2025-10-07T21:16:01Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": 2411161631,
   "text": "I don't see how memory ordering solves that? Consider this exact line where the `Submit` thread is suspended:\n```\n    auto Submit(T task) -> std::future<decltype(task())>\n    {\n        if (m_workers.empty() || m_interrupt.load()) throw std::runtime_error(\"No active workers; cannot accept new tasks\");\n        ---> Stop() is called right here on another thread, setting m_interrupt to true and all threads exit before this thread continues execution\n        using TaskType = std::packaged_task<decltype(task())()>;\n```\n\nI think we need to say instead that all public methods must be called on the same thread. That way we can use `relaxed` ordering, since `Submit` and `Stop` cannot be called by different threads so this won't happen."
  },
  {
   "t": "2025-10-07T22:40:49Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": 2411161631,
   "text": "[quoted text omitted]\n\nI don't think it could? Once a write to true becomes visible to a thread, it cannot be read again as false until something writes false to it again. This is independent of memory ordering. Relaxed memory ordering can reorder when writes to different data become visible to a thread. For instance:\n```\natomic<bool> x{false}, y{false};\n// thread 1 stores true to x first then y\nx.store(true, relaxed);\ny.store(true, relaxed);\n// thread 2\ny.load(relaxed); // Could be true\nx.load(relaxed); // Could still be false even if y above is already visible as true\ny.load(relaxed); // Cannot be false again after already reading true above\n```\n\nOf course this is only relevant if there are other threads that will be reading and writing. Since the same thread will be calling Stop, Start, and Submit, there is no need to be concerned with this.\nThe only reason m_interrupt needs to be atomic is because it is also being read from worker threads, and those are reading it while synchronized with `m_mutex`."
  },
  {
   "t": "2025-10-07T23:28:19Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": 2411161631,
   "text": "Thinking about this more, since `m_workers` is not synchronized it implies `Stop` and `Submit` must be called on the same thread. Therefore `m_workers.empty()` is sufficient to test if the pool is stopped since `Stop()` must complete before a subsequent `Submit()`. So `m_interrupt` is redundant in `if (m_workers.empty() || m_interrupt.load())`, and `m_interrupt` can just be made a regular bool guarded by `m_mutex`. `Start` would have to set it with the mutex locked, but that is infrequent compared to all the loads done in `Submit` and `WorkerThread`."
  },
  {
   "t": "2025-10-08T11:16:35Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "https://github.com/bitcoin/bitcoin/pull/26966/commits/0afb05cad29e4c30a2f9eb4295e997967129dc88\n\nI wrote a test for recursive task submission. You can add it if you consider it to be useful.\n```diff\ndiff --git a/src/test/threadpool_tests.cpp b/src/test/threadpool_tests.cpp\nindex ed0af8fa3f..4797144b78 100644\n--- a/src/test/threadpool_tests.cpp\n+++ b/src/test/threadpool_tests.cpp\n@@ -197,6 +197,21 @@ BOOST_AUTO_TEST_CASE(threadpool_basic)\n         blocker.set_value();\n         threadPool.Stop();\n     }\n+\n+    // Test case 7, recursive submission of tasks.\n+    {\n+        ThreadPool threadPool(POOL_NAME);\n+        threadPool.Start(NUM_WORKERS_DEFAULT);\n+\n+        std::promise<void> signal;\n+        threadPool.Submit([&]() {\n+            threadPool.Submit([&]() {\n+                signal.set_value();\n+            });\n+        });\n+\n+        signal.get_future().wait();\n+    }\n }\n\n BOOST_AUTO_TEST_SUITE_END()\n```\n\nEDIT\n```\n + signal.get_future().wait();\n + threadPool.Stop()\n```"
  },
  {
   "t": "2025-10-08T11:50:25Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2413513497,
   "text": "This would require `m_workers` to be synchronized somehow, since it is written in `Stop` and `Start` but read in `Submit`.\n\nEdit: actually this will work, since `m_workers` won't be written until after the signal inside the last task. But, if `Stop` was called manually before waiting for the signal it could cause a deadlock."
  },
  {
   "t": "2025-10-08T13:02:05Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2413513497,
   "text": "Is there value in making the `ThreadPool` safe for recursive task submission? We might not be able to implement certain divide and conquer algorithms, but we might not need to."
  },
  {
   "t": "2025-10-08T13:23:33Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2413513497,
   "text": "I don't think so. In that case, should your test have some warnings that this can be unsafe if the thread pool is stopped or destroyed before all calls to `Submit` have returned?"
  },
  {
   "t": "2025-10-08T13:34:29Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2413513497,
   "text": "We can modify the test to make the parent task wait for the child task to complete before returning. `Stop` waits for all workers to complete before clearing the workers vector,  that is, of course, if @furszy  determines that there is any point to a recursive test at all, since we are not designing for that"
  },
  {
   "t": "2025-10-08T13:40:05Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "https://github.com/bitcoin/bitcoin/pull/26966/commits/0afb05cad29e4c30a2f9eb4295e997967129dc88:\n\nIs it not beneficial for a generic ThreadPool to support assigning tasks in batches to threads? I expect this to be useful when submitting many small tasks."
  },
  {
   "t": "2025-10-08T14:42:42Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "5a992bce07ea1f038bfaa18aa6f774e7d179075a"
  },
  {
   "t": "2025-10-08T15:18:26Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": 2411161631,
   "text": "Nice thread of thoughts! Will try to go over the comments.\n\nRegarding `Submit` being called only from the initial controller thread:\n\nRight now, we are actually submitting tasks from threads that are not the initial one. Each index starts its own \"background sync\" thread that is responsible for computing the tasks submitted to the thread pool.\n\nI don\u2019t think it\u2019s reasonable to expect a generic thread pool to only accept submissions from a single thread. We must be able to submit tasks from any thread. I\u2019ve adapted the code to reflect this.\n\nRegarding the `Start` and `Stop` restriction to the initial controller thread:\n\nAgree. In fact, calling `Stop` from a worker thread would actually deadlock. I\u2019ve added documentation clearly expressing this requirement.\nCould extend this and check the thread id during shutdown too but it seemed like an overkill.\n\nRegarding `Submit` after shutdown:\n\nYeah, good eye there. I think the simplest solution is to move the interruption check inside the mutex guard, just before enqueuing the task. This is not really going to happen anyway. And even if it happens, the extra overhead of creating the wrapper and destructing it is minimal since the app will be shutting down anyway.\n\nThanks for the thread of thoughts! I have updated the class to reflect all of this."
  },
  {
   "t": "2025-10-08T15:22:49Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2413902914,
   "text": "[quoted text omitted]\n\nI don't think that's thread pool responsibility. Generic thread pools are designed to execute tasks efficiently, but they don't dictate their granularity (mainly because they do not know the content of the task). If batching is beneficial, the caller should handle it before submission (that's actually what I'm doing here for the indexes)."
  },
  {
   "t": "2025-10-08T15:24:11Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": 2411161631,
   "text": "Ok, but now we also need `std::vector<std::thread> m_workers;` guarded by `m_mutex`. Since it can be written by `Stop` and also read in `Submit`. So it also needs to be locked at the end of `Stop` and in `WorkersCount`."
  },
  {
   "t": "2025-10-08T15:39:32Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "202b7cc81a71444297dc3cfb4be461cac995d944"
  },
  {
   "t": "2025-10-08T15:52:13Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "9270fdded923ddea7e89f72fed5184fecb462efa"
  },
  {
   "t": "2025-10-08T15:53:06Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "671bcec1608ae2d74483d7b56bf7ecd770dd3b95",
   "in_reply_to": 2411161631,
   "text": "[quoted text omitted]\n\nTrue. Pushed. Thanks!"
  },
  {
   "t": "2025-10-08T15:57:58Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9270fdded923ddea7e89f72fed5184fecb462efa",
   "in_reply_to": null,
   "text": "This comment is stale now."
  },
  {
   "t": "2025-10-08T15:59:11Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2413513497,
   "text": "Yeah, I found the test worthwhile. Taken. Thanks for sharing! I had something similar on the fuzz test.\nAllowing task submission from any thread should be a must for me. It otherwise defeats the purpose of having a shared thread pool if you can only submit tasks from a single spot.\n\nThis also pushed me further. Added another test for the case where the pool is about to shut down while workers are all busy and a thread is concurrently submitting a new task."
  },
  {
   "t": "2025-10-08T16:04:41Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "3c4c3cbe09061088d2649b6bfadacaeed49a6ed6"
  },
  {
   "t": "2025-10-08T16:06:53Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "9270fdded923ddea7e89f72fed5184fecb462efa",
   "in_reply_to": 2414346916,
   "text": "Ups, updated. Thanks.\nAlso moved the condition variable comment above the member declaration (this caused a nasty bug that we shouldn't forget)."
  },
  {
   "t": "2025-10-08T16:07:02Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "These 2 checks are redundant since they are both set atomically. Should just pick one for clarity."
  },
  {
   "t": "2025-10-08T16:10:42Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2414369729,
   "text": "Actually, we must use worker count check. Since we can Start with 0 worker threads but still set interrupt to false, so this would deadlock with only the interrupt check.\nMaybe guard against starting with 0 threads and then pick either?"
  },
  {
   "t": "2025-10-08T16:12:11Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2414369729,
   "text": "[quoted text omitted]\n\nNot really. What if the `ThreadPool` was just constructed but not started? Only using `m_interrupt` here would allow submission when there are no workers."
  },
  {
   "t": "2025-10-08T16:20:11Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2414369729,
   "text": "Right, m_interrupt should be initialized to true."
  },
  {
   "t": "2025-10-08T16:28:32Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2414369729,
   "text": "[quoted text omitted]\n\nI'm tempted to leave it as is, mainly because we might want to add `Interrupt()` and `Resume()` methods in the future that don't kill the worker threads. We might want to clear the queue due to an early failure in one of our tasks without having to recreate the threads."
  },
  {
   "t": "2025-10-08T20:12:58Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "234f65b6a733a96242381e657ae9978d22b49a8f"
  },
  {
   "t": "2025-10-08T20:38:45Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "f856b8780a90a636012c4137884a26b79cd646a9"
  },
  {
   "t": "2025-10-08T20:55:28Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "1abd4afe69920c9caca01eda3ab3964823f9ecd6"
  },
  {
   "t": "2025-10-08T21:37:44Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "80e63b71d828276bb18ec1eaa6038ff298db6f21"
  },
  {
   "t": "2025-10-09T18:26:09Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "6a81272c2e7e5852187cbaa2f5d9dd5c95dc2745"
  },
  {
   "t": "2025-10-09T18:32:51Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "da73d3dc6cec170e3c9e459d15960af196a124f9"
  },
  {
   "t": "2025-10-09T18:41:15Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Updated with a fuzz test case from @TheCharlatan. Thanks!"
  },
  {
   "t": "2025-10-10T17:13:33Z",
   "kind": "review",
   "who": "brunoerg",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "da73d3dc6cec170e3c9e459d15960af196a124f9",
   "text": "Left the fuzz target (`threadpool`) running overnight, performance is reasonable and didn't get any memory leak. I generated a coverage report at https://brunoerg.xyz/bitcoin-core-coverage/26966/coverage_report/."
  },
  {
   "t": "2025-10-10T17:35:18Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nBoth awesome news! Added report link to the PR description. Thanks!"
  },
  {
   "t": "2025-10-10T18:50:05Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095"
  },
  {
   "t": "2025-10-10T18:55:17Z",
   "kind": "review",
   "who": "furszy",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "text": "Rebased to get latest CI updates."
  },
  {
   "t": "2025-10-10T19:22:49Z",
   "kind": "review_comment",
   "who": "mzumsande",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "in_reply_to": null,
   "text": "I don't understand the need for the sanity cleanup. Shouldn't the logic from `WorkerThread` guarantee that `m_work_queue` is empty at this point, so that we could just assert that here?"
  },
  {
   "t": "2025-10-10T20:33:58Z",
   "kind": "review_comment",
   "who": "mzumsande",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "nit: could add Test case 0) in the description"
  },
  {
   "t": "2025-10-10T20:45:41Z",
   "kind": "review_comment",
   "who": "mzumsande",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "Is this meant to be used by tests only, or are there possible other use cases? If it's the latter, it might be helpful to return a bool that indicates whether a task was executed or not (empty queue)."
  },
  {
   "t": "2025-10-10T21:00:57Z",
   "kind": "review_comment",
   "who": "mzumsande",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "`-indexworkers=0` is allowed according to the doc, but it will trigger the assert `num_workers > 0` in the `ThreadPool`. Shouldn't create a `ThreadPool` in this case."
  },
  {
   "t": "2025-10-10T21:08:50Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "in_reply_to": null,
   "text": "In commit \"util: introduce general purpose thread pool\" (3943630a4cea56b040377b33476803745055510c)\n\nWould be nice to avoid shared_ptr here. Apparently it is needed because std::function objects are required to be copyable and packaged tasks aren't copyable. Could fix this by avoiding std::function which would also simplify the Submit() function:\n\ndiff\n\n```diff\n--- a/src/util/threadpool.h\n+++ b/src/util/threadpool.h\n@@ -47,7 +47,7 @@ class ThreadPool {\n private:\n     std::string m_name;\n     Mutex m_mutex;\n-    std::queue<std::function<void()>> m_work_queue GUARDED_BY(m_mutex);\n+    std::queue<std::packaged_task<void()>> m_work_queue GUARDED_BY(m_mutex);\n     std::condition_variable m_cv;\n     // Note: m_interrupt must be modified while holding the same mutex used by threads waiting on the condition variable.\n     // This ensures threads blocked on m_cv reliably observe the change and proceed correctly without missing signals.\n@@ -59,7 +59,7 @@ private:\n     {\n         WAIT_LOCK(m_mutex, wait_lock);\n         for (;;) {\n-            std::function<void()> task;\n+            std::packaged_task<void()> task;\n             {\n                 // Wait only if needed; avoid sleeping when a new task was submitted while we were processing another one.\n                 if (!m_interrupt && m_work_queue.empty()) {\n@@ -135,7 +135,7 @@ public:\n         {\n             // Sanity cleanup: release any std::function captured shared_ptrs\n             LOCK(m_mutex);\n-            std::queue<std::function<void()>> empty;\n+            std::queue<std::packaged_task<void()>> empty;\n             m_work_queue.swap(empty);\n         }\n         // Note: m_interrupt is left true until next Start()\n@@ -147,21 +147,17 @@ public:\n      * Enqueues a callable to be executed by one of the worker threads.\n      * Returns a `std::future` that can be used to retrieve the task\u2019s result.\n      */\n-    template<class T> EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)\n-    auto Submit(T task) -> std::future<decltype(task())>\n+    template<class F> EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)\n+    auto Submit(F&& fn)\n     {\n-        using TaskType = std::packaged_task<decltype(task())()>;\n-        auto ptr_task = std::make_shared<TaskType>(std::move(task));\n-        std::future<decltype(task())> future = ptr_task->get_future();\n+        std::packaged_task task{std::forward<F>(fn)};\n+        auto future{task.get_future()};\n         {\n             LOCK(m_mutex);\n             if (m_workers.empty() || m_interrupt) {\n                 throw std::runtime_error(\"No active workers; cannot accept new tasks\");\n             }\n-            m_work_queue.emplace([ptr_task]() mutable {\n-                (*ptr_task)();\n-                ptr_task.reset(); // Explicitly release packaged_task and the stored function obj.\n-            });\n+            m_work_queue.emplace(std::move(task));\n         }\n         m_cv.notify_one();\n         return future;\n@@ -173,7 +169,7 @@ public:\n      */\n     void ProcessTask() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)\n     {\n-        std::function<void()> task;\n+        std::packaged_task<void()> task;\n         {\n             LOCK(m_mutex);\n             if (m_work_queue.empty()) return;\n```"
  },
  {
   "t": "2025-10-10T21:29:17Z",
   "kind": "review_comment",
   "who": "ryanofsky",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "21479ee0d62f734a1aa2c431f4f0ccb289e89f2c",
   "in_reply_to": null,
   "text": "In commit \"util: introduce general purpose thread pool\" (3943630a4cea56b040377b33476803745055510c)\n\nI'm a little unclear what this swap is trying to do. I would expect the point of swapping into a temporary vector would be to destroy the callables without holding m_mutex (since they could own resources and take time to destroy). But it looks like the `empty` vector is destroyed while `m_mutex` is still locked, so that isn't happening. Also I don't understand why `m_mutex` is locked twice in this function with a notify in between. I would expect it to look more like:\n\n```c++\nvoid Stop()\n{\n    std::vector<std::thread> threads_to_join;\n    std::queue<std::function<void()>> callables;\n    LOCK(m_mutex);\n    m_interrupt = true;\n    threads_to_join.swap(m_workers);\n    callables.swap(m_work_queue);\n    m_cv.notify_all();\n}\n```"
  },
  {
   "t": "2025-10-10T21:35:28Z",
   "kind": "review",
   "who": "mzumsande",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "text": "Reviewed only the first two commits so far.\n\nIt could make sense to introduce the threadpool in an extra PR first, since it is very generic and can be used for multiple things, in case there are some reviewers who don't feel comfortable ACKing the pretty involved index changes, but would want to ACK the threadpool changes. (just a suggestion, not necessary just for my sake since I happen know the index code)"
  },
  {
   "t": "2025-10-11T15:14:05Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "in_reply_to": 2422090129,
   "text": "ha, that's awesome. The queue's `std::packaged_task<void()>` change broke my mind because the task has obviously a different return value..\nI see the trick now, it seems the `packaged_task<R()>` is wrapped into another `packaged_task<void()>` which forwards the call internally. That's elegant."
  },
  {
   "t": "2025-10-11T22:17:46Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "21479ee0d62f734a1aa2c431f4f0ccb289e89f2c",
   "in_reply_to": 2422124854,
   "text": "[quoted text omitted]\n\nThe idea behind the swap was more about ensuring the queue is empty after joining the threads, mainly to avoid any lingering future that would block callers indefinitely. That's why I used the \"sanity\" wording there.\nI did it after joining the threads because we\u2019re currently waiting for all pending tasks to complete before stopping the worker loop. If we cleaned the queue prior to joining, all futures would throw a `future_error` - which could be fine too, but we\u2019d probably want to throw something different, like a custom `TaskInterruptedException`, and adapt the submitter code to handle possible interruptions.\n\nA bit more context, I added this sanity swap after finding https://github.com/bitcoin/bitcoin/pull/26966#discussion_r2411949367. But it is not really necessary anymore with the latest changes. Could also just drop it.\n\n[quoted text omitted]\nThat's an overseen, yeah. Thanks!"
  },
  {
   "t": "2025-10-11T22:35:05Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "in_reply_to": 2421835077,
   "text": "[quoted text omitted]\n\nYeah, right now it is not really necessary. Have explained the context and the rationale here: https://github.com/bitcoin/bitcoin/pull/26966#discussion_r2423172535"
  },
  {
   "t": "2025-10-11T23:37:56Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/init.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2422078863,
   "text": "nice catch. Fixed."
  },
  {
   "t": "2025-10-12T02:30:11Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "nit: since it's strongly typed we don't need to include the type name in the variable anymore"
  },
  {
   "t": "2025-10-12T19:32:56Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "I think it might be clearer if this commit came later in the series, after the groundwork is established. In my experience, it's often easier to review when the problem definition is presented before the solution.\n\nAs it stands, this commit introduces code whose purpose isn't immediately clear without reading ahead to future commits.\n\nI'd like to echo the concern other reviewers raised about separating the thread pool from the script parallelization work. Since script parallelization is already quite complex, would you consider starting with a simpler 2-threaded implementation in this PR, and introducing the general-purpose thread pool in a separate PR?\n\nGiven the review bandwidth challenges this PR has faced over the years, breaking it into smaller, more focused pieces might help reviewers feel more confident providing ACKs."
  },
  {
   "t": "2025-10-12T19:45:54Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/index/txindex.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "based on my benchmarks on various platforms it seems this isn't speeding up txindex - was this measured by anyone?"
  },
  {
   "t": "2025-10-12T19:57:41Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "It seems to me we can loosen the args and avoid string concat:\n```suggestion\ntemplate <typename T>\nvoid WaitFor(std::span<const std::future<T>> futures, std::string_view context)\n{\n    for (size_t i = 0; i < futures.size(); ++i) {\n        if (futures[i].wait_for(TIMEOUT_SECS) != std::future_status::ready) {\n            throw std::runtime_error(strprintf(\"Timeout waiting for: %s, task index %d\", context, i));\n        }\n    }\n}\n```"
  },
  {
   "t": "2025-10-12T20:05:18Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": null,
   "text": "3087bd1ef1e9f85bf6a0b7c24be650f77a6c030a\n\nI'm finding this commit difficult to review in depth due to its complexity. Like before, I'm seeing the solution (out-of-order processing with task tracking and opportunistic post-processing) before experiencing the problem it solves.\n\nCould we break this down into focused, trivial commits, where low-risk changes are separated from high-risk ones and where the commits tell a story, where the pain is experienced before we propose a solution."
  },
  {
   "t": "2025-10-12T20:51:48Z",
   "kind": "review",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "state": "CHANGES_REQUESTED",
   "commit": "aa9cf5db0dce0e5982f69857070c9f2a30fcf095",
   "text": "I started a detailed review of this PR and I'm enthusiastic about the concept - it addresses a real problem and the potential performance gains are significant. \\\nStrong Concept ACK!\n\nHowever, I've encountered some concerns that make me hesitant to continue reviewing the current implementation in depth:\n* The txindex parallelization appears to be broken (showing 3-13% slowdowns rather than speedups), and this went unnoticed for years\n* The complexity of the changes makes thorough review very challenging\n* A shared thread pool introduces additional complexity when IO-bound and CPU-bound tasks compete for the same resources - this typically requires work-stealing mechanisms or separate pools to prevent CPU starvation.\n\n### Reproducer\n\nNot sure how other reviewers have reproduced these results, but I needed something reliable that I can run on different platforms with different numbers of threads and storage types.\nI have automated it to make it easier to replicate on different platforms, this is a variation of the script I used:\n```bash\nBEFORE=\"3943630a4cea56b040377b33476803745055510c\"; AFTER=\"aa9cf5db0dce0e5982f69857070c9f2a30fcf095\"; \\\nDATA_DIR=\"/mnt/my_storage/BitcoinData\"; export DATA_DIR; \\\nwait_index() { tail -F ${DATA_DIR}/debug.log | grep -q -m1 'is enabled at height'; killall bitcoind 2>/dev/null || true; sleep 10; }; export -f wait_index; \\\ngit reset --hard >/dev/null 2>&1 && git clean -fxd >/dev/null 2>&1 && git fetch origin $BEFORE $AFTER >/dev/null 2>&1; \\\nfor c in $BEFORE:build-before $AFTER:build-after; do \\\n  git checkout ${c%:*} >/dev/null 2>&1 && cmake -B ${c#*:} -G Ninja -DCMAKE_BUILD_TYPE=Release >/dev/null 2>&1 && ninja -C ${c#*:} bitcoind >/dev/null 2>&1; \\\ndone; \\\necho \"indexes | $(hostname) | $(uname -m) | $(lscpu | grep 'Model name' | head -1 | cut -d: -f2 | xargs) | $(nproc) cores | $(free -h | awk '/^Mem:/{print $2}') RAM | $(df -T $DATA_DIR | awk 'NR==2{print $2}') | $(lsblk -no ROTA $(df --output=source $DATA_DIR | tail -1) | grep -q 0 && echo SSD || echo HDD)\" && \\\nfor INDEX_FLAG in \"-txindex=1\" \"-blockfilterindex=1\"; do \\\n  hyperfine --runs 1 --shell bash --sort command \\\n    --prepare \"rm -rf ${DATA_DIR}/indexes/* ${DATA_DIR}/debug.log\" \\\n    \"./build-before/bin/bitcoind -datadir=${DATA_DIR} ${INDEX_FLAG} -connect=0 -printtoconsole=0                  & wait_index\" \\\n    \"./build-after/bin/bitcoind  -datadir=${DATA_DIR} ${INDEX_FLAG} -connect=0 -printtoconsole=0 -indexworkers=1  & wait_index\" \\\n    \"./build-after/bin/bitcoind  -datadir=${DATA_DIR} ${INDEX_FLAG} -connect=0 -printtoconsole=0 -indexworkers=2  & wait_index\" \\\n    \"./build-after/bin/bitcoind  -datadir=${DATA_DIR} ${INDEX_FLAG} -connect=0 -printtoconsole=0 -indexworkers=4  & wait_index\" \\\n    \"./build-after/bin/bitcoind  -datadir=${DATA_DIR} ${INDEX_FLAG} -connect=0 -printtoconsole=0 -indexworkers=8  & wait_index\" \\\n    \"./build-after/bin/bitcoind  -datadir=${DATA_DIR} ${INDEX_FLAG} -connect=0 -printtoconsole=0 -indexworkers=16 & wait_index\" \\\n    \"./build-after/bin/bitcoind  -datadir=${DATA_DIR} ${INDEX_FLAG} -connect=0 -printtoconsole=0 -indexworkers=32 & wait_index\";\\\ndone\n```\n\n### `txindex` parallelization seems broken\n\ntxindex 3-9% slower (i9-ssd | Intel i9-9900K | 16 cores | SSD)\n\n```\nBenchmark 1: ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0                  & wait_index\n  Time (abs \u2261):        6761.801 s               [User: 9011.877 s, System: 875.527 s]\n\nBenchmark 2: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=1  & wait_index\n  Time (abs \u2261):        6996.856 s               [User: 9344.333 s, System: 896.312 s]\n\nBenchmark 3: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=2  & wait_index\n  Time (abs \u2261):        7015.870 s               [User: 9530.219 s, System: 912.646 s]\n\nBenchmark 4: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=4  & wait_index\n  Time (abs \u2261):        7156.885 s               [User: 10203.442 s, System: 954.607 s]\n\nBenchmark 5: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=8  & wait_index\n  Time (abs \u2261):        7315.911 s               [User: 11774.143 s, System: 1047.173 s]\n\nBenchmark 6: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=16 & wait_index\n  Time (abs \u2261):        7325.947 s               [User: 13227.157 s, System: 1200.721 s]\n\nBenchmark 7: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=32 & wait_index\n  Time (abs \u2261):        7382.974 s               [User: 14253.246 s, System: 1989.118 s]\n\nRelative speed comparison\n        1.00          ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0                  & wait_index\n        1.03          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=1  & wait_index\n        1.04          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=2  & wait_index\n        1.06          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=4  & wait_index\n        1.08          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=8  & wait_index\n        1.08          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=16 & wait_index\n        1.09          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=32 & wait_index\n```\n\ntxindex 10-13% slower (rpi5-16-2 | ARM Cortex-A76 | 4 cores | SSD)\n\n```\nBenchmark 1: ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0                 & wait_index\n  Time (abs \u2261):        12433.186 s               [User: 14666.244 s, System: 2535.770 s]\n\nBenchmark 2: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=1 & wait_index\n  Time (abs \u2261):        13671.833 s               [User: 16723.879 s, System: 2939.177 s]\n\nBenchmark 3: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=2 & wait_index\n  Time (abs \u2261):        13738.200 s               [User: 17270.730 s, System: 2942.644 s]\n\nBenchmark 4: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=3 & wait_index\n  Time (abs \u2261):        13924.557 s               [User: 17881.093 s, System: 3034.775 s]\n\nBenchmark 5: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=4 & wait_index\n  Time (abs \u2261):        14023.831 s               [User: 18130.048 s, System: 3040.891 s]\n\nRelative speed comparison\n        1.00          ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0                 & wait_index\n        1.10          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=1 & wait_index\n        1.10          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=2 & wait_index\n        1.12          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=3 & wait_index\n        1.13          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=4 & wait_index\n```\n\ntxindex 11.6% faster at 2 workers, then degrades (i7-hdd | Intel i7-7700 | 8 cores | HDD)\n\n```\nBenchmark 1: ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0                  & wait_index\n  Time (abs \u2261):        22530.714 s               [User: 13506.955 s, System: 1931.403 s]\n\nBenchmark 2: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=2  & wait_index\n  Time (abs \u2261):        20188.026 s               [User: 13265.594 s, System: 1815.091 s]\n\nBenchmark 3: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=4  & wait_index\n  Time (abs \u2261):        21060.158 s               [User: 13379.147 s, System: 1862.493 s]\n\nBenchmark 4: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=8  & wait_index\n  Time (abs \u2261):        20894.177 s               [User: 13484.637 s, System: 1921.494 s]\n\nBenchmark 5: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=16 & wait_index\n  Time (abs \u2261):        20998.180 s               [User: 13956.983 s, System: 2132.438 s]\n\nRelative speed comparison\n        1.12          ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0                  & wait_index\n        1.00          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=2  & wait_index\n        1.04          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=4  & wait_index\n        1.03          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=8  & wait_index\n        1.04          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -txindex=1 -connect=0 -printtoconsole=0 -indexworkers=16 & wait_index\n\n```\n\n---\n\n### `blockfilterindex` scales beautifully even on an HDD and on an Rpi5\n\nblockfilterindex 6.4x faster (i9-ssd | Intel i9-9900K | 16 cores | SSD)\n\n```\nBenchmark 1: ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0                  & wait_index\n  Time (abs \u2261):        5883.572 s               [User: 5571.286 s, System: 162.350 s]\n\nBenchmark 2: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=1  & wait_index\n  Time (abs \u2261):        3179.316 s               [User: 5783.941 s, System: 189.363 s]\n\nBenchmark 3: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=2  & wait_index\n  Time (abs \u2261):        2223.222 s               [User: 6033.892 s, System: 199.876 s]\n\nBenchmark 4: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=4  & wait_index\n  Time (abs \u2261):        1497.150 s               [User: 6744.119 s, System: 228.107 s]\n\nBenchmark 5: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=8  & wait_index\n  Time (abs \u2261):        1069.091 s               [User: 8684.280 s, System: 286.993 s]\n\nBenchmark 6: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=16 & wait_index\n  Time (abs \u2261):        919.136 s               [User: 13555.771 s, System: 406.747 s]\n\nBenchmark 7: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=32 & wait_index\n  Time (abs \u2261):        923.283 s               [User: 13998.239 s, System: 405.510 s]\n\nRelative speed comparison\n        6.40          ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0                  & wait_index\n        3.46          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=1  & wait_index\n        2.42          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=2  & wait_index\n        1.63          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=4  & wait_index\n        1.16          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=8  & wait_index\n        1.00          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=16 & wait_index\n        1.00          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=32 & wait_index\n```\n\nblockfilterindex 2.23x faster (rpi5-16-2 | ARM Cortex-A76 | 4 cores | SSD)\n\n```\nBenchmark 1: ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0                 & wait_index\n  Time (abs \u2261):        9862.855 s               [User: 8111.196 s, System: 683.591 s]\n\nBenchmark 2: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=1 & wait_index\n  Time (abs \u2261):        5793.623 s               [User: 9166.985 s, System: 818.496 s]\n\nBenchmark 3: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=2 & wait_index\n  Time (abs \u2261):        4832.000 s               [User: 11593.642 s, System: 1101.024 s]\n\nBenchmark 4: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=3 & wait_index\n  Time (abs \u2261):        4598.405 s               [User: 14802.846 s, System: 1408.865 s]\n\nBenchmark 5: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=4 & wait_index\n  Time (abs \u2261):        4421.838 s               [User: 14844.763 s, System: 1388.922 s]\n\nRelative speed comparison\n        2.23          ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0                 & wait_index\n        1.31          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=1 & wait_index\n        1.09          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=2 & wait_index\n        1.04          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=3 & wait_index\n        1.00          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=4 & wait_index\n```\n\nblockfilterindex 1.63x faster (i7-hdd | Intel i7-7700 | 8 cores | HDD)\n\n```\nBenchmark 1: ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0                  & wait_index\n  Time (abs \u2261):        13060.843 s               [User: 7263.991 s, System: 372.429 s]\n\nBenchmark 2: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=2  & wait_index\n  Time (abs \u2261):        12159.703 s               [User: 11120.566 s, System: 571.598 s]\n\nBenchmark 3: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=4  & wait_index\n  Time (abs \u2261):        9941.238 s               [User: 10606.612 s, System: 534.873 s]\n\nBenchmark 4: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=8  & wait_index\n  Time (abs \u2261):        8396.993 s               [User: 10272.194 s, System: 579.457 s]\n\nBenchmark 5: ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=16 & wait_index\n  Time (abs \u2261):        8028.142 s               [User: 10481.527 s, System: 719.766 s]\n\nRelative speed comparison\n        1.63          ./build-before/bin/bitcoind -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0                  & wait_index\n        1.51          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=2  & wait_index\n        1.24          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=4  & wait_index\n        1.05          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=8  & wait_index\n        1.00          ./build-after/bin/bitcoind  -datadir=/mnt/my_storage/BitcoinData -blockfilterindex=1 -connect=0 -printtoconsole=0 -indexworkers=16 & wait_index\n```\n\n### Way forward\n\nGiven that this PR has struggled to attract reviewers over the past two years, I think we need a different approach.\nThis is a risky change that touches critical index infrastructure - let's do it in tiny, focused steps instead.\n\nI'd like to propose keeping this PR open as a **draft tracking PR** that contains the full end-to-end implementation for reference and discussion. Meanwhile, we can merge the work through a series of smaller, focused PRs:\n1. Start with a minimal implementation: single index (blockfilterindex), hardcoded 2 threads, minimal configurability\n2. Get that reviewed, tested, and merged - let's see what users think\n3. Then add the thread pool infrastructure in a separate PR (if still needed - we might find simpler per-index solutions work better)\n4. Then add txindex and coinstatsindex (and eventually chainstate) support once we have confidence in the approach (and fix the current implementation issues).\n\nI'm happy to help in any way I can and I don't want to discourage you, but I strongly disagree with the current direction, so that's an **Approach NACK** from me.\n\nBreaking this into smaller, more manageable chunks would significantly improve the chances of getting this important work merged - let me know how I can help!\n\nSide note: C++20 coroutines as an alternative?\n\n```\nC++20 coroutines (with work-stealing threadpool, suspending on I/O operations) might be worth investigating for this scenario - they could provide a cleaner way to handle the async I/O patterns here. I haven't used them in C++ myself, so I'm not sure if they're a good fit, but I'd be happy to experiment with them if there's interest.\n```"
  },
  {
   "t": "2025-10-13T13:48:31Z",
   "kind": "comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "text": "I also measured the txindex with `-indexworkers=15` on a 32 vcpu machine and it was slightly slower than master.\nI guess this makes sense since each thread needs to write the index data to leveldb, and the actual work done by the CPU is minimal (serializing then computing offsets). So each thread is waiting for the other threads to finish writing.\nFor blockfilter the filter computation is more intensive, and then each filter is written to a separate file. Only after the filter is written the location of the filter is written to the shared leveldb.\n\nEdit: Just tried again on latest push f13033233d3f2953f179612ac0582b653ac629da on a node synced to 913866:\n\n`./build/bin/bitcoind -connect=0 -disablewallet -txindex=1 -indexworkers=5`\n\nbranch (f13033233d3f2953f179612ac0582b653ac629da) - time was 90 minutes 41 seconds\nmaster (becf1500131805bd6a47486cd5bc5bdb55839211) - time was 84 minutes 42 seconds\n\nSo master was ~7% faster than this PR with 5 workers on an SSD.\n\nMaybe we don't enable parallel sync for txindex?\n\nNote: https://github.com/bitcoin/bitcoin/pull/30039 was merged since many of the earlier benchmarks were made. That greatly speeds up the txindex creation."
  },
  {
   "t": "2025-10-13T15:34:31Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "f13033233d3f2953f179612ac0582b653ac629da"
  },
  {
   "t": "2025-10-13T15:48:58Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Haven't read the latest comments yet (will do soon), but I\u2019ve been working on an overhaul of the approach this weekend. I got enlightened after last week\u2019s review.\n\nThe latest code on Signet, syncing the block filter index up to block height 240593 on an SSD (following the testing steps in the PR description). Results:\n\nMaster branch (64a7c7cbb975cb3c3f25a3f784779f32cd95ebaa) sync time: ~50 minutes.\nCurrent PR (f130332) sync time: ~5 minutes (with 5 worker threads). Flying."
  },
  {
   "t": "2025-10-13T17:47:03Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "afd759a12ff8ee971fbc951d3008eb94073ff29f"
  },
  {
   "t": "2025-10-13T19:53:43Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "536f7b0acf376f05d18834a35436a6654e3b83ac"
  },
  {
   "t": "2025-10-14T01:31:11Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "201a90f03431708933040b3ba56aecd951db845e"
  },
  {
   "t": "2025-10-14T14:27:39Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "21479ee0d62f734a1aa2c431f4f0ccb289e89f2c"
  },
  {
   "t": "2025-10-14T15:06:10Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\nI'm still working on the current approach but synced the tx index on the same environment as before;  bare hardware running on an SSD, synchronizing Signet up to block height 240593 (following the testing steps in the PR description). Results:\n\nParallel Mode (5 workers): ~5 minutes.\n```\n2025-10-13T19:54:55Z initload thread exit\n2025-10-13T19:55:28Z Syncing txindex with block chain from height 149999\n2025-10-13T19:55:59Z Syncing txindex with block chain from height 197999\n2025-10-13T19:56:32Z Syncing txindex with block chain from height 224999\n2025-10-13T19:57:19Z Syncing txindex with block chain from height 225999\n2025-10-13T19:58:13Z Syncing txindex with block chain from height 231999\n2025-10-13T19:58:57Z Syncing txindex with block chain from height 236999\n2025-10-13T19:59:09Z txindex is enabled at height 240593\n```\n\nSequential Mode: 13 minutes.\n```\n2025-10-13T20:00:02Z initload thread exit\n2025-10-13T20:00:34Z Syncing txindex with block chain from height 64999\n2025-10-13T20:01:04Z Syncing txindex with block chain from height 84999\n2025-10-13T20:01:35Z Syncing txindex with block chain from height 130999\n2025-10-13T20:02:05Z Syncing txindex with block chain from height 182999\n2025-10-13T20:02:36Z Syncing txindex with block chain from height 192999\n2025-10-13T20:03:16Z Syncing txindex with block chain from height 200999\n2025-10-13T20:03:47Z Syncing txindex with block chain from height 212999\n2025-10-13T20:04:20Z Syncing txindex with block chain from height 223999\n2025-10-13T20:05:21Z Syncing txindex with block chain from height 226999\n2025-10-13T20:05:54Z Syncing txindex with block chain from height 227999\n2025-10-13T20:06:29Z Syncing txindex with block chain from height 228999\n2025-10-13T20:06:59Z Syncing txindex with block chain from height 229999\n2025-10-13T20:07:39Z Syncing txindex with block chain from height 230999\n2025-10-13T20:08:39Z Syncing txindex with block chain from height 231999\n2025-10-13T20:09:22Z Syncing txindex with block chain from height 232999\n2025-10-13T20:10:09Z Syncing txindex with block chain from height 233999\n2025-10-13T20:10:48Z Syncing txindex with block chain from height 234999\n2025-10-13T20:11:38Z Syncing txindex with block chain from height 236999\n2025-10-13T20:12:14Z Syncing txindex with block chain from height 237999\n2025-10-13T20:13:02Z Syncing txindex with block chain from height 239999\n2025-10-13T20:13:06Z txindex is enabled at height 240593\n```\n\n\u2014\u2014\u2014\n\nFurthermore, this is something I haven't pushed but this is from the batching DB writes branch:\n\nParallel Mode (5 workers) batching db writes:  ~2.30 minutes\n```\n2025-10-13T20:30:54Z initload thread exit\n2025-10-13T20:31:25Z Syncing txindex with block chain from height 181999\n2025-10-13T20:31:56Z Syncing txindex with block chain from height 217999\n2025-10-13T20:32:29Z Syncing txindex with block chain from height 225999\n2025-10-13T20:33:14Z Syncing txindex with block chain from height 230999\n2025-10-13T20:33:50Z Syncing txindex with block chain from height 236999\n2025-10-13T20:33:54Z txindex is enabled at height 240593\n```\n\nSo I\u2019d guess that it\u2019s not only related to disk access, but the difference might also be tied to your CPU virtualization technology? @andrewtoth.\n\nMoreover, I mentioned this somewhere earlier in this PR, but it probably got lost over the years. This PR also prepares the ground for batching DB writes across blocks, which will improve sequential runs as well. Which every user will benefit.\n\nAlso, parallelization is disabled by default. I don\u2019t think it\u2019s worth removing it for the tx index just because some users might not be able to take advantage of it. It seems to me that the benefits for those who can run it greatly outweigh the limitations for those who can\u2019t (they\u2019re not losing anything with this PR, since sequential mode is still available)."
  },
  {
   "t": "2025-10-14T20:04:27Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "fcdd89bdb8b9b89727f3817059b45ee2ed832ecc"
  },
  {
   "t": "2025-10-14T20:10:01Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "f7cf59e4fb373eaaa83e650850e66d0df5ac44cd"
  },
  {
   "t": "2025-10-14T20:17:02Z",
   "kind": "comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "text": "what's the reason for the frequent pushes here recently?"
  },
  {
   "t": "2025-10-14T20:44:22Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI wrote it above, https://github.com/bitcoin/bitcoin/pull/26966#issuecomment-3398073492. I'm working on a few changes that improves parallelism across indexes and the structure of the code, which work properly locally but are not stable on the CI yet. Will update the state with the modifications upon finishing."
  },
  {
   "t": "2025-10-15T14:23:55Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "3dede1f3005957264729713b1fcace65795e5818"
  },
  {
   "t": "2025-10-15T16:58:06Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "831dec831572249f838344d099cb38ab9555dc91"
  },
  {
   "t": "2025-10-15T17:34:28Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2424805627,
   "text": "[quoted text omitted]\n\nAdding 2 threads or `n` threads represents the exact same work. The code complexity comes from the two-step procedure that allows sequential dumps after parallel block data construction (a restriction that comes from the filters headers-chain structure). It is not related to the number of threads introduced, that's is simply controlled by the thread pool introduced in the first commit.\n\nI could split the thread pool into another PR. That would be easy to do. I'm just concerned about getting into a high-level reviewer discussion with not much substance. Such simple PRs tend to allow that. One can easily lose focus and end up discussing nuances endlessly."
  },
  {
   "t": "2025-10-15T17:52:32Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2424805627,
   "text": "[quoted text omitted]\n\nThe thread pool, tests, and fuzz harness are 560 lines. More than half the additions to this PR. I don't think it's fair to say that would be a simple PR."
  },
  {
   "t": "2025-10-15T18:20:22Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\nThis is a risky change that touches critical index infrastructure - let's do it in tiny, focused steps instead.\n[quoted text omitted]\n\n[quoted text omitted]\nFor the \"hardcoded 2 threads\" step, I wrote this above but adding 2 threads or `n` threads represents the exact same work. The code complexity comes from the two-step procedure that allows sequential dumps after parallel block data construction (a restriction that comes from the filters headers-chain structure; headers are linked to each other). It is not related to the number of threads introduced, that's is simply controlled by the thread pool introduced in the first commit. That line simply doesn't make sense to me.\n\nFurthermore, the description of this as \"critical index infrastructure\" is not entirely accurate. We are not dealing with consensus-related code here - this code lives on a different level. Existing tests already verify both its behavior and outcomes, and the previous sequential sync behavior is retained. This PR introduces a new optional feature which, in the worst-case scenario, can run slower than sequential if it is badly configured by the user (something that could also happen with other configurations, such as setting a high number of block sigs verification threads).\n\nAlso, about the \"Given that this PR has struggled to attract reviewers over the past two years\":\nI don't think it is fair to say that at the moment. This PR has gotten quite nice reviews lately and has been ramping up quite nicely. That's mainly why I ended up modifying part of it and simplifying some workflows (an update will come in the next comment).\nI think the problem in the past years was mainly related to the lack of urgency for having this new feature and its requirement of understanding not only what indexes are and how they are compounded, but also the underlying structure of the block filters chain.\nI think it is just a matter of coordination between people who have worked on this code before and anyone else who wants to spend time understanding it deeply. I think we are happily starting to roll the ball on that direction now."
  },
  {
   "t": "2025-10-15T18:27:26Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Now that the CI is happy again. Update regarding the introduced changes:\n\nBefore we had queues at two different levels, one at the thread pool level and another one at the index level. The initial rationale behind it was to granularly control the way on which the process interrupts them when needed. Not draining the index's `pending_tasks` queue as soon as the first task fails to process or a shutdown was triggered. But, after talking with mzumsande last week, I realized that this was actually over-engineering for not much gain and was harming parallelism across indexes. Top level index queues were racing with each other to take control of the underlying worker threads.\nBy unifying this two queues, we let the thread pool decide how/when to handle the tasks alone, with no index inference at all. Which allows for much better parallelism among any piece of code using the same workers (not just indexes) and also for further improvements along the road; like modifying a single spot with a lock-free structure or even the prioritization of certain tasks at the workers level - this former one was an idea coming from l0rinc two weeks ago.\n\nSo, in short, the code should be cleaner and run faster now, using less mutexes. Also, there is a possible follow-up improvement on moving the final post-processing round to the last task being executed (in case more than one task finishes at the same time by the end of sync). I didn't do it here because it comes with some extra code complexity that I don't think is worth adding to this PR; the same reason I don't think parallelizing the coinstats index here is a good idea, nor batching DB writes. Better to go in steps, as the gains introduced here are already a pretty solid step forward."
  },
  {
   "t": "2025-10-15T18:32:41Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2422052807,
   "text": "Yeah, updated. Thanks!"
  },
  {
   "t": "2025-10-15T18:32:48Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/test/threadpool_tests.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2422034019,
   "text": "updated. Thanks!"
  },
  {
   "t": "2025-10-15T18:38:14Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2424805627,
   "text": "[quoted text omitted]\n\nI don't think the fuzz and tests are hard to grasp, but fair. I should have said that it is simple in comparison to the other changes, which require further contextual knowledge and not just multi-threading.\nWe can try splitting the thread pool. I'm definitely not opposed to it, just have some concerns."
  },
  {
   "t": "2025-10-15T19:04:42Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/base.cpp",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2424874997,
   "text": "answered below https://github.com/bitcoin/bitcoin/pull/26966#issuecomment-3407722811."
  },
  {
   "t": "2025-10-15T19:14:59Z",
   "kind": "review_comment",
   "who": "l0rinc",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2424805627,
   "text": "The review effort would be the same for 2 or nproc threads, but the implications would be limited with the former - we cannot have e.g. complete deadlock if at most two threads are stuck on indexing. And the recent failures on CI indicate that deadlock concerns aren't unfounded."
  },
  {
   "t": "2025-10-15T19:31:42Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/util/threadpool.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2424805627,
   "text": "[quoted text omitted]\n\nThat's not what happened. Tasks are not depending on each other (they actually never were), they run in isolation, so threads cannot deadlock between each other. They are not waiting for any resource from other threads.\n\nThe issue on the CI was not related to the number of threads, they were all finishing and exiting. It would have occurred with 2 as well. I was just not contemplating the fact that tasks can finish at the same time on the new approach so the opportunistic post-processing procedure was not triggered."
  },
  {
   "t": "2025-10-17T18:42:45Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "c38a5db0ebfe05749789d0230420cb52dc773adb"
  },
  {
   "t": "2025-10-17T18:50:41Z",
   "kind": "review_comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "path": "src/index/txindex.h",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080",
   "in_reply_to": 2424838828,
   "text": "Yes. Up to block 381999 on mainnet.\n\nParallel run, ~20 minutes:\n\n2025-10-16T13:58:05Z initload thread exit\n2025-10-16T13:58:35Z Syncing txindex with block chain from height 189999\n2025-10-16T13:59:12Z Syncing txindex with block chain from height 211999\n2025-10-16T13:59:42Z Syncing txindex with block chain from height 223999\n2025-10-16T14:00:13Z Syncing txindex with block chain from height 235999\n....\n2025-10-16T14:16:01Z Syncing txindex with block chain from height 373999\n2025-10-16T14:16:41Z Syncing txindex with block chain from height 377999\n2025-10-16T14:17:37Z Syncing txindex with block chain from height 379999\n2025-10-16T14:18:11Z Syncing txindex with block chain from height 381999\n\n\u2014\u2014\u2014\n\nSequential run, ~48 minutes:\n\n2025-10-16T14:21:09Z initload thread exit\n2025-10-16T14:21:40Z Syncing txindex with block chain from height 135999\n2025-10-16T14:22:10Z Syncing txindex with block chain from height 157999\n2025-10-16T14:22:43Z Syncing txindex with block chain from height 179999\n2025-10-16T14:23:18Z Syncing txindex with block chain from height 185999\n2025-10-16T14:23:49Z Syncing txindex with block chain from height 191999\n....\n2025-10-16T15:08:25Z Syncing txindex with block chain from height 378999\n2025-10-16T15:08:56Z Syncing txindex with block chain from height 379999\n2025-10-16T15:09:59Z Syncing txindex with block chain from height 381999"
  },
  {
   "t": "2025-10-21T15:42:21Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Small update, per discussion, will push the thread pool on an isolated PR in the coming week. Replacing the current http server `WorkQueue` and all its related code; it is an easy dedup that adds further test coverage to a never unit nor fuzz tested area. Plus a way to start using this class in the repo that allow others to use it.\n\nAlso, I'm investigating the tx index slow down with @andrewtoth. Trying to understand the differences between us. Worst-case scenario, we can disable parallel sync for it (will update accordantly in the coming week).\nThat being said, this refactoring enables batching DB writes as well, which will speedup tx index anyway. But prefer to do it in a follow-up to not continue expanding this PR.\n\nLast note; I reworked the large commit, splitting it into smaller chunks. We can come back to this one after (or while) the http server thread handling PR moves on. But will focus on moving forward there first per feedback."
  },
  {
   "t": "2025-10-24T05:34:17Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "0ad89bf7a6b2e3161a153308c14029c43b0e06a3"
  },
  {
   "t": "2026-02-01T23:26:45Z",
   "kind": "comment",
   "who": "furszy",
   "assoc": "MEMBER",
   "text": "Drafted while work continues on #33689. Once the thread pool is included, I'll rebase this one and continue moving forward."
  },
  {
   "t": "2026-02-02T00:21:36Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "01a96ee859a9ba80a6f198a6b0c0c5443821d130"
  },
  {
   "t": "2026-02-02T05:04:38Z",
   "kind": "force_push",
   "who": "furszy",
   "commit": "c5dad86523e9715340851fa03e2ee470b0c35080"
  }
 ],
 "labels_log": [
  {
   "t": "2023-01-25T13:36:28Z",
   "action": "labeled",
   "label": "UTXO Db and Indexes",
   "who": "DrahtBot"
  },
  {
   "t": "2023-02-17T23:32:40Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-02-21T15:07:08Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-05-11T10:29:56Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-05-11T18:57:37Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-06-12T17:03:55Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-06-21T17:13:39Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-06-30T11:24:11Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-06-30T18:03:36Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-06-30T18:52:30Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2023-07-04T18:17:07Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2023-07-06T22:10:00Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-08-09T16:24:14Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-08-29T10:28:39Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2023-09-12T10:33:21Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-09-12T14:46:49Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2023-09-12T14:47:33Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-10-02T21:55:48Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-10-18T14:31:16Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2023-10-18T17:35:43Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2023-10-27T16:29:55Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2023-12-14T21:54:46Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-03-20T23:53:12Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-03-21T01:36:39Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-03-21T19:29:31Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-03-23T14:10:34Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-04-04T16:08:44Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-04-05T07:54:33Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-04-05T08:19:24Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-04-05T08:37:46Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-04-05T08:38:56Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-04-05T09:34:50Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-04-30T22:53:44Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-05-01T15:30:03Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-05-01T18:18:13Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-06-14T14:16:28Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-08-05T23:03:11Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-08-14T21:15:17Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-08-16T10:24:22Z",
   "action": "labeled",
   "label": "Needs CMake port",
   "who": "hebasto"
  },
  {
   "t": "2024-08-29T05:21:37Z",
   "action": "unlabeled",
   "label": "Needs CMake port",
   "who": "maflcko"
  },
  {
   "t": "2024-09-02T22:46:29Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-10-01T20:04:01Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-01-22T18:28:11Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-02-25T20:54:00Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-02-25T21:03:30Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-03-12T20:45:59Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-05T21:00:35Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-05T22:09:57Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-06T02:24:00Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-12T23:51:58Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-13T01:58:29Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-17T00:05:16Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-17T09:06:46Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-20T00:34:40Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-20T17:43:18Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-23T20:34:25Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-25T15:14:23Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-25T17:54:36Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-06-26T23:18:32Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-07-08T02:07:29Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-07-08T04:13:24Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-07-14T17:21:37Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-07-14T21:33:14Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-07-15T01:26:50Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-07-15T19:59:46Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-08-08T00:19:12Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-08-08T03:44:02Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-08-19T20:45:41Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-08-20T11:26:39Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-08-20T11:49:18Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-08-30T18:58:56Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2025-10-28T12:56:02Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-02T01:02:51Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-02T01:45:21Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-11T18:22:54Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-09T18:05:44Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "maflcko"
  }
 ],
 "state_log": [
  {
   "t": "2023-02-27T22:37:56Z",
   "kind": "renamed",
   "who": "furszy",
   "from": "index: blockfilter initial sync speedup, parallelize process",
   "to": "index: blockfilter and txindex initial sync speedup, parallelize process"
  },
  {
   "t": "2023-02-27T23:07:13Z",
   "kind": "renamed",
   "who": "furszy",
   "from": "index: blockfilter and txindex initial sync speedup, parallelize process",
   "to": "index: blockfilter initial sync speedup, parallelize process"
  },
  {
   "t": "2024-04-18T11:50:54Z",
   "kind": "convert_to_draft",
   "who": "furszy"
  },
  {
   "t": "2024-08-07T14:41:29Z",
   "kind": "renamed",
   "who": "furszy",
   "from": "index: blockfilter initial sync speedup, parallelize process",
   "to": "index: initial sync speedup, parallelize process"
  },
  {
   "t": "2025-06-23T19:28:28Z",
   "kind": "ready_for_review",
   "who": "furszy"
  },
  {
   "t": "2026-02-01T23:25:30Z",
   "kind": "convert_to_draft",
   "who": "furszy"
  }
 ],
 "text_chars": 106597,
 "text_tokens_estimate": 26649,
 "changed_paths": [
  "src/index/base.cpp",
  "src/index/base.h",
  "src/index/blockfilterindex.cpp",
  "src/index/blockfilterindex.h",
  "src/index/txindex.h",
  "src/init.cpp",
  "src/node/context.h",
  "src/test/CMakeLists.txt",
  "src/test/blockfilter_index_tests.cpp",
  "src/test/fuzz/CMakeLists.txt",
  "src/test/fuzz/threadpool.cpp",
  "src/test/threadpool_tests.cpp",
  "src/test/txindex_tests.cpp",
  "src/util/threadpool.h"
 ],
 "files": [
  {
   "path": "src/index/base.cpp",
   "add": 265,
   "del": 62
  },
  {
   "path": "src/index/base.h",
   "add": 55,
   "del": 2
  },
  {
   "path": "src/index/blockfilterindex.cpp",
   "add": 17,
   "del": 9
  },
  {
   "path": "src/index/blockfilterindex.h",
   "add": 8,
   "del": 2
  },
  {
   "path": "src/index/txindex.h",
   "add": 7,
   "del": 0
  },
  {
   "path": "src/init.cpp",
   "add": 22,
   "del": 1
  },
  {
   "path": "src/node/context.h",
   "add": 3,
   "del": 0
  },
  {
   "path": "src/test/CMakeLists.txt",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/test/blockfilter_index_tests.cpp",
   "add": 51,
   "del": 77
  },
  {
   "path": "src/test/fuzz/CMakeLists.txt",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/test/fuzz/threadpool.cpp",
   "add": 100,
   "del": 0
  },
  {
   "path": "src/test/threadpool_tests.cpp",
   "add": 267,
   "del": 0
  },
  {
   "path": "src/test/txindex_tests.cpp",
   "add": 42,
   "del": 0
  },
  {
   "path": "src/util/threadpool.h",
   "add": 192,
   "del": 0
  }
 ],
 "test_lines": 539,
 "git": {
  "head": "c5dad86523e9715340851fa03e2ee470b0c35080",
  "head_matches_backup": true,
  "base": "8bb77f348ef390b7f7c7fb6b57fdc7e86ddb4ce7",
  "commits": [
   {
    "sha": "59d88389ab",
    "subject": "index: split block processing into process and post-process phases",
    "files": 2,
    "add": 44,
    "del": 12
   },
   {
    "sha": "2f4b456e85",
    "subject": "index: add support for processing batches of blocks",
    "files": 2,
    "add": 52,
    "del": 4
   },
   {
    "sha": "f3e621ca61",
    "subject": "index: support processing blocks out of order",
    "files": 2,
    "add": 24,
    "del": 5
   },
   {
    "sha": "770b45d31c",
    "subject": "Index: introduce SyncContext - move logging & last locator timers",
    "files": 1,
    "add": 17,
    "del": 10
   },
   {
    "sha": "aff4476f0e",
    "subject": "index: encapsulate Task processing code into RunTask",
    "files": 2,
    "add": 48,
    "del": 37
   },
   {
    "sha": "fd32e394e0",
    "subject": "util: introduce general purpose thread pool",
    "files": 3,
    "add": 460,
    "del": 0
   },
   {
    "sha": "b57f8f2df3",
    "subject": "init: provide thread pool to indexes",
    "files": 3,
    "add": 39,
    "del": 2
   },
   {
    "sha": "b99c6909cb",
    "subject": "index: implement index parallel sync",
    "files": 3,
    "add": 199,
    "del": 150
   },
   {
    "sha": "4b93e8c51e",
    "subject": "index: enable block filter index parallel sync",
    "files": 3,
    "add": 76,
    "del": 11
   },
   {
    "sha": "2ba13efb89",
    "subject": "txindex: enable parallel sync",
    "files": 2,
    "add": 49,
    "del": 0
   },
   {
    "sha": "c5dad86523",
    "subject": "fuzz: add test case for threadpool",
    "files": 2,
    "add": 101,
    "del": 0
   }
  ],
  "patch_truncated": true
 },
 "input_hash": "ee6e6ad681f2e958",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}