)]}'
{
  "log": [
    {
      "commit": "345c3d92b59f6efb187d0d8f2dbd5ef12f65ffd8",
      "tree": "f9499d6240132d0fc5fe27df62cba8d78e04ccaa",
      "parents": [
        "0af6e6071ab46acc7af7adb01a112567ccd1aa05"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Wed Jun 03 16:41:12 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jun 03 16:41:12 2026 +0300"
      },
      "message": "Upgrade to Netty 4.2.15 to address CVEs (#4813)"
    },
    {
      "commit": "0af6e6071ab46acc7af7adb01a112567ccd1aa05",
      "tree": "5ab6be55845add8f0738b595f49f17e9851e88b2",
      "parents": [
        "55e0088171b1a502e39886eab09015be3507ef89"
      ],
      "author": {
        "name": "Hang Chen",
        "email": "chenhang@apache.org",
        "time": "Mon Jun 01 14:11:11 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 14:11:11 2026 -0700"
      },
      "message": "Add cargo-zigbuild for release dockerfile (#4805)\n\n* Add cargo-zigbuild for release dockerfile\n\n* address comments"
    },
    {
      "commit": "55e0088171b1a502e39886eab09015be3507ef89",
      "tree": "cef1c14abe48ef06ac0e942b2b16bf32fb9b8526",
      "parents": [
        "3c4a02673f82501d016a9c26828df96fabdba474"
      ],
      "author": {
        "name": "Hang Chen",
        "email": "chenhang@apache.org",
        "time": "Mon Jun 01 10:50:47 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 10:50:47 2026 -0700"
      },
      "message": "Fix release user id (#4806)"
    },
    {
      "commit": "3c4a02673f82501d016a9c26828df96fabdba474",
      "tree": "365d34a009df4b0352bd5f8b34722b1056dee63d",
      "parents": [
        "ccfabfaf50efccdb3f8cec46e6243a76cac04ad7"
      ],
      "author": {
        "name": "void-ptr974",
        "email": "void-ptr974@protonmail.com",
        "time": "Tue Jun 02 01:49:10 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jun 01 10:49:10 2026 -0700"
      },
      "message": "Include lombok config in source release (#4808)"
    },
    {
      "commit": "ccfabfaf50efccdb3f8cec46e6243a76cac04ad7",
      "tree": "f60fca458dedc3aa2ec7f6c657a7a1696a41b06a",
      "parents": [
        "8e88b03b6a660f4c8055f2b0b1f683e96711a174"
      ],
      "author": {
        "name": "void-ptr974",
        "email": "void-ptr974@protonmail.com",
        "time": "Thu May 28 09:27:39 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 18:27:39 2026 -0700"
      },
      "message": "fix Bookkeeper can\u0027t startup cause by \u0027IOException: Recovery log xxx is missing\u0027 (#4740)\n\n* fix #4105\n\nWhen journal and dbledgerstorage on the same disk, concurrent checkpointComplete calls from SyncThread and SingleDirectoryDbLedgerStorage.flush() can overwrite lastMark backwards,causing journal files to be deleted while still referenced by the older mark.\n\n* fix #4105\n\nWhen singleLedgerDirs\u003dtrue, bookie can crash on restart with \"Recovery log is missing\" due to lastMark file being overwritten backwards.\n\n  Root cause\n\n  SyncThread.flush() has a nested call pattern that causes lastMark regression:\n\n  SyncThread.flush():\n    1. outerCheckpoint \u003d newCheckpoint()        → mark \u003d (file 5, offset 100)\n    2. ledgerStorage.flush()\n       → SingleDirectoryDbLedgerStorage.flush():\n         a. innerCheckpoint \u003d newCheckpoint()   → mark \u003d (file 7, offset 200), journal has advanced\n         b. flushes data to disk\n         c. checkpointComplete(mark\u003d7, compact\u003dtrue)\n            → persists lastMark\u003d7, deletes journal files with id \u003c 7 (including file 5)\n    3. checkpointComplete(mark\u003d5, compact\u003dfalse)\n       → persists lastMark\u003d5, overwriting the 7 written in step 2c\n    4. Bookie restarts → reads lastMark\u003d5 → file 5 no longer exists → crash\n\n  Step 1 captures the checkpoint before step 2 runs, so it is always older. Step 2c advances lastMark and garbage-collects old journals. Step 3 then overwrites lastMark backwards to a\n  position whose journal file was already deleted.\n\n  Conditions\n\n  - singleLedgerDirs\u003dtrue (journal and ledger on the same disk, so SingleDirectoryDbLedgerStorage.flush() calls checkpointComplete internally)\n  - Journal file rotates between the two newCheckpoint() calls (requires sufficient write throughput)\n  - maxBackupJournals small enough for old files to actually be deleted\n\n  Fix\n\n  Add a monotonic guard in checkpointComplete(): track the highest mark persisted so far, skip any call with an older mark. This prevents rollLog from overwriting lastMark backwards.\n\n* fix #4105\n\nWhen singleLedgerDirs\u003dtrue, bookie can crash on restart with \"Recovery log is missing\" due to lastMark file being overwritten backwards.\n\n  Root cause\n\n  SyncThread.flush() has a nested call pattern that causes lastMark regression:\n\n  SyncThread.flush():\n    1. outerCheckpoint \u003d newCheckpoint()        → mark \u003d (file 5, offset 100)\n    2. ledgerStorage.flush()\n       → SingleDirectoryDbLedgerStorage.flush():\n         a. innerCheckpoint \u003d newCheckpoint()   → mark \u003d (file 7, offset 200), journal has advanced\n         b. flushes data to disk\n         c. checkpointComplete(mark\u003d7, compact\u003dtrue)\n            → persists lastMark\u003d7, deletes journal files with id \u003c 7 (including file 5)\n    3. checkpointComplete(mark\u003d5, compact\u003dfalse)\n       → persists lastMark\u003d5, overwriting the 7 written in step 2c\n    4. Bookie restarts → reads lastMark\u003d5 → file 5 no longer exists → crash\n\n  Step 1 captures the checkpoint before step 2 runs, so it is always older. Step 2c advances lastMark and garbage-collects old journals. Step 3 then overwrites lastMark backwards to a\n  position whose journal file was already deleted.\n\n  Conditions\n\n  - singleLedgerDirs\u003dtrue (journal and ledger on the same disk, so SingleDirectoryDbLedgerStorage.flush() calls checkpointComplete internally)\n  - Journal file rotates between the two newCheckpoint() calls (requires sufficient write throughput)\n  - maxBackupJournals small enough for old files to actually be deleted\n\n  Fix\n\n  Add a monotonic guard in checkpointComplete(): track the highest mark persisted so far, skip any call with an older mark. This prevents rollLog from overwriting lastMark backwards.\n\n* fix #4105\n\nWhen singleLedgerDirs\u003dtrue, bookie can crash on restart with \"Recovery log is missing\" due to lastMark file being overwritten backwards.\n\n  Root cause\n\n  SyncThread.flush() has a nested call pattern that causes lastMark regression:\n\n  SyncThread.flush():\n    1. outerCheckpoint \u003d newCheckpoint()        → mark \u003d (file 5, offset 100)\n    2. ledgerStorage.flush()\n       → SingleDirectoryDbLedgerStorage.flush():\n         a. innerCheckpoint \u003d newCheckpoint()   → mark \u003d (file 7, offset 200), journal has advanced\n         b. flushes data to disk\n         c. checkpointComplete(mark\u003d7, compact\u003dtrue)\n            → persists lastMark\u003d7, deletes journal files with id \u003c 7 (including file 5)\n    3. checkpointComplete(mark\u003d5, compact\u003dfalse)\n       → persists lastMark\u003d5, overwriting the 7 written in step 2c\n    4. Bookie restarts → reads lastMark\u003d5 → file 5 no longer exists → crash\n\n  Step 1 captures the checkpoint before step 2 runs, so it is always older. Step 2c advances lastMark and garbage-collects old journals. Step 3 then overwrites lastMark backwards to a\n  position whose journal file was already deleted.\n\n  Conditions\n\n  - singleLedgerDirs\u003dtrue (journal and ledger on the same disk, so SingleDirectoryDbLedgerStorage.flush() calls checkpointComplete internally)\n  - Journal file rotates between the two newCheckpoint() calls (requires sufficient write throughput)\n  - maxBackupJournals small enough for old files to actually be deleted\n\n  Fix\n\n  Add a monotonic guard in checkpointComplete(): track the highest mark persisted so far, skip any call with an older mark. This prevents rollLog from overwriting lastMark backwards.\n\n* Fix lastMark regression in checkpoint completion"
    },
    {
      "commit": "8e88b03b6a660f4c8055f2b0b1f683e96711a174",
      "tree": "d0016c31a418900e15f386806c4bd8eff5b7f5a2",
      "parents": [
        "cbb336702aaa17608d811a52ca7b03559e29cbb0"
      ],
      "author": {
        "name": "道君- Tao Jiuming",
        "email": "daojun@apache.org",
        "time": "Thu May 28 08:50:05 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed May 27 17:50:05 2026 -0700"
      },
      "message": "Optimize bounded batch reads by predicting entry count (#4741)\n\n* Optimize batch-read to avoid wasted disk IO\n\n* Fix checkstyle\n\n* Address review comments\n\n* Address review comments\n\n* fix conflicts\n\n* Address review comments\n\n* Address review comments\n\n* fix checkstyle\n\n* fix code\n\n* Address review comment\n\n* address review comment"
    },
    {
      "commit": "cbb336702aaa17608d811a52ca7b03559e29cbb0",
      "tree": "85961ceb6841dd6e42c3eb3fbff5929be67aaeef",
      "parents": [
        "4a98f1806c63ec74d96fd65856415eedf07af6ba"
      ],
      "author": {
        "name": "Masahiro Sakamoto",
        "email": "massakam@lycorp.co.jp",
        "time": "Wed May 27 08:25:52 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 16:25:52 2026 -0700"
      },
      "message": "Fix issue where options for sanity test command are ignored (#4691)"
    },
    {
      "commit": "4a98f1806c63ec74d96fd65856415eedf07af6ba",
      "tree": "f95651ca6c024efa290230d7eaf301145462e08b",
      "parents": [
        "18a4368d2df74107e3a825395d93ec8b57703c75"
      ],
      "author": {
        "name": "void-ptr974",
        "email": "void-ptr974@protonmail.com",
        "time": "Wed May 27 02:51:43 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 11:51:43 2026 -0700"
      },
      "message": "Fix WriteSet use-after-recycle in sequence reads (#4788)\n\nSequence read completion recycled the WriteSet before registering slow bookies.\n  When a speculative read completed the entry, the subclass could still access the\n  recycled WriteSet. Late error callbacks could also enter reattempt logic and call\n  indexOf() after the request had already completed.\n\n  Snapshot slow bookie addresses before delegating to the shared complete path, and\n  ignore late errors after sequence read requests have completed. Apply the same\n  guard to ReadLAC sequence reads, where orderedEnsemble is also recycled on\n  completion.\n\n  Add regression coverage for normal sequence reads and ReadLAC covering both\n  completion and late-error paths."
    },
    {
      "commit": "18a4368d2df74107e3a825395d93ec8b57703c75",
      "tree": "7481aa75a8666b49d1fc7d2ac91dc1a97efd3cec",
      "parents": [
        "1dc894fbb66774aef12301b43b6a3c22c972ba55"
      ],
      "author": {
        "name": "void-ptr974",
        "email": "void-ptr974@protonmail.com",
        "time": "Wed May 27 02:51:00 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 11:51:00 2026 -0700"
      },
      "message": "Fix batch reads hanging after digest mismatch retries are ignored (#4789)\n\n* Fix batch read retry on digest mismatch\n\n  Batch reads marked the request as complete before verifying the\n  digest of each returned entry. If one entry in the response failed\n  digest verification, the operation attempted to retry on another\n  replica, but the request had already been completed, so the retry\n  response could be ignored and the batch read could hang or fail.\n\n  Verify all entries in the batch before completing the request. On\n  digest mismatch, leave the request incomplete, discard the whole\n  response, and retry the same batch on the next replica. Only create\n  LedgerEntryImpl instances after the full batch has passed digest\n  verification, so no partially verified entries are retained.\n\n  Add tests for retrying after a corrupt batch response and for the\n  case where an earlier entry verifies successfully but a later entry\n  fails digest verification.\n\n* Return verified batch prefix on digest mismatch"
    },
    {
      "commit": "1dc894fbb66774aef12301b43b6a3c22c972ba55",
      "tree": "a6834804bc809fc069daecccc5b4f152e9171d1f",
      "parents": [
        "3565d0e289c3bc9c638ba435c4165bc66b5268d8"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Tue May 26 12:22:24 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 26 12:22:24 2026 +0300"
      },
      "message": "Upgrade Netty to 4.2.14 (#4799)"
    },
    {
      "commit": "3565d0e289c3bc9c638ba435c4165bc66b5268d8",
      "tree": "b1613564232cfdbd5c09a21349b24e41fc0bf9c4",
      "parents": [
        "076fabd23d11f16c1d86b72a4619c0b5f4548c13"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Wed May 13 00:35:05 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 12 14:35:05 2026 -0700"
      },
      "message": "Upgrade thrift to 0.23.0 to address CVE-2026-43869 (#4791)"
    },
    {
      "commit": "076fabd23d11f16c1d86b72a4619c0b5f4548c13",
      "tree": "6d8c0a4383283d13cb3460cb65e9bf36b083689a",
      "parents": [
        "6ef121e2b7f7d3f6c317806ac956ac043387824d"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Tue May 12 15:40:31 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 12 15:40:31 2026 +0300"
      },
      "message": "Upgrade vertx to 4.5.27 to address CVE-2026-6860 (#4792)"
    },
    {
      "commit": "6ef121e2b7f7d3f6c317806ac956ac043387824d",
      "tree": "465dab9bf0530b64af27b49708e6804a43f13f5f",
      "parents": [
        "8b6671fff8d43279dd89a1e1eea6851746f546a0"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Mon May 11 12:13:00 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon May 11 12:13:00 2026 +0300"
      },
      "message": "Upgrade vert.x to 4.5.25 to address CVE-2026-6860 (#4790)"
    },
    {
      "commit": "8b6671fff8d43279dd89a1e1eea6851746f546a0",
      "tree": "cd595026c14f21b6be5c895aaa3e941b6702fa2d",
      "parents": [
        "985fe2a7667e13bb520a2f0096d2047dc5c7f702"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Sat May 09 10:46:13 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 09 10:46:13 2026 -0700"
      },
      "message": "Scope gRPC dependencies and rely on grpc-bom for single version (#4784)\n\n* Scope gRPC dependencies and rely on grpc-bom for single version\n\nReplace the brittle `grpc-all` dependency (with its long exclusion list in\nthe parent pom) with the specific gRPC artifacts each module actually\nuses. The `grpc-bom` import in dependencyManagement continues to enforce\na single version across all `io.grpc:*` artifacts.\n\nModule changes:\n- pom.xml: drop the `grpc-all` dependencyManagement block; the BOM alone\n  manages versions\n- stream/common: `grpc-all` -\u003e `grpc-api`, `grpc-stub`, `grpc-core`\n  (compile); `grpc-inprocess`, `grpc-util` (test)\n- stream/tests-common: `grpc-all` -\u003e `grpc-api`, `grpc-stub`; drop unused\n  `grpc-testing` (no `io.grpc.testing.*` imports anywhere)\n- stream/proto: drop `grpc-protobuf` (dead after the LightProto\n  migration; generated code uses Netty `ByteBuf` only)\n- stream/clients/java/base: add explicit `grpc-inprocess`,\n  `grpc-netty-shaded`, `grpc-util` that were silently inherited\n- metadata-drivers/etcd: `grpc-all` -\u003e `grpc-api`, `grpc-stub`\n\nDrop pointless protobuf-java usages so statelib no longer needs it:\n- statelib `Bytes.toLong(ByteString)` was a dead overload (every caller\n  uses the `byte[]` variant); removed\n- statelib `MVCCStoreImpl` used `TextFormat.escapeBytes` once for an\n  error message; replaced with JDK 17 `HexFormat.of().formatHex`\n- bookkeeper-server `BookieImplTest` had two no-op protobuf\n  round-trips (`ByteString.copyFrom(bytes).toByteArray()` and\n  `unsafeWrap(...).asReadOnlyByteBuffer()`); replaced with plain\n  `byte[]` and `ByteBuffer.wrap`\n\nUpdate LICENSE-{all,server,bkctl}.bin.txt to match the new bundled jar\nset: drop entries for grpc-all/-alts/-auth/-grpclb/-opentelemetry\n/-protobuf/-protobuf-lite/-services/-testing/-xds/-rls\n/-gcp-csm-observability and the transitively-removed proto-google-common\n-protos, protobuf-java-util, api-common, google-http-client*,\ngoogle-auth-library-*, auto-value-annotations, re2j, opencensus-*,\nconscrypt; clean up the orphaned source-URL footnotes.\n\nNet dist change: 7 grpc jars (down from 18) and 1 protobuf jar\n(down from 3, now sourced only via prometheus-metrics-exposition-formats).\n\n* Fix LICENSE/NOTICE files for the dropped jars\n\nCI\u0027s `dev/check-all-licenses` flagged remaining stale references that\nwere not caught in the previous commit:\n\n- NOTICE-{all,server,bkctl}.bin.txt: drop the unbundled `grpc-all`,\n  `grpc-auth`, `grpc-protobuf`, `grpc-protobuf-lite`, `grpc-testing`\n  entries; add the previously-missing `grpc-api`, `grpc-inprocess`,\n  `grpc-util` to the gRPC list (these have always been bundled).\n- LICENSE-{all,server,bkctl}.bin.txt: drop\n  `opentelemetry-gcp-resources-1.56.0-alpha.jar` (was transitive via the\n  removed `grpc-gcp-csm-observability`, no longer bundled).\n- LICENSE-bkctl.bin.txt: drop the entire OpenTelemetry SDK list and the\n  protobuf-java entry; bkctl never bundled any of these (pre-existing\n  staleness, hidden in CI by `set -e` stopping on the first dist error).\n- bookkeeper-dist/src/assemble/{bin-all,bin-server,bkctl}.xml: drop the\n  `deps/google-auth-library-credentials-0.20.0/LICENSE` include from\n  every assembly (orphaned now that the jar is no longer bundled), and\n  drop the `deps/protobuf/LICENSE` include from the bkctl assembly\n  (bkctl doesn\u0027t ship protobuf-java).\n- Delete the orphaned\n  `bookkeeper-dist/src/main/resources/deps/google-auth-library-credentials-0.20.0/`\n  directory.\n\nVerified locally: `dev/check-all-licenses` passes against freshly-built\nall/server/bkctl tarballs."
    },
    {
      "commit": "985fe2a7667e13bb520a2f0096d2047dc5c7f702",
      "tree": "85e716effae1b51ceb630002146ac747c8eabbbd",
      "parents": [
        "47efc0dd6d72c82fd5afe7637a673b80f4e070d3"
      ],
      "author": {
        "name": "Zixuan Liu",
        "email": "nodeces@gmail.com",
        "time": "Sun May 10 01:31:28 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 09 10:31:28 2026 -0700"
      },
      "message": "fix: add javadoc support for pom-only shaded modules (#4787)"
    },
    {
      "commit": "47efc0dd6d72c82fd5afe7637a673b80f4e070d3",
      "tree": "70966d9622a37f0952fd462e43327b302cf91075",
      "parents": [
        "9633bb740e946c13ee685af887863ec86f6d6460"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Fri May 08 16:02:38 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 08 16:02:38 2026 -0700"
      },
      "message": "Migrate BookkeeperProtocol from protobuf-java to LightProto (#4780)\n\n* Fix LedgerHandle.batchReadUnconfirmedAsync: use slog log instead of LOG\n\nThe batchReadUnconfirmedAsync method added in #4739 calls LOG.error(...),\nbut LedgerHandle was migrated to slog and only has a lowercase `log`\nfield. Master fails to compile.\n\nConvert the call to the slog builder style used elsewhere in the file.\n\n* Migrate DataFormats and DbLedgerStorageDataFormats from protobuf-java to LightProto\n\nReplace Google\u0027s protobuf-java with StreamNative LightProto for the storage and\nmetadata formats in `bookkeeper-proto`. The wire protocol (`BookkeeperProtocol`)\nremains on protobuf-java for now.\n\nLightProto generates mutable, reusable, ByteBuf-aware classes with built-in\nproto2 TextFormat (de)serialization (via `generateTextFormat\u003dtrue`), so the\nexisting TextFormat-based znode payloads (cookies, auditor votes, lock data,\nunderreplication entries, layout) round-trip byte-identically.\n\nNotable behavior changes:\n- `BookKeeper.DigestType.toProtoDigestType` now returns the LightProto-generated\n  enum (same constants, different package).\n- v3 ledger metadata uses a hand-rolled length-prefixed delimited writer/reader\n  matching protobuf\u0027s `writeDelimitedTo`/`mergeDelimitedFrom`.\n\n* Add SpotBugs excludes for lightproto-generated classes\n\nThe existing exclude `~org.apache.bookkeeper.proto.DataFormats.*` matched\nprotobuf\u0027s nested `DataFormats$LedgerMetadataFormat` etc. LightProto generates\nthe same messages as flat top-level classes (`LedgerMetadataFormat` directly\nin `org.apache.bookkeeper.proto`), so those weren\u0027t excluded and triggered\n12 bugs in the generated code (bit-twiddling, exposed internal byte arrays,\netc.) that aren\u0027t actionable.\n\nReplace the obsolete `DataFormats.*` exclude with explicit per-message\npatterns covering both packages and `LightProtoCodec` (also generated\nper-package).\n\n* Fix checkstyle: remove redundant same-package import in MockBookies\n\n* Migrate BookkeeperProtocol from protobuf-java to LightProto\n\nMigrates the BookkeeperProtocol.proto wire protocol to use LightProto for\nserialization. Combined with the prior migrations of DataFormats and\nDbLedgerStorageDataFormats, this drops the protobuf-java runtime dependency\nfrom bookkeeper-proto entirely.\n\nLightProto produces wire-compatible output with protoc for the same .proto,\nso on-the-wire bookie/client compatibility is preserved.\n\nNotes on lifecycle handling:\n- LightProto messages parsed from a ByteBuf hold lazy references into that\n  buffer for field access. The decoders now call materialize() on parsed\n  Request/Response/AuthMessage instances so they survive after the source\n  buffer is released.\n- Server response paths that put entry payloads into ReadLacResponse or\n  ReadResponse now copy the bytes via ByteBufUtil.getBytes(...), matching\n  the previous ByteString.copyFrom semantics.\n\nDrive-by fix: processWriteLacRequestV3/processReadLacRequestV3 were\nordering work on r.getAddRequest().getLedgerId() instead of the matching\nWriteLac/ReadLac request. With protobuf this returned a default 0 for the\nunset field; with LightProto it throws IllegalStateException.\n\n* Fix checkstyle: remove redundant same-package imports\n\n* Fix shaded-jar tests after protobuf-java removal\n\nprotobuf-java is no longer pulled in transitively by bookkeeper-server,\nso the shaded jars no longer contain (shaded) protobuf classes, and the\nflat lightproto-generated classes have replaced the BookkeeperProtocol\nouter class.\n\n- BookKeeperServerShadedJarTest / DistributedLogCoreShadedJarTest:\n  drop the now-irrelevant testProtobufShadedPath checks and switch the\n  BookkeeperProtocol presence check to a real lightproto class\n  (AddRequest).\n- Drop the dead com.google.protobuf:protobuf-java \u003cinclude\u003e from the\n  three shade plugin configs (bookkeeper-server-shaded,\n  bookkeeper-server-tests-shaded, distributedlog-core-shaded).\n\n* Use ByteBufList.toByteBuf to avoid copying request bodies\n\nReplace ByteBufList.coalesce(...) with a new ByteBufList#toByteBuf method\non the WriteLac and AddEntry request paths. coalesce allocates a new\nbuffer and copies all the bytes; toByteBuf wraps the existing buffers in\na CompositeByteBuf (or returns the single buffer directly when the list\nhas one entry, or Unpooled.EMPTY_BUFFER when empty), transferring\nownership to the caller and releasing the source ByteBufList.\n\nAdds unit tests covering the empty / single / multi-buffer paths,\nincluding ref-count behaviour for both the list and the underlying\nbuffers.\n\n* Fix ByteBufList.toByteBuf to not release the source list\n\nThe previous implementation released the source ByteBufList when\nproducing the wrapping ByteBuf, but callers in PerChannelBookieClient\nonly wrap a buffer they don\u0027t own \u0026mdash; the ByteBufList\u0027s lifecycle is\nmanaged by the upstream PendingAddOp, which shares the same list across\nmultiple bookies in a quorum write. Releasing in toByteBuf produced a\ndouble-release / use-after-free that surfaced as\nIllegalReferenceCountException in tests like BookieStickyReadsTest.\n\nChange toByteBuf to leave the source list\u0027s ref count untouched and\nreturn a wrapper that holds its own retains (a CompositeByteBuf for\nmultiple buffers, a retainedDuplicate for the single-buffer fast path,\nor Unpooled.EMPTY_BUFFER for empty). This matches the original\nByteBufList.coalesce semantics from the caller\u0027s point of view, while\nstill avoiding the byte copy.\n\nTests updated to assert the new ownership semantics.\n\n* Don\u0027t read required ledgerId/entryId from error responses\n\nFour completion handlers (AddCompletion, ForceLedgerCompletion,\nReadCompletion, WriteLacCompletion) were always reading the inner\n*Response\u0027s ledgerId/entryId fields, even when the outer Response\ncarried an error status. On error responses (e.g. EUA from a rejected\nSASL handshake) those required fields are not populated. Under\nprotobuf-java they returned the default 0; under LightProto they throw\nIllegalStateException(\"Field \u0027ledgerId\u0027 is not set\"), which surfaced as\nGSSAPIBookKeeperTest.testNotAllowedClientId blowing up on the client\nside after the server rejected the auth.\n\nRead the inner response only when status is EOK and hasXxxResponse() is\ntrue. Otherwise fall back to the request\u0027s ledgerId/entryId, which the\nCompletionValue base class already records.\n\n* Read response ledgerId/entryId on long-poll reads in ReadCompletion\n\nLong-poll reads send entryId\u003dLAST_ADD_CONFIRMED and the bookie fills in\nthe actual entry id (and ledgerId) on the response when an entry is\nreturned. The previous fix in ReadCompletion always used the request\u0027s\nrecorded entryId, which made the long-poll path look like an empty\npiggy-back response on the client side and dropped the entry buffer.\n\nRead ledgerId/entryId from the response when status is EOK and the\ninner ReadResponse is present; fall back to the request\u0027s recorded\nvalues only on error envelopes (where the inner response may be\nmissing or unpopulated). Fixes\nTestReadLastConfirmedAndEntry.testRaceOnLastAddConfirmed."
    },
    {
      "commit": "9633bb740e946c13ee685af887863ec86f6d6460",
      "tree": "10ee90a5c1470240ae12bc2bd56b5c921cc8e3c6",
      "parents": [
        "c2866286cebc2c945ce86e741fc50fbb46a58077"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Fri May 08 16:02:19 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 08 16:02:19 2026 -0700"
      },
      "message": "Migrate stream gRPC protos to LightProto (#4783)\n\n* Migrate stream gRPC protos to LightProto\n\nMigrates the remaining stream protos (the ones with gRPC services and\ntheir imports) from `protobuf-java` to LightProto:\n\n- `stream/proto/src/main/proto/`: cluster, common, kv, kv_rpc, kv_store,\n  storage, stream — all switched to lightproto-only generation. The\n  protobuf-maven-plugin (and protoc-gen-grpc-java) are removed; the\n  lightproto plugin produces both message classes and gRPC service stubs\n  natively (the same pattern oxia-java uses).\n- `stream/tests-common/src/main/proto/rpc.proto`: same migration. The\n  unused `proto2_coder_test_messages.proto` is deleted (it relied on\n  proto2 `extensions` which lightproto doesn\u0027t support, and had no Java\n  references).\n\nJava sources and tests across stream/{api,statelib,storage/api,storage/impl,\nclients/java/{base,kv,all},server,common,tests-common}, tools/stream and\ntests/integration/cluster were updated to the lightproto API:\n\n- `Foo.newBuilder().setX(v).build()` → `new Foo().setX(v)` (no .build())\n- `Foo.newBuilder(other)` → `new Foo().copyFrom(other)`\n- nested-message setters: `outer.setInner(inner)` → `outer.setInner().copyFrom(inner)`\n- repeated-message setters: `addRequests(req)` → `addRequest().copyFrom(req)`\n  (singular `addX()` returns the new instance to mutate)\n- bytes: `UnsafeByteOperations.unsafeWrap(b)` → just `b`; `getX().toByteArray()`\n  → just `getX()` (returns byte[] directly)\n- parsing: static `Foo.parseFrom(byte[])` → `Foo f \u003d new Foo(); f.parseFrom(b)`\n- `getXMap()` / `getXList()` getter renames where lightproto uses different\n  pluralisation (e.g. `getRoEndpointCount` → `getRoEndpointsCount`,\n  `getRequests(i)` → `getRequestAt(i)`)\n- boolean accessors `getX()` → `isX()` for primitive booleans\n- enums: drop `UNRECOGNIZED` checks (lightproto enums don\u0027t have it)\n- `InvalidProtocolBufferException` / `CodedOutputStream` removed; lightproto\n  parses to/from `ByteBuf` directly and throws `RuntimeException` on bad input\n\nLightProto\u0027s plugin only emits async + blocking stubs — it does not emit\n`*FutureStub`. Where the existing client code relied on `ListenableFuture`\nAPIs, four hand-written `*FutureStub` adapters were added under\n`stream/clients/java/base/src/main/java/org/apache/bookkeeper/clients/grpc/`\n(`RootRangeServiceFutureStub`, `MetaRangeServiceFutureStub`,\n`StorageContainerServiceFutureStub`, `TableServiceFutureStub`). They wrap\n`AbstractStub` and call `ClientCalls.futureUnaryCall` against the\nlightproto-generated `MethodDescriptor`s, so the rest of the client code\nneeded only an import swap.\n\nA handful of tests had `assertSame(req, receivedReq)` /\n`assertTrue(a \u003d\u003d b)` checks that previously worked because gRPC\u0027s\nin-process transport happened to pass protobuf-java messages by reference.\nLightProto\u0027s gRPC marshaller always serializes/deserializes (matching the\noxia/pulsar implementation), so those identity checks were switched to\n`assertEquals` / `.equals(...)`.\n\n* Slice rKey in routing header to avoid ByteBuf aliasing bug\n\n`bkctl table put foo bar` and `Table#put(key, value)` route requests by\npassing the key as both the partition key and the local key. The simple\ntable flow ends up storing the *same* ByteBuf reference in\n`request.key` and `request.header.rKey`. LightProto\u0027s serializer for\n`bytes` fields backed by ByteBuf calls `dst.writeBytes(src)`, which\nadvances `src`\u0027s readerIndex; once the request body\u0027s `key` field has\nbeen written, the same ByteBuf has zero readable bytes left, so when\nthe routing header\u0027s `rKey` field is serialized next it writes nothing.\n\nThe routing header therefore reaches the server with an empty rKey,\nthe put goes to the wrong storage container, and a cross-language\nreader (e.g. the Python integration test) computing the correct\nrouting key against the actual key bytes finds no value.\n\nFix: pass `pKey.slice()` to `RoutingHeader#setRKey` in both\n`PByteBufSimpleTableImpl` and `PByteBufTableRangeImpl` so the routing\nheader gets its own readerIndex independent of any other ByteBuf\nfields aliasing the same buffer.\n\n* Slice every ByteBuf passed to a lightproto setX(ByteBuf)\n\nThe previous fix only sliced pKey before setRKey. There\u0027s a second\nmanifestation of the same underlying issue: the integration test\nTableClientTest reuses the same lKey/value ByteBuf across two\nconsecutive put() calls. After the first request is serialized,\nlightproto\u0027s writeBytes(src) has consumed lKey\u0027s readerIndex, so the\nsecond request\u0027s setKey(lKey) records _keyLen \u003d 0, and the wire format\nomits the key field entirely. The second put silently writes under an\nempty key, prevKv comes back null, and the test fails.\n\nWrap every user-supplied ByteBuf with .slice() inside KvUtils before\nhanding it to a lightproto setter (newPutRequest, newRangeRequest,\nnewDeleteRequest, newIncrementRequest, populateProtoCompare,\npopulateProtoPutRequest, populateProtoDeleteRequest,\npopulateProtoRangeRequest). The slice gives lightproto its own\nreaderIndex over the same backing data, so mutating it during\nserialization doesn\u0027t affect the caller\u0027s buffer or any other field\nbacked by the same ByteBuf.\n\nThe right long-term fix is in lightproto itself — setX(ByteBuf) should\neither slice internally or use the non-mutating dst.writeBytes(src,\nidx, len). Worth filing upstream."
    },
    {
      "commit": "c2866286cebc2c945ce86e741fc50fbb46a58077",
      "tree": "979620fa6fc4cf873405f3e587205577ae8dcbd0",
      "parents": [
        "a0231ede679132fc5362c17b104d41383d828ec1"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Thu May 07 22:55:35 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 08 08:55:35 2026 +0300"
      },
      "message": "Migrate stream non-gRPC protos to LightProto (#4781)\n\n* Fix LedgerHandle.batchReadUnconfirmedAsync: use slog log instead of LOG\n\nThe batchReadUnconfirmedAsync method added in #4739 calls LOG.error(...),\nbut LedgerHandle was migrated to slog and only has a lowercase `log`\nfield. Master fails to compile.\n\nConvert the call to the slog builder style used elsewhere in the file.\n\n* Migrate stream non-gRPC protos to LightProto\n\nMigrates the stream module\u0027s non-gRPC proto definitions to LightProto:\n- stream/statelib/src/main/proto/kv.proto (KV state-store commands)\n- stream/proto/src/main/proto/cluster.proto (cluster metadata/assignment)\n\nBoth files use proto3, including oneof (kv.proto) and map (cluster.proto),\nwhich LightProto handles directly.\n\nFor stream/proto, cluster.proto is moved to a parallel proto-lightproto/\ndirectory so the existing protobuf-maven-plugin keeps generating the rest\nof the protos (which still depend on the gRPC service stubs). Java sources\nthat touched the migrated types are updated to use the LightProto\nmutable-instance API instead of the protobuf-java builder pattern.\n\nThis change is independent of the other in-flight LightProto work in\nbookkeeper-proto and bookkeeper-server. gRPC service migration is left\nfor a follow-up.\n\n* Fix checkstyle: remove unused ServerAssignmentData import"
    },
    {
      "commit": "a0231ede679132fc5362c17b104d41383d828ec1",
      "tree": "7cf53215112b1910aa3277c3b416e5a7c77717a9",
      "parents": [
        "12d4bbdfedfa8372638b20268c3b7e0a530ce217"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Thu May 07 22:54:36 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri May 08 08:54:36 2026 +0300"
      },
      "message": "Fix LedgerHandle.batchReadUnconfirmedAsync: use slog log instead of LOG (#4782)\n\nThe batchReadUnconfirmedAsync method added in #4739 calls LOG.error(...),\nbut LedgerHandle was migrated to slog and only has a lowercase `log`\nfield. Master fails to compile.\n\nConvert the call to the slog builder style used elsewhere in the file."
    },
    {
      "commit": "12d4bbdfedfa8372638b20268c3b7e0a530ce217",
      "tree": "7cf53215112b1910aa3277c3b416e5a7c77717a9",
      "parents": [
        "58c850356271360dc9030ca85cf3f61429a8faaa"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Thu May 07 21:31:55 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 07 21:31:55 2026 -0700"
      },
      "message": "Migrate DataFormats from protobuf-java to LightProto (#4779)\n\n* Fix LedgerHandle.batchReadUnconfirmedAsync: use slog log instead of LOG\n\nThe batchReadUnconfirmedAsync method added in #4739 calls LOG.error(...),\nbut LedgerHandle was migrated to slog and only has a lowercase `log`\nfield. Master fails to compile.\n\nConvert the call to the slog builder style used elsewhere in the file.\n\n* Migrate DataFormats and DbLedgerStorageDataFormats from protobuf-java to LightProto\n\nReplace Google\u0027s protobuf-java with StreamNative LightProto for the storage and\nmetadata formats in `bookkeeper-proto`. The wire protocol (`BookkeeperProtocol`)\nremains on protobuf-java for now.\n\nLightProto generates mutable, reusable, ByteBuf-aware classes with built-in\nproto2 TextFormat (de)serialization (via `generateTextFormat\u003dtrue`), so the\nexisting TextFormat-based znode payloads (cookies, auditor votes, lock data,\nunderreplication entries, layout) round-trip byte-identically.\n\nNotable behavior changes:\n- `BookKeeper.DigestType.toProtoDigestType` now returns the LightProto-generated\n  enum (same constants, different package).\n- v3 ledger metadata uses a hand-rolled length-prefixed delimited writer/reader\n  matching protobuf\u0027s `writeDelimitedTo`/`mergeDelimitedFrom`.\n\n* Add SpotBugs excludes for lightproto-generated classes\n\nThe existing exclude `~org.apache.bookkeeper.proto.DataFormats.*` matched\nprotobuf\u0027s nested `DataFormats$LedgerMetadataFormat` etc. LightProto generates\nthe same messages as flat top-level classes (`LedgerMetadataFormat` directly\nin `org.apache.bookkeeper.proto`), so those weren\u0027t excluded and triggered\n12 bugs in the generated code (bit-twiddling, exposed internal byte arrays,\netc.) that aren\u0027t actionable.\n\nReplace the obsolete `DataFormats.*` exclude with explicit per-message\npatterns covering both packages and `LightProtoCodec` (also generated\nper-package).\n\n* Fix checkstyle: remove redundant same-package import in MockBookies"
    },
    {
      "commit": "58c850356271360dc9030ca85cf3f61429a8faaa",
      "tree": "ca74524536679fa0d501d4c5590c879adc93a349",
      "parents": [
        "ad8f4fc3338456e687abab2fc61f00a9108fabf0"
      ],
      "author": {
        "name": "道君- Tao Jiuming",
        "email": "daojun@apache.org",
        "time": "Fri May 08 05:16:40 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu May 07 14:16:40 2026 -0700"
      },
      "message": "[[BP-62] Expose batchReadUnconfirmedAsync to ReadHandle (#4739)\n\n* Expose batchReadUnconfirmedAsync to ReadHandle.java\n\n* Address review comments"
    },
    {
      "commit": "ad8f4fc3338456e687abab2fc61f00a9108fabf0",
      "tree": "f6b806dcfa16646bc048f61dde37bbe0a665c608",
      "parents": [
        "a871f6efc2076032a9dc61b6f2e23674bc1a99cd"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Wed May 06 02:01:46 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 16:01:46 2026 -0700"
      },
      "message": "Migrate from Yahoo DataSketches to Apache DataSketches 7.0.1 (KLL) (#4774)\n\nThe com.yahoo.datasketches:sketches-core:0.8.3 dependency has been\nunmaintained since the project moved to Apache. Switch to the current\norg.apache.datasketches:datasketches-java:7.0.1 and adopt the KLL\nquantiles sketch, which the upstream maintainers recommend over the\nclassic DoublesSketch for new code (faster, smaller, comparable\naccuracy at default K\u003d200).\n\nDataSketchesOpStatsLogger changes:\n- On-heap throughout, matching the previous code (no off-heap usage).\n- KLL has no reset(); per-thread sketches are reassigned to a fresh\n  newHeapInstance() under the existing StampedLock write lock.\n- KLL has no DoublesUnion; aggregation uses merge() directly into a\n  freshly allocated KllDoublesSketch each rotation.\n- KllDoublesSketch.getQuantile() throws SketchesArgumentException on\n  an empty sketch, so getQuantileValue() now guards with isEmpty() to\n  preserve the prior NaN return.\n\nLICENSE bundles list datasketches-java-7.0.1.jar and the transitively\nrequired datasketches-memory-4.1.0.jar separately, since the two jars\ncome from distinct upstream repositories."
    },
    {
      "commit": "a871f6efc2076032a9dc61b6f2e23674bc1a99cd",
      "tree": "64e36073943fb4a04b3605bf930961be5d2d3af8",
      "parents": [
        "28b3336910e029eb208bd4f5c19d03423bbe4ee5"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Tue May 05 18:09:18 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 08:09:18 2026 -0700"
      },
      "message": "Upgrade jctools to 4.0.6 (jctools-core-jdk11) (#4776)\n\n* Upgrade jctools to 4.0.6 (jctools-core-jdk11)\n\nSwitch from org.jctools:jctools-core to org.jctools:jctools-core-jdk11\nand bump version 4.0.5 -\u003e 4.0.6.\n\nThe jctools-core-jdk11 artifact is a drop-in replacement that adds\nproper Java 9 module support (module-info) on top of jctools-core,\nwhich is brought in transitively. Both jars are bundled in the\ndistribution, so the LICENSE files reference both.\n\nRelease notes: https://github.com/JCTools/JCTools/releases/tag/v4.0.6\nChangelog: https://github.com/JCTools/JCTools/compare/v4.0.5...v4.0.6\n\n* Add jctools-core to dependency management\n\nKeep both jctools-core and jctools-core-jdk11 in \u003cdependencyManagement\u003e\nso their versions stay aligned. jctools-core-jdk11 transitively depends\non jctools-core, and pinning both ensures any consumer that pulls in\neither artifact directly resolves to the same version."
    },
    {
      "commit": "28b3336910e029eb208bd4f5c19d03423bbe4ee5",
      "tree": "bc53de5639a2245fd0d53d4ddce0809b0d456858",
      "parents": [
        "e42928e73b97580e913d3d59bc7e70ebe91220fe"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Tue May 05 13:16:41 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 18:16:41 2026 +0800"
      },
      "message": "Upgrade Netty to 4.2.13 to address CVEs (#4775)"
    },
    {
      "commit": "e42928e73b97580e913d3d59bc7e70ebe91220fe",
      "tree": "e5c4eebb0f7abf688f4708662f6202a9da202070",
      "parents": [
        "bb1a7cbf9ec8aafbe2161dfdd5c95c1b046348ef"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue May 05 02:51:57 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 12:51:57 2026 +0300"
      },
      "message": "BP-69: Convert remaining SLF4J usages in non-test code to slog (#4765)\n\n* BP-69: Convert remaining SLF4J usages in non-test code to slog\n\nFinal pass to retire SLF4J from production-style code paths. After this,\nthe only remaining LoggerFactory.getLogger() in non-test code is the\nintentional Codahale Slf4jReporter integration in CodahaleMetricsProvider,\nwhich forwards stats to a named SLF4J logger and must stay on the SLF4J\nAPI.\n\nConversion patterns (same as earlier BP-69 phases):\n- LoggerFactory.getLogger(Foo.class) / @Slf4j → @CustomLog\n- LOG.info(\"text {} {}\", a, b) → log.info().attr(\"name1\", a).attr(\"name2\", b).log(\"text\")\n- LOG.error(\"msg\", exception) → log.error().exception(exception).log(\"msg\")\n- if (LOG.isDebugEnabled()) LOG.debug(\"...{}\", expr)\n    → log.debug().attr(\"name\", () -\u003e expr).log(\"...\")\n  using the lazy-supplier overload to preserve zero-overhead-when-disabled\n  semantics on attrs whose value is a method call.\n\nFiles: 48 across bookkeeper-common-allocator, bookkeeper-common,\nbookkeeper-server, circe-checksum, cpu-affinity,\ntests/integration-tests-utils, tests/integration-tests-topologies.\n\nIOUtils: removed the two SLF4J overloads of close() (kept \"for transition\nduring the slog migration\" per the doc comment). The transition is\ncomplete — no remaining callers in non-test code.\n\n* Suppress SpotBugs unwritten-field warnings on ZooKeeperAwaitStrategy\n\nThe `cube` and `dockerClientExecutor` fields are populated by Arquillian\nvia reflection. Removing the SLF4J `LOG` field as part of the slog\nconversion left those fields as the only declared instance state, which\nSpotBugs now flags as UWF_UNWRITTEN_FIELD / NP_UNWRITTEN_FIELD.\nAnnotate the class to suppress these warnings.\n\n* Update BookieInitializationTest assertion for slog message text\n\nThe slog conversion in this PR changed the exception-handler log message\nfrom \"Triggered exceptionHandler of Component: {}\" (with placeholder)\nto \"Triggered exceptionHandler of Component\" (component name moved to\na structured attr). Drop the trailing colon from the test\u0027s substring\nmatch so it matches the new literal message.\n\n* Print structured attributes in log4j2 patterns; add OTel JSON example\n\nSlog attributes are forwarded to log4j2 as ContextData (the same channel\nSLF4J\u0027s MDC uses). Add %X to every PatternLayout in the user-facing\nlog4j2 configs so the structured attributes attached via .attr(...)\nappear in log output by default. Drop the now-redundant *MDC variant\nappenders and the directentrylogger override that targeted them.\n\nAlso add conf/log4j2.otel-json.xml plus conf/otel-log-template.json as\nan example layout that emits each event as a JSON object aligned with\nthe OpenTelemetry log data model (severity_text, severity_number, body,\nattributes, resource, instrumentation_scope, trace_id/span_id from MDC).\nThe example uses log4j-layout-template-json, which must be added to the\nclasspath alongside log4j-core to enable JsonTemplateLayout.\n\n* Snapshot log events eagerly in LoggerOutput\n\nSlog\u0027s Log4j2 backend (BaseLogger / Log4j2Logger) reuses a thread-local\nMutableLogEvent per thread for allocation-free emission: the same event\ninstance is cleared and refilled on every log call. The test rule\npreviously captured those events via Mockito\u0027s ArgumentCaptor, which\nstores references rather than snapshots — so by the time the test read\nthem, every captured event aliased to the last log emitted on each\nthread. Earlier events on a thread silently lost their message and\nattributes.\n\nReplace the ArgumentCaptor with a doAnswer that calls\nLogEvent.toImmutable() at append time, copying each event into a stable\nsnapshot before the underlying buffer can be reused."
    },
    {
      "commit": "bb1a7cbf9ec8aafbe2161dfdd5c95c1b046348ef",
      "tree": "124e0d4609016778bc4267ee18d6ace9c19b0619",
      "parents": [
        "354cc549655d46a39abec28f29feff91e3fc3125"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue May 05 01:00:47 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 11:00:47 2026 +0300"
      },
      "message": "[FIX] Avoid killing the Auditor leader in BookieAutoRecoveryTest (#4769)\n\nThree tests in BookieAutoRecoveryTest were flaking in CI because they\nsometimes killed the bookie that happened to be the Auditor leader, and\nthe leader-failover chain (10s AuditorElector shutdown timeout + ZK\nsession expiry + new election + first audit cycle) routinely exceeded\nthe tests\u0027 20s/90s latch timeouts under CI load.\n\nMake the bookie-to-kill choice deterministic w.r.t. the Auditor:\n\n- testOpenLedgers, testClosedLedgers, testStopWhileReplicationInProgress,\n  testNoSuchLedgerExists: pick a non-Auditor bookie from the ensemble.\n- testEmptyLedgerLosesQuorumEventually needs to kill ensemble[2] then\n  ensemble[1] specifically (it tests non-write-quorum bookie loss).\n  Pin the Auditor onto ensemble[0] by stopping AutoRecovery on the two\n  bookies the test will kill. Any slow failover happens in pre-flight,\n  outside the timed assertions."
    },
    {
      "commit": "354cc549655d46a39abec28f29feff91e3fc3125",
      "tree": "40a5049df54acd100d262e52ecf832489190263b",
      "parents": [
        "de34d1434cd3259b498dfbef61f3d7a26ee93251"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue May 05 01:00:01 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 11:00:01 2026 +0300"
      },
      "message": "Log only overridden config in BookieServer startup (#4768)\n\nReplace the JSON dump of the entire ServerConfiguration with a\n\"Starting Bookie server\" message that carries an \"overrides\" attribute\nholding only the entries that differ from the built-in defaults."
    },
    {
      "commit": "de34d1434cd3259b498dfbef61f3d7a26ee93251",
      "tree": "8eafad3d22a9f4c06fac5d56b07e559c51403240",
      "parents": [
        "66a6d5d434824ccf0ccbcc090c3ba004fe7cccc1"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue May 05 00:58:41 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 10:58:41 2026 +0300"
      },
      "message": "BP-69: Inherit LedgerHandle logger context in ledger operations (#4767)\n\n* BP-69: Inherit LedgerHandle logger context in ledger operations\n\nOperations created in the context of a LedgerHandle (PendingAddOp,\nPendingReadOp, BatchedReadOp, ReadOpBase, ForceLedgerOp,\nTryReadLastConfirmedOp, ReadLastConfirmedAndEntryOp, LedgerRecoveryOp,\nPendingReadLacOp, PendingWriteLacOp) now attach LedgerHandle.log\u0027s\ncontext (ledgerId, etc.) to every emitted event via Event.ctx(lh.log).\n\nUsing per-event ctx() rather than building a child Logger per op keeps\nthe static @CustomLog field allocation-free at the call site, while\nstill ensuring every operation log carries its parent ledger context.\n\nReadLastConfirmedAndEntryOp and LedgerRecoveryOp previously built an\ninstance-scoped Logger to carry the ledger context; they now use the\nstatic @CustomLog logger plus per-event ctx(lh.log), removing one\nLogger allocation per operation.\n\nRedundant .attr(\"ledgerId\", ...) calls (already provided by\nLedgerHandle.log\u0027s context) are removed.\n\n* Bump slog to 0.9.9\n\nslog 0.9.9 makes Event.ctx(null) a no-op instead of throwing NPE,\nwhich lets the operation classes call .ctx(lh.log) directly even\nwhen the parent logger is unset (e.g. tests using mock LedgerHandle\nwhere the log field is null)."
    },
    {
      "commit": "66a6d5d434824ccf0ccbcc090c3ba004fe7cccc1",
      "tree": "f0a66c8764ab3f456c432f31bb4b08efe7664f77",
      "parents": [
        "db5cd4e2a2323e5ec13c7d9753198e44bbbcc4b7"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue May 05 00:58:14 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 10:58:14 2026 +0300"
      },
      "message": "BP-69: Allow caller to pass a parent slog Logger to created/opened ledgers (#4766)\n\nAdd CreateBuilder.withLoggerContext(io.github.merlimat.slog.Logger) and\nOpenBuilder.withLoggerContext(io.github.merlimat.slog.Logger) so an\napplication that already has a per-request / per-tenant slog Logger can\npass it as the parent of the per-handle Logger that bookkeeper builds.\nThe LedgerHandle\u0027s logger inherits the parent\u0027s context via slog\u0027s\nLoggerBuilder.ctx(Logger), then layers on the always-present `ledgerId`.\n\nPassing a Logger (vs. a Map\u003cString, Object\u003e) avoids the need to\nextract/serialize attrs out of slog: the application\u0027s Logger is\ntypically the natural source of context already.\n\nImplementation:\n- New default no-op method on the @Public/@Unstable CreateBuilder /\n  OpenBuilder interfaces to keep source compatibility for any\n  out-of-tree implementor.\n- CreateBuilderImpl, OpenBuilderBase store the parent Logger and pass\n  it through to LedgerCreateOp / LedgerOpenOp, which forward it to\n  LedgerHandle / LedgerHandleAdv / ReadOnlyLedgerHandle constructor.\n- New constructor overloads on the three handle types take a\n  parentLogger; existing constructors delegate with null.\n- LedgerOpenOp\u0027s own contextual log is built with the same parent so\n  log events emitted during the open path also carry the attrs.\n\nTests: new LoggerContextTest covers compile-time API contract\n(chainable, accepts a Logger), runtime happy-path (null is no-op,\nnon-null context map executes cleanly), and a deterministic check via\na log4j2 capturing appender that the parent\u0027s attrs flow into the\nevent\u0027s ContextData on the resulting handle\u0027s Logger via the\nLoggerBuilder.ctx(Logger) hook."
    },
    {
      "commit": "db5cd4e2a2323e5ec13c7d9753198e44bbbcc4b7",
      "tree": "8f37009ecfb314308ea1af3e6bfe3de6c7915d97",
      "parents": [
        "449fe78dd51f2eb3e73e3c5d577851ff18144025"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue May 05 00:53:40 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 10:53:40 2026 +0300"
      },
      "message": "BP-69: Remove bookkeeper-slogger module (#4764)\n\nConvert the remaining bookkeeper-slogger callsites in bookkeeper-server\nto the slog library and remove the bookkeeper-slogger module entirely.\n\nConversion mapping:\n- new Slf4jSlogger(Foo.class)                  → Logger.get(Foo.class)\n- slog.kv(\"k\", v).info(Events.X)               → log.info().attr(\"k\", v).log(Events.X.toString())\n- slog.kv(...).warn/error(Events.X, t)         → log.warn/error().exception(t).attr(...).log(Events.X.toString())\n- slogParent.kv(\"dir\", d).ctx(Foo.class)       → Logger.get(Foo.class).with().ctx(parent).attr(\"dir\", d).build()\n- Slogger.NULL / Slogger.CONSOLE in tests      → Logger.get(TestClass.class)\n\nFiles converted:\n- 5 main: DirectEntryLogger, DirectWriter, DirectCompactionEntryLog,\n  EntryLogIdsImpl, DbLedgerStorage\n- 9 tests: GarbageCollectorThreadTest, EntryLogTestUtils, TestMetadata,\n  TestTransactionalEntryLogCompactor, TestDirectWriter,\n  TestDirectEntryLogger, TestDirectReader, TestDirectEntryLoggerCompat,\n  TestEntryLogIds\n\nModule removal:\n- Removed bookkeeper-slogger/ directory tree (api + slf4j + tests).\n- Dropped \u003cmodule\u003ebookkeeper-slogger\u003c/module\u003e from root pom.xml.\n- Dropped bookkeeper-slogger-api / bookkeeper-slogger-slf4j deps from\n  bookkeeper-server/pom.xml.\n\nVerified: mvn -pl bookkeeper-server compile checkstyle:check passes;\nall 64 tests under Test*EntryLog* / Test*Direct* / GarbageCollectorThreadTest\n/ TestMetadata pass."
    },
    {
      "commit": "449fe78dd51f2eb3e73e3c5d577851ff18144025",
      "tree": "106d9538956756d11821854882d75c617ba9d590",
      "parents": [
        "0b7cc3d0a442a93bd4751f6f558dfb474a0ef20c"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue May 05 00:51:57 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue May 05 10:51:57 2026 +0300"
      },
      "message": "BP-69: Print slog context attrs in log4j2; add OTel JSON example (#4770)\n\n* Print structured attributes in log4j2 patterns; add OTel JSON example\n\nSlog attributes are forwarded to log4j2 as ContextData (the same channel\nSLF4J\u0027s MDC uses). Add %X to every PatternLayout in the user-facing\nlog4j2 configs so the structured attributes attached via .attr(...)\nappear in log output by default. Drop the now-redundant *MDC variant\nappenders and the directentrylogger override that targeted them.\n\nAlso add conf/log4j2.otel-json.xml plus conf/otel-log-template.json as\nan example layout that emits each event as a JSON object aligned with\nthe OpenTelemetry log data model (severity_text, severity_number, body,\nattributes, resource, instrumentation_scope, trace_id/span_id from MDC).\nThe example uses log4j-layout-template-json, which must be added to the\nclasspath alongside log4j-core to enable JsonTemplateLayout.\n\n* log4j2: render context map after the message\n\n* Align OTel JSON template with the OpenTelemetry log data model\n\nReplace the previous template with the structure used by Pulsar\n(apache/pulsar/blob/master/conf/OtelLogLayout.json):\n  * Top-level fields use the spec\u0027s PascalCase naming (Timestamp,\n    SeverityText, Body, TraceId, SpanId, TraceFlags, Attributes).\n  * thread.name, thread.id, code.namespace, exception.{type,message,\n    stacktrace}, and the flattened MDC are placed under \"Attributes\",\n    using OTel semantic-convention keys.\n  * Drop SeverityNumber, ObservedTimestamp, InstrumentationScope, and\n    Resource — the first two are derivable / redundant for app-emitted\n    logs, the logger name is \"code.namespace\" not InstrumentationScope,\n    and Resource is normally attached by the collector pipeline.\n  * The MDC resolver with flatten\u003dtrue now sits inside Attributes, so\n    slog .attr(...) keys merge as siblings of thread.name etc. (the\n    previous placement at the top level didn\u0027t merge into \"attributes\"\n    as intended).\n\nUpdate the explanatory comment block and drop the now-unused\notel.service.name / otel.host.name properties."
    },
    {
      "commit": "0b7cc3d0a442a93bd4751f6f558dfb474a0ef20c",
      "tree": "8e40beadd64cf42ab9d043b6a3e81b2cc213ee4d",
      "parents": [
        "34ec6a45dfc20da6bf8f47d123604886e512829d"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Tue May 05 00:09:35 2026 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon May 04 14:09:35 2026 -0700"
      },
      "message": "Upgrade OpenTelemetry libraries to 1.61.0 / 2.27.0 (#4773)\n\n* Upgrade OpenTelemetry libraries\n\n* Migrate OtelMetricsProvider to RuntimeTelemetry and ExplicitBucketHistogramOptions\n\nThe runtime-telemetry-java8 module\u0027s individual observer classes (Classes,\nCpu, GarbageCollector, MemoryPools, Threads, ExperimentalBufferPools) were\nremoved in opentelemetry-instrumentation 2.27.0; switch to the unified\nopentelemetry-runtime-telemetry module\u0027s RuntimeTelemetry builder.\n\nAlso replace the Aggregation.explicitBucketHistogram(List\u003cDouble\u003e) overload\ndeprecated in opentelemetry-java SDK 1.60.0 with the new\nExplicitBucketHistogramOptions form.\n\n* Fix license files"
    },
    {
      "commit": "34ec6a45dfc20da6bf8f47d123604886e512829d",
      "tree": "3a7957abfcd53c6f4c992118d4380557a96e6e0b",
      "parents": [
        "697bd594dffb2cd5b82695106e1b5560e17f0840"
      ],
      "author": {
        "name": "void-ptr974",
        "email": "void-ptr974@protonmail.com",
        "time": "Sun May 03 10:07:10 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat May 02 19:07:10 2026 -0700"
      },
      "message": "[Fix] ConcurrentLongHashMap throw ArrayIndexOutOfBoundsException (#4771)\n\n* fix ConcurrentLongHashMap throw ArrayIndexOutOfBoundsException\n\nWhen concurrent read write access the map, The key array and value array are not publish at the same time when shrink or expand.\n\nThis fix encapsulate the key and value in the same field to avoid this happen\n\n* fix ConcurrentLongHashMap throw ArrayIndexOutOfBoundsException\n\nWhen concurrent read write access the map, The key array and value array are not publish at the same time when shrink or expand.\n\nThis fix encapsulate the key and value in the same field to avoid this happen\n\n* fix ConcurrentLongHashMap throw ArrayIndexOutOfBoundsException\n\nWhen concurrent read write access the map, The key array and value array are not publish at the same time when shrink or expand.\n\nThis fix encapsulate the key and value in the same field to avoid this happen\n\n* fix ConcurrentLongHashMap throw ArrayIndexOutOfBoundsException\n\nWhen concurrent read write access the map, The key array and value array are not publish at the same time when shrink or expand.\n\nThis fix encapsulate the key and value in the same field to avoid this happen\n\n* fix ConcurrentLongHashMap throw ArrayIndexOutOfBoundsException\n\nWhen concurrent read write access the map, The key array and value array are not publish at the same time when shrink or expand.\n\nThis fix encapsulate the key and value in the same field to avoid this happen"
    },
    {
      "commit": "697bd594dffb2cd5b82695106e1b5560e17f0840",
      "tree": "1a9fa880d30c9654bf0c187ca7a53b96922355d6",
      "parents": [
        "46c842ca4a93ce295bf712bcd52287d52da1a0cf"
      ],
      "author": {
        "name": "Enrico Olivelli",
        "email": "eolivelli@apache.org",
        "time": "Thu Apr 30 00:23:34 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 29 15:23:34 2026 -0700"
      },
      "message": "Fix NPE in PendingAddOp.maybeTimeout() when clientCtx is null after recycling (#4760)\n\n* Fix NPE in PendingAddOp.maybeTimeout() when clientCtx is null after recycling\n\nmonitorPendingAddOps() iterates the pendingAddOps queue and calls\nmaybeTimeout() on each op. Concurrently, sendAddSuccessCallbacks()\ncan remove a completed op from the queue and, via submitCallback(),\nlead to recyclePendAddOpObject() which sets clientCtx \u003d null. If the\nscheduler thread still holds an iterator reference to that op and then\ncalls maybeTimeout(), the dereference of clientCtx causes an NPE.\n\nThe race is triggered in practice when addEntryQuorumTimeoutNanos \u003e 0\n(i.e. the quorum-timeout monitor is enabled). It became visible with\nNetty 4.1.130, which changed Recycler thread-scheduling behavior and\nmade the narrow window between the iterator snapshot and the null\nassignment observable.\n\nFix:\n- Make clientCtx volatile so that the null write in the synchronized\n  recyclePendAddOpObject() is immediately visible to the unsynchronized\n  maybeTimeout() reader.\n- Guard the top of maybeTimeout() with an explicit null check: if\n  clientCtx is null the op has already completed and recycled, so\n  there is nothing to time out and the method returns false.\n\nCloses: https://github.com/apache/bookkeeper/issues/4759\n\n* Address review: snapshot clientCtx and guard timeoutQuorumWait against recycle\n\nPer @merlimat\u0027s review on #4760, the original null-check in maybeTimeout()\nstill races: clientCtx may be nulled between the guard and the subsequent\ngetConf() dereference. Volatile only addresses visibility, not atomicity\nacross two reads.\n\n- Snapshot clientCtx into a local in maybeTimeout(); the local cannot be\n  mutated by another thread, so the dereference is race-free.\n- Drop the volatile modifier on clientCtx (no longer load-bearing once the\n  read is done once into a local).\n- Extend timeoutQuorumWait()\u0027s early-return to also short-circuit when\n  lh / clientCtx are null. recyclePendAddOpObject() resets `completed` to\n  false, so the prior `if (completed)` guard does not cover the\n  already-recycled case; without this check timeoutQuorumWait() NPEs on\n  lh.getLedgerMetadata() (and worse, could invoke\n  handleUnrecoverableErrorDuringAdd on a stale handle).\n- Add testTimeoutQuorumWaitIsNoOpWhenAlreadyRecycled covering the new guard.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e\n\n---------\n\nCo-authored-by: Claude Opus 4.7 (1M context) \u003cnoreply@anthropic.com\u003e"
    },
    {
      "commit": "46c842ca4a93ce295bf712bcd52287d52da1a0cf",
      "tree": "2534b818403de5a8761ce828842168c5a05867be",
      "parents": [
        "51abbc248fadd615340b5aeaf2402aa75d91ab54"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue Apr 28 08:23:19 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 28 08:23:19 2026 -0700"
      },
      "message": "BP-69: Convert http, tools, benchmark and metadata-drivers to slog (phase 4+6) (#4758)\n\n* BP-69 base: add slog dependency and LICENSE entries\n\nMinimal scaffolding commit for the BP-69 slog migration series:\n\n- Add `io.github.merlimat.slog:slog:0.9.7` to the root pom.xml\n  dependencyManagement and to the global compile classpath alongside\n  the existing SLF4J API (which stays as the rendering backend).\n- Add lombok.config at the repo root so `@CustomLog` generates a slog\n  `Logger` instead of an SLF4J one.\n- Register the slog jar in LICENSE-all.bin.txt, LICENSE-server.bin.txt\n  and LICENSE-bkctl.bin.txt so the CI check-binary-license script\n  finds it accounted for in the bundled-jars list.\n\nNo actual Java file is converted in this commit. Individual module\nmigrations stack on top of this one.\n\n* BP-69: Convert http, tools, benchmark and metadata-drivers modules to slog\n\n* Bump slog dependency to 0.9.8\n\nPulls in the MDC propagation fix from merlimat/slog#6, which makes\nlog4j2 ThreadContext entries visible on slog events emitted via the\nLog4j2Logger backend (so %X{key} layouts and appenders that read\nevent.getContextData().getValue(key) see the caller\u0027s MDC)."
    },
    {
      "commit": "51abbc248fadd615340b5aeaf2402aa75d91ab54",
      "tree": "bdbda1e1618be6c202e4deb479ef9695b214890e",
      "parents": [
        "a3ec40b1db9c11d93ef0cae8a43c74d6ccd35276"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue Apr 28 08:21:11 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 28 08:21:11 2026 -0700"
      },
      "message": "BP-69: Convert stream/distributedlog modules to slog (phase 5) (#4757)\n\n* BP-69 base: add slog dependency and LICENSE entries\n\nMinimal scaffolding commit for the BP-69 slog migration series:\n\n- Add `io.github.merlimat.slog:slog:0.9.7` to the root pom.xml\n  dependencyManagement and to the global compile classpath alongside\n  the existing SLF4J API (which stays as the rendering backend).\n- Add lombok.config at the repo root so `@CustomLog` generates a slog\n  `Logger` instead of an SLF4J one.\n- Register the slog jar in LICENSE-all.bin.txt, LICENSE-server.bin.txt\n  and LICENSE-bkctl.bin.txt so the CI check-binary-license script\n  finds it accounted for in the bundled-jars list.\n\nNo actual Java file is converted in this commit. Individual module\nmigrations stack on top of this one.\n\n* BP-69: Convert stream module from SLF4J to slog\n\n* Bump slog dependency to 0.9.8\n\nPulls in the MDC propagation fix from merlimat/slog#6, which makes\nlog4j2 ThreadContext entries visible on slog events emitted via the\nLog4j2Logger backend (so %X{key} layouts and appenders that read\nevent.getContextData().getValue(key) see the caller\u0027s MDC)."
    },
    {
      "commit": "a3ec40b1db9c11d93ef0cae8a43c74d6ccd35276",
      "tree": "d482fd4a516e7780d693177b234fa76ab8cbe99b",
      "parents": [
        "22a37eccdd7b167900084c66f50fd4b54311d8a7"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue Apr 28 08:20:52 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 28 08:20:52 2026 -0700"
      },
      "message": "BP-69: Convert bookkeeper-server to slog (phase 3) (#4756)\n\n* BP-69 base: add slog dependency and LICENSE entries\n\nMinimal scaffolding commit for the BP-69 slog migration series:\n\n- Add `io.github.merlimat.slog:slog:0.9.7` to the root pom.xml\n  dependencyManagement and to the global compile classpath alongside\n  the existing SLF4J API (which stays as the rendering backend).\n- Add lombok.config at the repo root so `@CustomLog` generates a slog\n  `Logger` instead of an SLF4J one.\n- Register the slog jar in LICENSE-all.bin.txt, LICENSE-server.bin.txt\n  and LICENSE-bkctl.bin.txt so the CI check-binary-license script\n  finds it accounted for in the bundled-jars list.\n\nNo actual Java file is converted in this commit. Individual module\nmigrations stack on top of this one.\n\n* BP-69: Convert bookkeeper-server from SLF4J to slog\n\n* Bump slog dependency to 0.9.8\n\nAlso includes a few follow-ups now that 0.9.8 is in use:\n- Wrap method-call attr values in Supplier for lazy evaluation on debug/\n  trace log calls that were originally guarded by isXxxEnabled. The\n  fluent slog form would otherwise eagerly evaluate the attr arguments\n  even when the level is disabled.\n- Simplify the response promise in PacketProcessorBase: always attach a\n  ChannelFutureListener (the listener body itself uses log.debug() and\n  is cheap when debug is off) rather than the side-effecting lambda.\n  Mock newPromise() with RETURNS_SELF in ReadEntryProcessorTest so the\n  chained .addListener() returns the same mock.\n- Update MdcContextTest assertions for the new bare slog messages\n  (ledgerId / entryId / firstEntry / lastEntry are now structured attrs\n  rather than baked into the message string). With the slog 0.9.8 fix,\n  MDC propagates through the log4j2 backend and the test verifies that.\n\n* Restore \u0027\u003cnumEntries\u003e entries written to ledger \u003cledgerId\u003e\u0027 message format\n\nThe slog conversion of SimpleTestCommand turned the original\nLOG.info(\"{} entries written to ledger {}\", numEntries, ledgerId)\ninto a structured form that lost the substring TestCLI and\nTestBookieShellCluster\u0027s test001_SimpleTest assert on (\"100 entries\nwritten to ledger\"). The default log4j2 PatternLayout in the\nbookkeeper image prints %m only, so the structured attrs are not\nvisible to the integration tests that inspect command stdout.\n\nUse logf() to bake the values into the message so it again matches\nthe integration-test expectations. The progress-line log on line 118\nsimilarly drops a non-descriptive single-letter \"i\" attr in favor\nof the formatted \u0027\u003ccount\u003e entries written\u0027.\n\n* Restore \u0027LastLogMark : Journal\u0027 message format in lastmark CLI\n\nSame regression as SimpleTestCommand: the slog conversion lost the\nverbatim message text \u0027LastLogMark : Journal Id - \u003cid\u003e(\u003chex\u003e.txn),\nPos - \u003coffset\u003e\u0027 that the integration test\nBkCtlTest.showLastMark asserts on. Default log4j2 PatternLayout\nprints %m only, so structured attrs are not visible to integration\ntests that inspect command stdout.\n\nUse logf() to bake the values into the message."
    },
    {
      "commit": "22a37eccdd7b167900084c66f50fd4b54311d8a7",
      "tree": "cd2256c4181da58739ce4feb1e90aec999c6b1f4",
      "parents": [
        "ed71a06dc8bb44ca530b83e53b024ccb024834c3"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue Apr 28 08:20:34 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 28 08:20:34 2026 -0700"
      },
      "message": "BP-69: Convert stats and allocator modules to slog (phase 2) (#4755)\n\n* BP-69 base: add slog dependency and LICENSE entries\n\nMinimal scaffolding commit for the BP-69 slog migration series:\n\n- Add `io.github.merlimat.slog:slog:0.9.7` to the root pom.xml\n  dependencyManagement and to the global compile classpath alongside\n  the existing SLF4J API (which stays as the rendering backend).\n- Add lombok.config at the repo root so `@CustomLog` generates a slog\n  `Logger` instead of an SLF4J one.\n- Register the slog jar in LICENSE-all.bin.txt, LICENSE-server.bin.txt\n  and LICENSE-bkctl.bin.txt so the CI check-binary-license script\n  finds it accounted for in the bundled-jars list.\n\nNo actual Java file is converted in this commit. Individual module\nmigrations stack on top of this one.\n\n* BP-69: Convert stats and allocator modules from SLF4J to slog\n\nPart of BP-69. Converts the stats-api, codahale/otel/prometheus metrics\nproviders, stats-utils, and bookkeeper-common-allocator modules.\n\n* Fix checkstyle ImportOrder violations\n\n* Bump slog dependency to 0.9.8\n\nPulls in the MDC propagation fix from merlimat/slog#6, which makes\nlog4j2 ThreadContext entries visible on slog events emitted via the\nLog4j2Logger backend (so %X{key} layouts and appenders that read\nevent.getContextData().getValue(key) see the caller\u0027s MDC)."
    },
    {
      "commit": "ed71a06dc8bb44ca530b83e53b024ccb024834c3",
      "tree": "bcc1dec1bfe9b40f60c7824bfd72af4249273da3",
      "parents": [
        "a61a6305db0ba5b4fe84dac83a0435a94d37c23b"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Tue Apr 28 08:20:08 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 28 08:20:08 2026 -0700"
      },
      "message": "BP-69: Convert bookkeeper-common to slog (phase 1) (#4754)\n\n* BP-69: Convert bookkeeper-common from SLF4J to slog\n\nFirst phase of the slog migration for BP-69. Adds the slog dependency,\nLombok @CustomLog configuration, and converts the bookkeeper-common module.\n\n* Added license header\n\n* BP-69 common: fix checkstyle ImportOrder violations\n\nCI flagged 4 ImportOrder violations in the Phase 1 conversion:\n- SafeRunnable.java: io.github.merlimat.slog.Logger placed after java.*\n- OrderedExecutor.java: lombok.CustomLog placed after org.*\n- AbstractLifecycleComponent.java: same\n- TestOrderedExecutorDecorators.java: same\n\nMove each import to its correct alphabetical position per the project\u0027s\ncheckstyle rule (`io.` \u003c `java.`; `lombok.` \u003c `org.`).\n\n* BP-69 common: add slog-0.9.7 to bookkeeper-dist LICENSE files\n\nCI check-binary-license flagged the new io.github.merlimat.slog-slog-0.9.7.jar\nas unaccounted for. Add it to the Apache-2.0 bundled-jars list in both\nLICENSE-all.bin.txt and LICENSE-bkctl.bin.txt, with a new numbered source\nreference pointing at https://github.com/merlimat/slog/tree/v0.9.7.\n\n* BP-69 common: also add slog-0.9.7 to LICENSE-server.bin.txt\n\nThe previous LICENSE fix only updated LICENSE-all.bin.txt and\nLICENSE-bkctl.bin.txt, but the bin-server.xml assembly descriptor\npackages LICENSE-server.bin.txt into the server tarball that the CI\nlicense-check script inspects.\n\n* BP-69 common: remove TestOrderedExecutorDecorators and MdcContextTest\n\nBoth tests exercise SLF4J MDC context propagation through the\nOrderedExecutor / OrderedScheduler decorators. With the slog migration\nthe logger API is different and these tests are not straightforward to\nkeep working against the new logger while still testing MDC behaviour\nas seen by the underlying SLF4J backend.\n\nDrop them for now. The MDC propagation code itself (MdcUtils,\nOrderedExecutor\u0027s mdcContextMap snapshot/restore) is unchanged, so\nend-to-end MDC-in-logs behaviour under Logback/Log4j2 backends is\npreserved.\n\n* Revert \"BP-69 common: remove TestOrderedExecutorDecorators and MdcContextTest\"\n\nThis reverts commit 790d4955aad0fc5f559c148a99084d921e7dec8b.\n\n* Bump slog dependency to 0.9.8\n\nPulls in the MDC propagation fix from merlimat/slog#6, which makes\nlog4j2 ThreadContext entries visible on slog events emitted via the\nLog4j2Logger backend (so %X{key} layouts and appenders that read\nevent.getContextData().getValue(key) see the caller\u0027s MDC)."
    },
    {
      "commit": "a61a6305db0ba5b4fe84dac83a0435a94d37c23b",
      "tree": "0f7b93403efc0e5158ef67ebd113c1a0b69ddc88",
      "parents": [
        "74109879801cb8966503a4ea7b822b37a8e91105"
      ],
      "author": {
        "name": "xiaolong ran",
        "email": "xiaolongran@tencent.com",
        "time": "Tue Apr 28 19:47:49 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 28 19:47:49 2026 +0800"
      },
      "message": "Support bookie server config tcp keep-alive (#4762)\n\n* Support bookie server config tcp keep-alive\n\nSigned-off-by: xiaolongran \u003cxiaolongran@tencent.com\u003e\n\n* fix comments\n\nSigned-off-by: xiaolongran \u003cxiaolongran@tencent.com\u003e\n\n* fix comments\n\nSigned-off-by: xiaolongran \u003cxiaolongran@tencent.com\u003e\n\n---------\n\nSigned-off-by: xiaolongran \u003cxiaolongran@tencent.com\u003e"
    },
    {
      "commit": "74109879801cb8966503a4ea7b822b37a8e91105",
      "tree": "6193f7c2a2eac1acb9a17146b58911f38dd6ffdb",
      "parents": [
        "fe5b8062cffe260edb076f912cedd3afab390ca9"
      ],
      "author": {
        "name": "Justin Prieto",
        "email": "justin.prieto@temporal.io",
        "time": "Thu Apr 23 10:15:31 2026 -0400"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 23 07:15:31 2026 -0700"
      },
      "message": "Fix race condition NPE in V3 response handling during timeout check (#4737)"
    },
    {
      "commit": "fe5b8062cffe260edb076f912cedd3afab390ca9",
      "tree": "d7afafb919e79d0aa21cde490d65ab28b06553c0",
      "parents": [
        "7b362b1d8fcbcd75e6203072e2139de5902550e2"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Thu Apr 23 07:04:23 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Apr 23 07:04:23 2026 -0700"
      },
      "message": "BP-69: Adopt slog for structured logging (#4751)\n\nProposal to migrate BookKeeper from SLF4J to the slog structured-logging\nlibrary (mirroring Pulsar PIP-467), and to add a public client API method\nwithLoggerContext(Map\u003cString, Object\u003e) on CreateBuilder/OpenBuilder/\nDeleteBuilder so callers can attach their own logging context attributes to\na ledger Handle. Retires the existing bookkeeper-slogger module (5 internal\ncall sites, no external adoption).\n\nTracking issue: #4750"
    },
    {
      "commit": "7b362b1d8fcbcd75e6203072e2139de5902550e2",
      "tree": "f80322f974cee9b9cf1d08ee14c6294a07af1ad3",
      "parents": [
        "b5ce0e5ff08c323cc833d0302575b503226bf2bd"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Wed Apr 22 09:51:45 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 22 09:51:45 2026 -0700"
      },
      "message": "Install native build toolchain in website PR validation workflow (#4753)\n\nThe website build runs javadoc-gen.sh, which invokes `mvn install` on\nbookkeeper-server and transitively requires native-io. After #4738\nmigrated native-io from C to Rust, building native-io requires\ncargo-zigbuild, Rust toolchains, and Zig — none of which were installed\nin this workflow, causing every website PR check to fail with\n`error: no such command: zigbuild`.\n\nReuse the existing setup-native-build composite action so the website\nworkflow matches the other workflows that build native-io."
    },
    {
      "commit": "b5ce0e5ff08c323cc833d0302575b503226bf2bd",
      "tree": "3c53646e970d87da7c1cb71c37c8a8aa2b17383f",
      "parents": [
        "ad2b6e939d0548e88ae6209ec57628814e8c6ee1"
      ],
      "author": {
        "name": "void-ptr974",
        "email": "void-ptr974@protonmail.com",
        "time": "Thu Apr 23 00:18:46 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 22 09:18:46 2026 -0700"
      },
      "message": "[FIX] Fix flaky BookieAutoRecoveryTest#testOpenLedgers timeout (#4743)\n\nWhen the killed bookie happens to be the Auditor leader, the test must wait for the ZK session timeout (default 10s) before the ephemeral node disappears, then wait for a new Auditor\n   leader election, metadata scan, and underreplicated ledger publishing. In resource-constrained CI environments, this chain can exceed the 60-second await timeout.\n\n  Two changes:\n  - setZkTimeout(4000) — reduces ZK session timeout so the ephemeral node disappears faster\n  - await 60s → 90s — provides more headroom for slow CI environments"
    },
    {
      "commit": "ad2b6e939d0548e88ae6209ec57628814e8c6ee1",
      "tree": "741fafb969a9169cc70d502f5734dd3e21768974",
      "parents": [
        "6fac859db3b1fb7ce640d2cfda4f4933771f2f28"
      ],
      "author": {
        "name": "Zixuan Liu",
        "email": "nodeces@gmail.com",
        "time": "Tue Apr 21 23:51:03 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Apr 21 08:51:03 2026 -0700"
      },
      "message": "fix: ensure discard_max_bytes is set to 0 only for existing block devices (#4745)"
    },
    {
      "commit": "6fac859db3b1fb7ce640d2cfda4f4933771f2f28",
      "tree": "a804f5eb822b7ec177cb3632ea47f80bd9bac95f",
      "parents": [
        "af117a669abac518d65bb97f535a6d86ec49d32b"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Sat Apr 18 06:45:25 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sat Apr 18 06:45:25 2026 -0700"
      },
      "message": "Upgrade Netty to 4.2.12.Final (#4744)"
    },
    {
      "commit": "af117a669abac518d65bb97f535a6d86ec49d32b",
      "tree": "e057179d6be27d3501150537028654b53199d15a",
      "parents": [
        "edf21555638800c31bb3b8d6c72d18c39a4ff056"
      ],
      "author": {
        "name": "道君- Tao Jiuming",
        "email": "daojun@apache.org",
        "time": "Sat Apr 18 00:44:38 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Apr 17 09:44:38 2026 -0700"
      },
      "message": "Add rerun-failure command to bkbot (#4746)"
    },
    {
      "commit": "edf21555638800c31bb3b8d6c72d18c39a4ff056",
      "tree": "ce7056c66534736d4ba803d45c2964e2181f65d2",
      "parents": [
        "6dc59255dc99745c0645374cd3b69d8d26de17ce"
      ],
      "author": {
        "name": "Zixuan Liu",
        "email": "nodeces@gmail.com",
        "time": "Wed Apr 15 23:41:49 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Apr 15 18:41:49 2026 +0300"
      },
      "message": "feat: migrate native-io implementation from C to Rust (#4738)\n\n* feat: migrate native-io implementation from C to Rust\n\n* fix: use Apache Commons SystemUtils for OS name and architecture retrieval\n\n* feat: add support for pure Rust native-io builds with conditional activation\n\n* chore: update Rust and Zig toolchain versions\n\n* chore: extract native build setup action\n\nCo-authored-by: Copilot \u003c223556219+Copilot@users.noreply.github.com\u003e\n\n* fix: add shell to native build action\n\nCo-authored-by: Copilot \u003c223556219+Copilot@users.noreply.github.com\u003e\n\n---------\n\nCo-authored-by: Copilot \u003c223556219+Copilot@users.noreply.github.com\u003e"
    },
    {
      "commit": "6dc59255dc99745c0645374cd3b69d8d26de17ce",
      "tree": "edf582c050a6dc4a42f7db9ed20bfd116ac64b50",
      "parents": [
        "8a8f4d607dc7056a75178316dc563445165ad460"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Thu Mar 26 17:45:12 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 26 17:45:12 2026 -0700"
      },
      "message": "[FIX] Fix IllegalThreadStateException in ComponentStarter shutdown hook (#4733)\n\nThe UncaughtExceptionHandler in ComponentStarter calls\nshutdownHookThread.start(), but this can throw IllegalThreadStateException\nif the thread was already started by a prior exception or by the JVM\nshutdown sequence. This exception propagates out of the handler, causing\nthe JVM to print \"Exception thrown from the UncaughtExceptionHandler\"\non the BookieDeathWatcher thread.\n\nCatch IllegalThreadStateException since it simply means shutdown is\nalready in progress."
    },
    {
      "commit": "8a8f4d607dc7056a75178316dc563445165ad460",
      "tree": "09435b232e2b9623508f270ff44ec3e6b197abe3",
      "parents": [
        "8664dd962935dc23d40218f1bf228661776580fd"
      ],
      "author": {
        "name": "zhou zhuohan",
        "email": "843520313@qq.com",
        "time": "Tue Mar 24 11:59:32 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 24 11:59:32 2026 +0800"
      },
      "message": "[FIX] fix SyncLedgerIterator.hasNext() failing to iterate across ZK ledger ranges (#4731)"
    },
    {
      "commit": "8664dd962935dc23d40218f1bf228661776580fd",
      "tree": "51b8e672ac29b8c784a819318151f7feb5408234",
      "parents": [
        "a7f0ba1ae1855fe5329e76ffa6aa6f0f012e91ba"
      ],
      "author": {
        "name": "Hang Chen",
        "email": "chenhang@apache.org",
        "time": "Thu Mar 19 23:37:54 2026 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 20 14:37:54 2026 +0800"
      },
      "message": "Fix read thread blocking in sendResponseAndWait causing READ_ENTRY_REQUEST p99 latency spike (#4730)\n\n* Fix read thread blocking in sendResponseAndWait causing READ_ENTRY_REQUEST p99 latency spike\n\n* address comments"
    },
    {
      "commit": "a7f0ba1ae1855fe5329e76ffa6aa6f0f012e91ba",
      "tree": "de084d210140502f55f1013ac7dd8b498b2bdb72",
      "parents": [
        "497aa4e53b9b10dad3c1e8800bcf417c9ef8014a"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Thu Mar 19 11:10:41 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 19 11:10:41 2026 +0200"
      },
      "message": "build(deps): bump org.apache.commons:commons-lang3 from 3.17.0 to 3.20.0 (#4725)"
    },
    {
      "commit": "497aa4e53b9b10dad3c1e8800bcf417c9ef8014a",
      "tree": "6fec6def4301ee564c1d04af01ffc3adbc19bf2d",
      "parents": [
        "d08fcb7094f404aca5f0351feedd744d40ea2602"
      ],
      "author": {
        "name": "Yong Zhang",
        "email": "zhangyong1025.zy@gmail.com",
        "time": "Thu Mar 19 17:09:49 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 19 11:09:49 2026 +0200"
      },
      "message": "Count the connection failure as the condition of quarantine (#4727)\n\n* Count the connection failure as the condition of quarantine\n---\n\n### Motivation\n\nCurrently, the BookieClient quarantine mechanism primarily triggers based on read and write error responses from Bookies. However, in multi-region deployments, a common failure mode is the Network Partition or DNS Resolution Failure at the Region level.\n\nIn such scenarios:\n\nA Bookie remains registered in ZooKeeper (it can still heartbeat to its local ZK observer).\n\nThe Client (Broker) cannot resolve the Bookie\u0027s IP or establish a TCP connection.\n\nThe EnsemblePlacementPolicy (especially RegionAwareEnsemblePlacementPolicy) sees the Bookie as \"Available\" and repeatedly selects it to satisfy minRack or E/Qw constraints.\n\nThe LedgerHandle fails to write because it cannot initialize a connection handle, triggering an Ensemble Change.\n\nBecause the connection failure didn\u0027t trigger a quarantine, the placement policy picks the same problematic Bookie again in the next iteration.\n\nThis creates an infinite Ensemble Change loop, causing the Ledger write to hang indefinitely and bloating the Ledger metadata in ZooKeeper with thousands of segments.\n\n* Add configuration to control the behavior"
    },
    {
      "commit": "d08fcb7094f404aca5f0351feedd744d40ea2602",
      "tree": "6ade348eb0f775a540ca8ef056b93594290d42de",
      "parents": [
        "951f10d9e7efb94da9f128fdc990f8bdab342a2c"
      ],
      "author": {
        "name": "JiangHaiting",
        "email": "jianghaiting@apache.org",
        "time": "Wed Mar 18 17:35:26 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 18 17:35:26 2026 +0800"
      },
      "message": "test: Use mock to eliminate BookieSocketAddressTest dependency on local system configuration (#4711)\n\n- Replace direct network calls with Mockito mocks in BookieSocketAddressTest\n- Add mockHostnameIPSetup helper method to simulate DNS.getDefaultIP behavior\n- Remove dependency on actual network configuration and loopback addresses\n- Ensure consistent test behavior across different environments\n- All 4 existing tests pass with the mock implementation\n\nCo-authored-by: JiangHaiting \u003cjasonxjiang@tencent.com\u003e\nCo-authored-by: Lari Hotari \u003clhotari@users.noreply.github.com\u003e"
    },
    {
      "commit": "951f10d9e7efb94da9f128fdc990f8bdab342a2c",
      "tree": "aa843694c78a1b8cbdd460017d8fc182bc2bd1c6",
      "parents": [
        "be1f6aa86f45903dea63752be50e44e43c9025d0"
      ],
      "author": {
        "name": "wenbingshen",
        "email": "oliver.shen999@gmail.com",
        "time": "Mon Mar 16 21:08:05 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 16 21:08:05 2026 +0800"
      },
      "message": "[client] fix writeLac memory leak and thread safety issue (#4713)\n\n* fix writeLac memory leak\n\n---------\n\nCo-authored-by: wenbingshen \u003cwenbingshen@tencent.com\u003e"
    },
    {
      "commit": "be1f6aa86f45903dea63752be50e44e43c9025d0",
      "tree": "d7a3913a4a662744c2f1dc5b32ceff8591782c7d",
      "parents": [
        "1808a084cbf5c44de9d81b8c7970c8f1b98e890f"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Fri Mar 13 17:26:46 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 13 16:26:46 2026 +0100"
      },
      "message": "Fix broken link in README.md (#4729)"
    },
    {
      "commit": "1808a084cbf5c44de9d81b8c7970c8f1b98e890f",
      "tree": "13664093cff8bc606fdd0408e89f905608c2c360",
      "parents": [
        "6c8141e38f3825d8e022b9c1b042bda77487d158"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Fri Mar 13 08:50:17 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 13 08:50:17 2026 +0200"
      },
      "message": "Upgrade zookeeper to 3.9.5 in docker-compose and integration tests (#4726)"
    },
    {
      "commit": "6c8141e38f3825d8e022b9c1b042bda77487d158",
      "tree": "a6c6b457d31f7363ad2e01eac63a85139b74eed7",
      "parents": [
        "534ba20dd9cd3be1519ee92c7a49918eda410901"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Wed Mar 11 15:41:53 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 11 15:41:53 2026 +0200"
      },
      "message": "Fix download page after website framework upgrade to Docusaurus 3 (#4724)"
    },
    {
      "commit": "534ba20dd9cd3be1519ee92c7a49918eda410901",
      "tree": "9df5c677fdb66f9bde67480c6e6ca0b0a9d6a3e1",
      "parents": [
        "f440725b673d8c609274a9c365fd24430d77f6ed"
      ],
      "author": {
        "name": "Yong Zhang",
        "email": "zhangyong1025.zy@gmail.com",
        "time": "Wed Mar 11 21:35:29 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 11 15:35:29 2026 +0200"
      },
      "message": "Update BK version in the Docker file (#4704)"
    },
    {
      "commit": "f440725b673d8c609274a9c365fd24430d77f6ed",
      "tree": "3110ebaaa66d954b62f0c0e2787b0712c04c609a",
      "parents": [
        "04d8ef1995e2a40107b02d826cc7a5f0af0b2b9e"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Wed Mar 11 14:11:45 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 11 14:11:45 2026 +0200"
      },
      "message": "Upgrade website to Docusaurus 3 (#4723)"
    },
    {
      "commit": "04d8ef1995e2a40107b02d826cc7a5f0af0b2b9e",
      "tree": "f60bb97b61f10c37ce629021e225af9100d11183",
      "parents": [
        "5549045eb16d0817c02fa301af91dbd9dda41f14"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Wed Mar 11 07:55:20 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 11 07:55:20 2026 +0200"
      },
      "message": "build(deps): bump org.apache.zookeeper:zookeeper from 3.9.4 to 3.9.5 (#4721)"
    },
    {
      "commit": "5549045eb16d0817c02fa301af91dbd9dda41f14",
      "tree": "115c6f849587acf56a92b4956f870791942dc549",
      "parents": [
        "b659503607ce4a959f7790085907b8a58ef18d3e"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Wed Mar 11 01:55:08 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Mar 11 01:55:08 2026 +0200"
      },
      "message": "Improve CI: add caching for Bookkeeper old release downloads and improve maven caching and error logs (#4722)"
    },
    {
      "commit": "b659503607ce4a959f7790085907b8a58ef18d3e",
      "tree": "9a253aef73407725b1fec00177fc565b673b8c6f",
      "parents": [
        "425001f4e12badb959e40f33dedf03804848d2f2"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Fri Mar 06 06:15:29 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 06 16:15:29 2026 +0200"
      },
      "message": "Upgrade Jetty from 9.4.57 to 12.1.7 (#4710)\n\n* Upgrade Jetty from 9.4.57 to 12.1.6\n\nThis upgrades Jetty to the latest stable 12.x release, which brings\nimproved performance, HTTP/2 and HTTP/3 support, and ongoing security\nupdates (Jetty 9.x is EOL).\n\nKey changes:\n- Update jetty.version from 9.4.57.v20241219 to 12.1.6\n- Use jetty-ee8-* artifacts to maintain javax.servlet compatibility\n- Update import statements to use org.eclipse.jetty.ee8.servlet package\n- Update LICENSE and NOTICE files with new artifact names and versions\n\n* 12.1.7\n\n* Fix source url\n\n* Fixed license\n\n* Removed unreferenced license\n\n* Fixed integration test\n\n* Make tests less flaky\n\n---------\n\nCo-authored-by: Lari Hotari \u003clhotari@users.noreply.github.com\u003e"
    },
    {
      "commit": "425001f4e12badb959e40f33dedf03804848d2f2",
      "tree": "258ffb175d2de088cfaabf7f169c7221c65130cf",
      "parents": [
        "5199c2886278566ad8b8ee6b13df484e01af101b"
      ],
      "author": {
        "name": "Xiangying Meng",
        "email": "55571188+liangyepianzhou@users.noreply.github.com",
        "time": "Fri Mar 06 06:37:20 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Mar 06 00:37:20 2026 +0200"
      },
      "message": "FlakyTest:  testEmptyLedgerLosesQuorumEventually (#4672)\n\nCo-authored-by: xiangying \u003cmengxiangying@xiaohongshu.com\u003e"
    },
    {
      "commit": "5199c2886278566ad8b8ee6b13df484e01af101b",
      "tree": "93decf7526d16ba145101adefd7d66fcd45b69f7",
      "parents": [
        "1f236a6e52d3c61412dced65ed745e07b5da9932"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Mar 05 13:43:23 2026 -0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 05 13:43:23 2026 -0800"
      },
      "message": "build(deps): bump org.apache.zookeeper:zookeeper from 3.9.3 to 3.9.4 (#4667)\n\n* build(deps): bump org.apache.zookeeper:zookeeper from 3.9.3 to 3.9.4\n\nBumps org.apache.zookeeper:zookeeper from 3.9.3 to 3.9.4.\n\n---\nupdated-dependencies:\n- dependency-name: org.apache.zookeeper:zookeeper\n  dependency-version: 3.9.4\n  dependency-type: direct:production\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\n\n* Fixed license files\n\n* Fixed test\n\n---------\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e\nCo-authored-by: Matteo Merli \u003cmmerli@apache.org\u003e"
    },
    {
      "commit": "1f236a6e52d3c61412dced65ed745e07b5da9932",
      "tree": "848f90001084ae562cbf4428d8a9f7615bb70889",
      "parents": [
        "2e397d426c08838c0495dbba08898cb9b6ad38a1"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Thu Mar 05 21:58:26 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 05 11:58:26 2026 -0800"
      },
      "message": "Upgrade jctools version to 4.0.5 (#4656)\n\n"
    },
    {
      "commit": "2e397d426c08838c0495dbba08898cb9b6ad38a1",
      "tree": "c18dd066d2b434ef4685d1c9edb09bce55583d98",
      "parents": [
        "07d18be73cd59993d69a3619790445807740e35c"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Thu Mar 05 20:31:51 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 05 20:31:51 2026 +0200"
      },
      "message": "build(deps-dev): bump org.assertj:assertj-core from 3.25.3 to 3.27.7 (#4708)\n\nBumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.25.3 to 3.27.7.\n- [Release notes](https://github.com/assertj/assertj/releases)\n- [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.25.3...assertj-build-3.27.7)\n\n---\nupdated-dependencies:\n- dependency-name: org.assertj:assertj-core\n  dependency-version: 3.27.7\n  dependency-type: direct:development\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e"
    },
    {
      "commit": "07d18be73cd59993d69a3619790445807740e35c",
      "tree": "a44fc423e1e58bcf3474a763a942546719375176",
      "parents": [
        "d7020fbec37d207eae0a042d702ae981e7fdeb79"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Thu Mar 05 18:02:00 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 05 18:02:00 2026 +0200"
      },
      "message": "Add workaround for ZOOKEEPER-3825 related to numeric IPs and DNS names with multiple numeric IPs and support on Java 17 (#4719)\n\n"
    },
    {
      "commit": "d7020fbec37d207eae0a042d702ae981e7fdeb79",
      "tree": "c0ddfc83209943fba12eadfcae0d628e27fc99bf",
      "parents": [
        "3629c4c81bcd30b3bcad2aecf19a9db9fb5a1aaf"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Thu Mar 05 10:34:28 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Mar 05 10:34:28 2026 +0200"
      },
      "message": "Require Java 17+ for building and running Bookkeeper (#4446)\n\n"
    },
    {
      "commit": "3629c4c81bcd30b3bcad2aecf19a9db9fb5a1aaf",
      "tree": "f6358480450b71519cb7276fd3a98dc17d5e73ba",
      "parents": [
        "a583e807dbce4c99489abfad8720b9e339b2d31d"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Tue Mar 03 23:28:59 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 03 13:28:59 2026 -0800"
      },
      "message": "Remove OWASP dependency check from CI (#4717)\n\n"
    },
    {
      "commit": "a583e807dbce4c99489abfad8720b9e339b2d31d",
      "tree": "7fdf599717fe32110c0cee94867a23b51a5d6162",
      "parents": [
        "ab2b29db9378cd3094c752b3a2cae20adf3bc088"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Tue Mar 03 10:40:40 2026 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Mar 03 10:40:40 2026 +0200"
      },
      "message": "Upgrade protobuf and grpc to latest versions (#4716)\n\n"
    },
    {
      "commit": "ab2b29db9378cd3094c752b3a2cae20adf3bc088",
      "tree": "c1e356d7502356f084fab62a5c9021d5f8a77fad",
      "parents": [
        "3a5cf9d2fb35c758e804855ac30ad0044ee983a4"
      ],
      "author": {
        "name": "dependabot[bot]",
        "email": "49699333+dependabot[bot]@users.noreply.github.com",
        "time": "Mon Mar 02 12:03:38 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Mar 02 12:03:38 2026 +0800"
      },
      "message": "build(deps): bump io.vertx:vertx-core from 4.5.11 to 4.5.24 (#4702)\n\n* build(deps): bump io.vertx:vertx-core from 4.5.11 to 4.5.24\n\nBumps [io.vertx:vertx-core](https://github.com/eclipse/vert.x) from 4.5.11 to 4.5.24.\n- [Commits](https://github.com/eclipse/vert.x/compare/4.5.11...4.5.24)\n\n---\nupdated-dependencies:\n- dependency-name: io.vertx:vertx-core\n  dependency-version: 4.5.24\n  dependency-type: direct:production\n...\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\n\n* Fix license issue\n\n---------\n\nSigned-off-by: dependabot[bot] \u003csupport@github.com\u003e\nCo-authored-by: dependabot[bot] \u003c49699333+dependabot[bot]@users.noreply.github.com\u003e\nCo-authored-by: zymap \u003czhangyong1025.zy@gmail.com\u003e"
    },
    {
      "commit": "3a5cf9d2fb35c758e804855ac30ad0044ee983a4",
      "tree": "7ddf6f2f47e2b37bfa1cf5ffcb1c53a8b891218b",
      "parents": [
        "1f9440b34280f9fd87fb0cf163a08e3e8b08f512"
      ],
      "author": {
        "name": "Yong Zhang",
        "email": "zhangyong1025.zy@gmail.com",
        "time": "Wed Jan 21 17:50:19 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jan 21 17:50:19 2026 +0800"
      },
      "message": "Fix the stream client python package name (#4703)\n\n---\n\n### Motivation\n\n```\nWARNING  Error during upload. Retry with the --verbose option for more details.\nERROR    HTTPError: 400 Bad Request from https://upload.pypi.org/legacy/\n         Filename \u0027apache-bookkeeper-client-4.17.3.tar.gz\u0027 is invalid, should be \u0027apache_bookkeeper_client-4.17.3.tar.gz\u0027.\n```"
    },
    {
      "commit": "1f9440b34280f9fd87fb0cf163a08e3e8b08f512",
      "tree": "206893107381ef855249ad83907a0e5d1dbf4060",
      "parents": [
        "44607a0e31ee8f979211eb5a4ef424de2871b92e"
      ],
      "author": {
        "name": "Yong Zhang",
        "email": "zhangyong1025.zy@gmail.com",
        "time": "Wed Jan 21 09:58:07 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Jan 21 09:58:07 2026 +0800"
      },
      "message": "Release note for 4.17.3 (#4700)\n\n* Release note for 4.17.3\n\n* Update latest release"
    },
    {
      "commit": "44607a0e31ee8f979211eb5a4ef424de2871b92e",
      "tree": "255db247cb7fee4341451da8b847ec5bc70a8cd7",
      "parents": [
        "9ce7cc96697f253366f28c2c3fbd36900ca447d5"
      ],
      "author": {
        "name": "fengyubiao",
        "email": "yubiao.feng@streamnative.io",
        "time": "Tue Jan 20 17:40:26 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Jan 20 17:40:26 2026 +0800"
      },
      "message": "[fix]Fix entry loss due to incorrect lock of LedgerHandle (#4701)\n\n* [fix]Fix entry loss due to incorrect lock of LedgerHandle"
    },
    {
      "commit": "9ce7cc96697f253366f28c2c3fbd36900ca447d5",
      "tree": "2b131e51dce358c2074eaebbecb689397f4ea60e",
      "parents": [
        "b88fb5f0390a37947b33f1ba151de820f10452c5"
      ],
      "author": {
        "name": "Yong Zhang",
        "email": "zhangyong1025.zy@gmail.com",
        "time": "Mon Jan 12 19:05:42 2026 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jan 12 19:05:42 2026 +0800"
      },
      "message": "Upgrade netty to 4.1.130 Final (#4699)\n\nUpgrade netty to 4.1.130 Final (#4699)"
    },
    {
      "commit": "b88fb5f0390a37947b33f1ba151de820f10452c5",
      "tree": "c2b4c1d8b0b769bbee4329c00ad41be5ee28e78e",
      "parents": [
        "341f7d708423c38311239dda6d118e37aa9315bb"
      ],
      "author": {
        "name": "Masahiro Sakamoto",
        "email": "massakam@lycorp.co.jp",
        "time": "Mon Jan 12 17:07:10 2026 +0900"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jan 12 16:07:10 2026 +0800"
      },
      "message": "Replace net.jpountz.lz4:lz4 with at.yawk.lz4:lz4-java (#4696)\n\n"
    },
    {
      "commit": "341f7d708423c38311239dda6d118e37aa9315bb",
      "tree": "980709f46f86b6740bdcadc2d44a893855cf3070",
      "parents": [
        "d71cd1e84fa52497eaaf489856b8d019f4f42fa3"
      ],
      "author": {
        "name": "SongOf",
        "email": "46475785+SongOf@users.noreply.github.com",
        "time": "Fri Dec 05 11:29:47 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Dec 05 11:29:47 2025 +0800"
      },
      "message": "Delete cookie as part of decommission API (#4592)\n\nImplement the logic in [BP-68:](https://github.com/apache/bookkeeper/pull/4500) Delete cookie as part of decommission API."
    },
    {
      "commit": "d71cd1e84fa52497eaaf489856b8d019f4f42fa3",
      "tree": "dfb49b2f18da0950f18d8acd21669adf2880da23",
      "parents": [
        "a369fe72379db4521b951a3fe036d4f76679886a"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Mon Dec 01 04:23:30 2025 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Dec 01 10:23:30 2025 +0800"
      },
      "message": "Upgrade OpenTelemetry to 1.56.0, Otel instrumentation to 2.21.0 and Otel semconv to 1.37.0 (#4690)\n\n* Upgrade OpenTelemetry to 1.56.0, intrumentation to 2.21.0 and semconv to 1.37.0\n\n* Fix compatibility on Otel 1.56\n\n* Add otel.contrib.version\n\n* Align okhttp3 and okio versions\n\n* Upgrade licenses"
    },
    {
      "commit": "a369fe72379db4521b951a3fe036d4f76679886a",
      "tree": "bf018de315d7c4f6372ed35aa6cab824624e8d49",
      "parents": [
        "14c2ffe07d7258e765081634b2bd6788edf4286f"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Fri Nov 28 09:30:55 2025 +0200"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Nov 28 09:30:55 2025 +0200"
      },
      "message": "Exclude commons-collections from dependencies (#4689)\n\n* [fix] Exclude optional commons-collections dependency from commons-beanutils\n\n* Update license files\n\n* Upgrade hadoop to 3.4.2 version"
    },
    {
      "commit": "14c2ffe07d7258e765081634b2bd6788edf4286f",
      "tree": "01d82d06e77e4f2b1b78d6b8aac85c5e88f0827b",
      "parents": [
        "ea7884a82d9f877773157f8756a08a91d95ed5be"
      ],
      "author": {
        "name": "Qiang Zhao",
        "email": "mattisonchao@gmail.com",
        "time": "Tue Nov 25 09:16:01 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Nov 24 17:16:01 2025 -0800"
      },
      "message": "feat: support `equalsAndHashcode` for BookieServerInfo (#4686)\n\n* feat: support equalsAndHashcode for BookieServerInfo\n\nSigned-off-by: mattisonchao \u003cmattisonchao@gmail.com\u003e\n\n* change order\n\n* fix checkstyle\n\n* fix checkstyle\n\n---------\n\nSigned-off-by: mattisonchao \u003cmattisonchao@gmail.com\u003e"
    },
    {
      "commit": "ea7884a82d9f877773157f8756a08a91d95ed5be",
      "tree": "41d77e2ec0d61ab0eb5bf30666b280fb2d8804bd",
      "parents": [
        "1a509e4f736a75bc4570748324afba94ba51e471"
      ],
      "author": {
        "name": "xiaolong ran",
        "email": "ranxiaolong716@gmail.com",
        "time": "Mon Nov 24 11:33:06 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Nov 24 11:33:06 2025 +0800"
      },
      "message": "Supports configuring TCP Keepalive related parameters in Bookie Client. (#4683)\n\n### Motivation\nIn a private environment, network connectivity between data centers is limited by firewall configuration. When the Broker accesses Bookkeeper infrequently, exceeding the firewall\u0027s default deactivation time (20 minutes), the firewall will actively disconnect the Broker → Bookkeeper connection. \nAt this point, if a new production or consumption request is made, the Broker cannot safely access the Bookkeeper node again, causing production/consumption failures that cannot be automatically recovered from. The error message primarily indicates connection-level anomalies:\n\u003cimg width\u003d\"2944\" height\u003d\"738\" alt\u003d\"Clipboard_Screenshot_1763091013\" src\u003d\"https://github.com/user-attachments/assets/e58b53b9-1c79-49cd-a4d7-1040f52569e0\" /\u003e\n\n\nThe current Broker → Bookkeeper access chain is based on the Netty communication framework, and the keepalive function at the TCP connection layer reuses the system default SO_KEEPALIVE. The code is `PerChannelBookieClient` → `connect()`, as follows:\n\u003cimg width\u003d\"1982\" height\u003d\"434\" alt\u003d\"Clipboard_Screenshot_1763091110\" src\u003d\"https://github.com/user-attachments/assets/979716b6-d99a-4dfd-9829-cd724c597c20\" /\u003e\n\nIf no policy is explicitly set, the \"System Default\" option in the table follow will be used directly:\n\nTCP_KEEPIDLE | 7200s\n-- | --\nTCP_KEEPINTVL | 75s\nTCP_KEEPCNT | 9\n\n\n\n\n### Changes\n\nInclude the following three configuration items in ClientConfiguration:\n\n```\n    public static final String TCP_KEEPIDLE \u003d \"tcpKeepIdle\";\n    public static final String TCP_KEEPINTVL \u003d \"tcpKeepIntvl\";\n    public static final String TCP_KEEPCNT \u003d \"tcpKeepCnt\";\n```\n\nTo maintain compatibility, the system default configuration will still be used by default."
    },
    {
      "commit": "1a509e4f736a75bc4570748324afba94ba51e471",
      "tree": "9cc0c394fe374f3c32cc49ba1e7b7cb35de19a88",
      "parents": [
        "bae9e496cce274398774a5cb52357ab34b07928b"
      ],
      "author": {
        "name": "Yong Zhang",
        "email": "zhangyong1025.zy@gmail.com",
        "time": "Mon Nov 24 11:32:24 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Nov 24 11:32:24 2025 +0800"
      },
      "message": "Bring back the old public method name (#4682)\n\n"
    },
    {
      "commit": "bae9e496cce274398774a5cb52357ab34b07928b",
      "tree": "f9eb8e5787f42c7b70266d647ed829b0acedda8e",
      "parents": [
        "3c80cefa50173924453985d536f91fa845f4161b"
      ],
      "author": {
        "name": "fengyubiao",
        "email": "yubiao.feng@streamnative.io",
        "time": "Wed Oct 29 18:26:59 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 29 18:26:59 2025 +0800"
      },
      "message": "[fix] Failed read entries after multiple decommissioning (#4613)\n\n* -\n\n* checkstyle\n\n* let all ledger handle enable watcher\n\n* let all ledger handle enable watcher\n\n* fix tests\n\n* fix tests\n\n* fix tests\n\n* add test logs for debug\n\n* add test logs for debug\n\n* add test logs for debug\n\n* -\n\n* add a new param keepUpdateMetadata when open a read-only ledger handle\n\n* address comments\n\n* address comment\n\n* address comment\n\n* test CI\n\n* test CI\n\n* test CI\n\n* test CI\n\n* test CI\n\n* test CI\n\n* test CI\n\n* remove logs for CI\n\n* test CI\n\n* remove logs for CI\n\n* address comment\n\n* fix test"
    },
    {
      "commit": "3c80cefa50173924453985d536f91fa845f4161b",
      "tree": "6dae68e346fc5aadb7d8d7937a9df818f7f2ae91",
      "parents": [
        "ae2c889fb563290b7b100c09550c70201f4277fb"
      ],
      "author": {
        "name": "Xiangying Meng",
        "email": "55571188+liangyepianzhou@users.noreply.github.com",
        "time": "Mon Oct 27 20:45:30 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Oct 27 14:45:30 2025 +0200"
      },
      "message": "[Doc] Introduce CI Workflow Management with BookKeeper Bot (#4674)\n\n"
    },
    {
      "commit": "ae2c889fb563290b7b100c09550c70201f4277fb",
      "tree": "05e6b503603ec23e4cabf4b47300dbb930920f7c",
      "parents": [
        "5154149936e3f8187ae9e2755f43ea9c0ecb866e"
      ],
      "author": {
        "name": "Xiangying Meng",
        "email": "55571188+liangyepianzhou@users.noreply.github.com",
        "time": "Fri Oct 24 17:04:22 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Oct 24 17:04:22 2025 +0800"
      },
      "message": "Introduce /bkbot Command to Control CI Workflow Runs via PR Comments (#4673)\n\n* Introduce /bkbot Command to Control CI Workflow Runs via PR Comments"
    },
    {
      "commit": "5154149936e3f8187ae9e2755f43ea9c0ecb866e",
      "tree": "b720d70e431cb16700fae1cc527faf830a3ee79a",
      "parents": [
        "bde9ff2503ae71c4ef03b212577a1a8be673123a"
      ],
      "author": {
        "name": "fengyubiao",
        "email": "yubiao.feng@streamnative.io",
        "time": "Wed Oct 22 12:53:14 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Oct 22 12:53:14 2025 +0800"
      },
      "message": "[fix]compile error of the file native_io_jni.c for the env jdk11 \u0026 windows (#4665)\n\nCo-authored-by: fengyubiao \u003cfengyubiao@100tal.com\u003e"
    },
    {
      "commit": "bde9ff2503ae71c4ef03b212577a1a8be673123a",
      "tree": "d7ad4fccb7a1203a0047aee1994e0c74786a8550",
      "parents": [
        "0621ae6fce446c00f5becf0c81744bd8f9ec1c90"
      ],
      "author": {
        "name": "fengyubiao",
        "email": "yubiao.feng@streamnative.io",
        "time": "Wed Sep 17 12:29:19 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Sep 17 12:29:19 2025 +0800"
      },
      "message": "[fix]BK stays at read_only state even if the disk is empty (#4640)\n\n* [fix]BK stay at read_only state even if the disk is empty\n\n* correct the javadoc\n\n* improve test"
    },
    {
      "commit": "0621ae6fce446c00f5becf0c81744bd8f9ec1c90",
      "tree": "66c9088cbd7d17a26ac2595aa200baf3ee0ebbaf",
      "parents": [
        "5870922754b9585f7d3592dcd0714a4da65d9d80"
      ],
      "author": {
        "name": "Xiangying Meng",
        "email": "55571188+liangyepianzhou@users.noreply.github.com",
        "time": "Wed Sep 03 10:21:28 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Wed Sep 03 10:21:28 2025 +0800"
      },
      "message": "Only stop Gc for the fill disk for DbLedgerStorage (#4661)\n\n* Only stop Gc for the fill disk for DbLedgerStorage"
    },
    {
      "commit": "5870922754b9585f7d3592dcd0714a4da65d9d80",
      "tree": "8430cd319cb9e58f05549e649e6167662135c1b3",
      "parents": [
        "2789316c18e12cbb6d17fa4a023410dbad6593a0"
      ],
      "author": {
        "name": "fengyubiao",
        "email": "yubiao.feng@streamnative.io",
        "time": "Tue Sep 02 12:26:50 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Sep 02 12:26:50 2025 +0800"
      },
      "message": "[fix]Wrong error code(-107) of opening a deleted ledger (#4657)\n\n"
    },
    {
      "commit": "2789316c18e12cbb6d17fa4a023410dbad6593a0",
      "tree": "d5e51b8ffd2ea26b3ee1cba0149da59ed654aebb",
      "parents": [
        "c9b893f58fe200776f237110514785848a4cda80"
      ],
      "author": {
        "name": "Matteo Merli",
        "email": "mmerli@apache.org",
        "time": "Thu Aug 21 14:20:26 2025 -0700"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Thu Aug 21 14:20:26 2025 -0700"
      },
      "message": "Remove unused commons-lang dependency (#4654)\n\n* Remove unused commons-lang dependency\n\n* Removed from license files"
    },
    {
      "commit": "c9b893f58fe200776f237110514785848a4cda80",
      "tree": "8d5db0aaf8e44c3f10e9f1811d7b20927b2ca7e7",
      "parents": [
        "417ff1651fed3d37abc4b3d4e57f8eac950dce9e"
      ],
      "author": {
        "name": "Lari Hotari",
        "email": "lhotari@users.noreply.github.com",
        "time": "Tue Aug 12 11:38:43 2025 +0300"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 12 11:38:43 2025 +0300"
      },
      "message": "Remove commons-configuration2 and commons-beanutils from top level dependencies (#4648)\n\n* Remove commons-configuration2 and commons-beanutils from dependencies of all modules\n\n* Add commons-logging to dependency management"
    },
    {
      "commit": "417ff1651fed3d37abc4b3d4e57f8eac950dce9e",
      "tree": "3969a17fe90df5d60b019e538648049ed9326cb3",
      "parents": [
        "e80d0318cfdebbc79f37d9120de8df28c8c1c13a"
      ],
      "author": {
        "name": "Wenzhi Feng",
        "email": "thetumbled@apache.org",
        "time": "Tue Aug 12 16:37:25 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 12 11:37:25 2025 +0300"
      },
      "message": "add rate limit for zk read rate in gc. (#4645)\n\n* add rate limit for gc.\n\n* fix checkstyle.\n\n* fix conf name and acqurie.\n\n* rename gcZkOpRateLimit to gcMetadataOpRateLimit.\n\n* rename gcZkOpRateLimit to gcMetadataOpRateLimit.\n\n* add return label.\n\n* add test code.\n\n* document conf.\n\n---------\n\nCo-authored-by: fengwenzhi \u003cfengwenzhi.max@bigo.sg\u003e"
    },
    {
      "commit": "e80d0318cfdebbc79f37d9120de8df28c8c1c13a",
      "tree": "ff0743de6f21985c6044864acde7b18269056fcc",
      "parents": [
        "45f7a9202450d7c2093e3dd47fd51ff1519c20e8"
      ],
      "author": {
        "name": "Yong Zhang",
        "email": "zhangyong1025.zy@gmail.com",
        "time": "Tue Aug 12 10:18:44 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Tue Aug 12 10:18:44 2025 +0800"
      },
      "message": "Log all the error in the GarbageCollectorThread (#4649)\n\n### Motivation\n\nWe met the GarbageCollectionThread was stopped by some runtime error, but we didn\u0027t catch it then, causing the GC to stop. \nSuch as: \nhttps://github.com/apache/bookkeeper/pull/3901\nhttps://github.com/apache/bookkeeper/pull/4544\n\nIn our case, the GC stopped because of the OutOfDirectMemoryException then the process stopped and the files can not be deleted. But we didn\u0027t see any error logs. This PR enhance the log info when an unhandled error happens.\nWe already have the [PR](https://github.com/apache/bookkeeper/pull/4544) fixed that. \n\nAnd another fix in this PR is to change the Exception to the Throwable in the getEntryLogMetadata.\n\nHere is the error stack:\n\n```\n    io.netty.util.internal.OutOfDirectMemoryError: failed to allocate 213909504 byte(s) of direct memory (used: 645922847, max: 858783744)\n\tat io.netty.util.internal.PlatformDependent.incrementMemoryCounter(PlatformDependent.java:880)\n\tat io.netty.util.internal.PlatformDependent.allocateDirectNoCleaner(PlatformDependent.java:809)\n\tat io.netty.buffer.PoolArena$DirectArena.allocateDirect(PoolArena.java:718)\n\tat io.netty.buffer.PoolArena$DirectArena.newUnpooledChunk(PoolArena.java:707)\n\tat io.netty.buffer.PoolArena.allocateHuge(PoolArena.java:224)\n\tat io.netty.buffer.PoolArena.allocate(PoolArena.java:142)\n\tat io.netty.buffer.PoolArena.reallocate(PoolArena.java:317)\n\tat io.netty.buffer.PooledByteBuf.capacity(PooledByteBuf.java:123)\n\tat io.netty.buffer.AbstractByteBuf.ensureWritable0(AbstractByteBuf.java:305)\n\tat io.netty.buffer.AbstractByteBuf.ensureWritable(AbstractByteBuf.java:280)\n\tat io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:1103)\n\tat org.apache.bookkeeper.bookie.BufferedReadChannel.read(BufferedReadChannel.java:104)\n\tat org.apache.bookkeeper.bookie.DefaultEntryLogger.extractEntryLogMetadataFromIndex(DefaultEntryLogger.java:1109)\n\tat org.apache.bookkeeper.bookie.DefaultEntryLogger.getEntryLogMetadata(DefaultEntryLogger.java:1060)\n\tat org.apache.bookkeeper.bookie.GarbageCollectorThread.extractMetaFromEntryLogs(GarbageCollectorThread.java:678)\n\tat org.apache.bookkeeper.bookie.GarbageCollectorThread.runWithFlags(GarbageCollectorThread.java:365)\n\tat org.apache.bookkeeper.bookie.GarbageCollectorThread.lambda$triggerGC$4(GarbageCollectorThread.java:268)\n\tat java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)\n\tat java.base/java.util.concurrent.FutureTask.run(Unknown Source)\n\tat java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)\n\tat io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)\n\tat java.base/java.lang.Thread.run(Unknown Source)\n```\n\nYou can see it get much more memory used here extractEntryLogMetadataFromIndex(DefaultEntryLogger.java:1109). The reason is that the header has the wrong data of the header, which should already be fixed by https://github.com/apache/bookkeeper/pull/4607. Then it reading with a wrong map size which could take a lot of memory.\n"
    },
    {
      "commit": "45f7a9202450d7c2093e3dd47fd51ff1519c20e8",
      "tree": "f0391b68c4d32809cf9d91c27568db6f3d27a1e4",
      "parents": [
        "4e6583dd0cf16b294523e72ce8614d7285f88809"
      ],
      "author": {
        "name": "Xiangying Meng",
        "email": "55571188+liangyepianzhou@users.noreply.github.com",
        "time": "Sun Jul 13 07:46:54 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 13 07:46:54 2025 +0800"
      },
      "message": "Fix documentation terminology and method usage (#4617)\n\nFix documentation terminology and method usage"
    },
    {
      "commit": "4e6583dd0cf16b294523e72ce8614d7285f88809",
      "tree": "88301766d48a55e9f45284b22140c1673d7524e2",
      "parents": [
        "3bb2401eefe5fc381f643eb53dbba9c0f3e7b564"
      ],
      "author": {
        "name": "Zixuan Liu",
        "email": "nodeces@gmail.com",
        "time": "Fri Jul 11 18:24:53 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 11 18:24:53 2025 +0800"
      },
      "message": "chore: cleanup useless arg from Dockerfile (#4634)\n\n"
    },
    {
      "commit": "3bb2401eefe5fc381f643eb53dbba9c0f3e7b564",
      "tree": "a375da2ae4e8e99722865bff017564702f763533",
      "parents": [
        "487be9751d5e2975117c9a2f958d5d6955b607ff"
      ],
      "author": {
        "name": "StevenLuMT",
        "email": "lushiji@apache.org",
        "time": "Fri Jul 11 17:07:32 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Fri Jul 11 17:07:32 2025 +0800"
      },
      "message": "[improve][build] solve the dead links found in md (#4637)\n\n* [improve][build] solve the dead links found in md\n\n* timeout is the check timeout for each md file. 10 seconds is a bit short and always times out.\n\n* cancel all future O’Reilly in-person conferences. Instead, we’ll continue to invest in and grow O’Reilly online learning"
    },
    {
      "commit": "487be9751d5e2975117c9a2f958d5d6955b607ff",
      "tree": "4802802e976cd9dc213c0423a29af6ef77f7039c",
      "parents": [
        "f2f6f5648a5a928e3c7665990ee64307aa76eb2b"
      ],
      "author": {
        "name": "StevenLuMT",
        "email": "lushiji@apache.org",
        "time": "Mon Jul 07 21:16:58 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 07 21:16:58 2025 +0800"
      },
      "message": "docs: add news about 4.16.7 and 4.17.2 release (#4635)\n\n"
    },
    {
      "commit": "f2f6f5648a5a928e3c7665990ee64307aa76eb2b",
      "tree": "bc060e54e92a16372b8f8c2d7d3ace2ba9adcc5e",
      "parents": [
        "0f6e8595fdc41fdffb724205941b34fd6f3fa746"
      ],
      "author": {
        "name": "StevenLuMT",
        "email": "lushiji@apache.org",
        "time": "Mon Jul 07 16:11:34 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Mon Jul 07 16:11:34 2025 +0800"
      },
      "message": "Update latest docker image version to 4.17.2 (#4633)\n\n"
    },
    {
      "commit": "0f6e8595fdc41fdffb724205941b34fd6f3fa746",
      "tree": "f3c814269eb2855d38103eaa8c53248009b16f7e",
      "parents": [
        "3942f25be79ca46a03c122e8add3f06f5e327edf"
      ],
      "author": {
        "name": "StevenLuMT",
        "email": "lushiji@apache.org",
        "time": "Sun Jul 06 16:45:09 2025 +0800"
      },
      "committer": {
        "name": "GitHub",
        "email": "noreply@github.com",
        "time": "Sun Jul 06 16:45:09 2025 +0800"
      },
      "message": "Update latest docker image version to 4.17.2 (#4626)\n\n"
    }
  ],
  "next": "3942f25be79ca46a03c122e8add3f06f5e327edf"
}
