You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by "lhotari (via GitHub)" <gi...@apache.org> on 2026/06/15 16:13:53 UTC
[PR] [improve][build] Restore fastutil and minimize it in the client and server distributions [pulsar]
lhotari opened a new pull request, #26032:
URL: https://github.com/apache/pulsar/pull/26032
Closes #24996
Reverts #25413 and addresses the review feedback in #26028.
### Motivation
#25413 removed the `fastutil` dependency and replaced it with hand-written primitive
collections under `org.apache.pulsar.common.util.collections` (`Int2ObjectOpenHashMap`,
`IntIntPair`, `Long2ObjectOpenHashMap`, and similar). That change was driven by a real
problem — the full `fastutil` jar is ~24 MB / ~12,965 classes and we don't want to ship
all of it — but the conversion of the Maven build's fastutil *minification* to Gradle was
the actual blocker, not fastutil itself.
As discussed in the #26028 review, hand-rolled primitive collections are the wrong
trade-off: custom collection implementations have repeatedly been a source of bugs and are
an ongoing maintenance burden, and there is a large combinatorial space of primitive
collection types we'd have to reimplement and maintain. fastutil is a mature library whose
collections have been optimized for performance for many years, and when a new use case
needs another primitive collection, fastutil already has it — it can be used directly
without adding more custom classes to the Pulsar code base.
Memory efficiency is a concrete reason this matters. The Key_Shared draining-hashes design
in [PIP-379](https://github.com/apache/pulsar/blob/master/pip/pip-379.md) uses fastutil
primitive collections specifically to keep the per-subscription tracking footprint small,
and its memory model is computed directly on those types:
- The draining-hashes tracker is a fastutil `Int2ObjectOpenHashMap<DrainingHashEntry>`,
using primitive `int` keys to avoid boxed `Integer` keys.
- The per-ledger pending-acks map is a `Long2ObjectSortedMap<IntIntPair>` backed by a
`Long2ObjectRBTreeMap`, with `IntIntPair.of(batchSize, stickyKeyHash)` as the value —
primitive `long` keys and a primitive int-pair value instead of boxed wrappers.
- PIP-379's sizing (≈52 bytes/entry, padded to 80 to account for memory alignment and the
`Int2ObjectOpenHashMap` overhead; bounded worst case, ~0 once hashes drain) is derived
from these fastutil maps.
Reimplementing these collections by hand undercuts both the correctness and the memory
characteristics that design relies on, so there is a memory-usage regression unless
fastutil is restored.
Finally, this resolves #24996: the full ~24 MB fastutil inflates deployments that use the
non-shaded `-original` clients (e.g. exceeding AWS Lambda's unpacked-size limit). With this
change, consumers of `pulsar-client-original` get a ~1.3 MB minimized fastutil jar instead
of the full one.
### Modifications
- **Restore fastutil (revert of #25413).** Switch the broker and client back to fastutil
in `NegativeAcksTracker`, `PendingAcksMap`, `Consumer`, `DrainingHashesTracker`,
`InMemoryRedeliveryTracker`, `InMemoryDelayedDeliveryTracker`, and
`PersistentStickyKeyDispatcherMultipleConsumers` (and their tests), preferring fastutil
primitive sorted maps (`Long2ObjectRBTreeMap` / `Long2ObjectAVLTreeMap`) over
`java.util.TreeMap`. Post-#25413 improvements are kept (Roaring bitmap usage, the
delayed-delivery race fixes, and the `PendingAcksMap` O(1) `size` optimization from
#26019) — only the collection types are swapped back. Delete the hand-written
`org.apache.pulsar.common.util.collections.*` classes and tests. Re-add `fastutil`
(`8.5.18`) to `pulsar-broker`, `pulsar-client`, and the version catalog.
- **Reusable `pulsar.minimized-dependencies-conventions` plugin.** A library-agnostic
build-logic convention (+ `MinimizedDependenciesExtension`, exposed as `minimizedJar { }`).
A packaging module applies it, declares reachability roots as `api(project(...))`
dependencies, and sets `minimizedDependencies` (`"group:name"` list) + `maxRetainedClasses`.
It builds a Shadow jar bundling only the configured libraries, runs `minimize()` (seeded
from the `api` roots, since Shadow's `UnusedTracker` uses the project's own + `api`-scoped
classes and these modules have no source), strips the build-only `api` seed from the
`apiElements`/`runtimeElements` variants, and adds a config-cache-compatible
`verifyMinimizedJar` check (wired into `check`) that fails if the retained class count
exceeds the limit.
- **Two minimized modules.** `pulsar-client-fastutil-minimized` (roots: `pulsar-client-original`,
`pulsar-client-tools`, `pulsar-client-admin-original`; ~591 classes) and
`pulsar-broker-fastutil-minimized` (roots: `pulsar-broker` + `pulsar-client-original`,
~818 classes — a superset of the client set).
- **Client shaded jars** (`pulsar-client-shaded`, `-all`, `-admin-shaded`) exclude
`it.unimi.dsi:fastutil` from `pulsar-client(-admin)-original` and bundle
`pulsar-client-fastutil-minimized`; the client shade conventions relocate
`it.unimi.dsi.fastutil` under the shade prefix.
- **Server and shell distributions** exclude the full fastutil jar from their `distLib` and
ship a minimized one instead: the server bundles the (unrelocated)
`pulsar-broker-fastutil-minimized`, and the pulsar-shell CLI bundles
`pulsar-client-fastutil-minimized`. Both binary LICENSE files carry a free-text fastutil
(Apache-2.0) attribution without a jar filename — accepted by `checkBinaryLicense`, since
the classes now ship inside a Pulsar-owned jar.
- **`pulsar-client-original` published metadata** (publication-only) rewrites the POM
(`pom.withXml`) and Gradle Module Metadata (post-processing the generated `.module` JSON)
to replace the `it.unimi.dsi:fastutil` dependency with
`org.apache.pulsar:pulsar-client-fastutil-minimized`. There is no build-graph dependency
on the minimized module — intra-build, `pulsar-client-original` still exposes full fastutil
(the broker uses more classes than the client set). `pulsar-client-fastutil-minimized` is
published; the broker variant is not.
- **Version bumps.** fastutil `8.5.18`; GradleUp Shadow `9.4.1` → `9.4.2`.
Resulting sizes (built and measured):
| Jar | Size | Classes |
|-----|------|---------|
| `pulsar-client-fastutil-minimized` | ~1.3 MB | 591 |
| `pulsar-broker-fastutil-minimized` | ~1.7 MB | 818 |
| Full `fastutil-8.5.18.jar` | ~24 MB | 12,965 |
≈17× smaller (client) and ≈14× smaller (broker), keeping exactly the fastutil classes the
broker and client actually reach.
### Verifying this change
This change is already covered by existing broker and client unit and integration tests —
the collection types are swapped back to fastutil while behavior (and the kept post-#25413
improvements) is unchanged, so the Key_Shared, negative-ack, redelivery, delayed-delivery,
and pending-ack suites exercise it. The build-logic additions are guarded by the
`verifyMinimizedJar` check and by `checkBinaryLicense` for the distribution LICENSE changes.
CI passes (build, unit, and integration suites, including the shaded-jar and distribution
assembly steps).
### Does this pull request potentially affect one of the following parts:
- [x] Dependencies (add or upgrade a dependency)
- [ ] The public API
- [ ] The schema
- [ ] The default values of configurations
- [ ] The threading model
- [ ] The binary protocol
- [ ] The REST endpoints
- [ ] The admin CLI options
- [ ] The metrics
- [x] Anything that affects deployment
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [improve][build] Restore fastutil and minimize it in the client and server distributions [pulsar]
Posted by "merlimat (via GitHub)" <gi...@apache.org>.
merlimat merged PR #26032:
URL: https://github.com/apache/pulsar/pull/26032
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org