Thank you for voting on this PIP. Results:
4 binding +1s: * Matteo * Mattison * Zixuan * PengHui 3 non-binding +1s: * Philipp * Yike * Tao -- Matteo Merli <[email protected]> On Mon, Mar 23, 2026 at 12:22 PM PengHui Li <[email protected]> wrote: > +1 (binding) > > Regards, > Penghui > > On Mon, Mar 23, 2026 at 7:14 AM Zixuan Liu <[email protected]> wrote: > > > +1 (binding) > > > > Thanks, > > Zixuan > > > > Matteo Merli <[email protected]> 于2026年3月22日周日 00:50写道: > > > > > https://github.com/apache/pulsar/pull/25359 > > > > > > PoC PR: https://github.com/merlimat/pulsar/pull/16 > > > > > > > > > > > > --------------------------------------------------------------------------------------------- > > > > > > # PIP-463: Migrate Build System from Maven to Gradle > > > > > > # Background Knowledge > > > > > > Apache Pulsar currently uses Maven as its build system. The project > > > has grown to over 100 modules > > > with complex dependency relationships, shaded JARs, NAR packaging, and > > > Docker image builds. > > > Maven's sequential execution model and limited caching capabilities > > > result in long build times > > > that impact developer productivity and CI throughput. > > > > > > [Gradle](https://gradle.org/) is a modern build system used by > > > large-scale Java projects > > > (e.g., Spring Boot, Micronaut, Apache Kafka). It provides parallel > > > task execution, > > > fine-grained caching, and incremental compilation out of the box. > > > > > > # Motivation > > > > > > The current Maven build has several pain points that affect developer > > > velocity and CI efficiency: > > > > > > **Slow local builds.** A full `mvn install -DskipTests` takes 5-8 > > > minutes on a modern machine. > > > Developers frequently wait for unrelated modules to rebuild when > > > iterating on a single component. > > > Maven has no built-in mechanism to skip unchanged modules — it > > > rebuilds everything in the reactor. > > > > > > **Slow CI.** The CI pipeline takes 50-60 minutes end-to-end. Maven's > > > lack of caching means > > > each CI run starts from scratch. Test jobs must either rebuild > > > everything or rely on fragile > > > artifact-sharing workarounds. > > > > > > **Imprecise dependency tracking.** Maven treats the entire module as > > > the unit of rebuild. > > > Changing a test resource file triggers a full recompile of the module. > > > There is no way to > > > run "only the tests affected by my change" — developers must run the > > > entire test suite > > > for a module or manually specify test classes. > > > > > > **Limited parallelism.** Maven's `-T` flag enables module-level > > > parallelism, but tasks within > > > a module still run sequentially. The Pulsar build has several > > > bottleneck modules (e.g., > > > `pulsar-broker`) where compilation, resource processing, and test > > > execution could overlap > > > with other modules but don't. > > > > > > **Complex shading and packaging.** The project uses Maven Shade > > > plugin, NAR plugin, and > > > custom Ant tasks for packaging. These configurations are verbose, hard > > > to maintain, and > > > have subtle interactions (e.g., the `ahc-default.properties` content > > > replacement for > > > AsyncHttpClient requires an Ant `<replace>` task in Maven but is a > > > single `filesMatching` > > > call in Gradle). > > > > > > **Poor IDE integration for multi-module builds.** IntelliJ IDEA's > > > Maven import for a project > > > of Pulsar's size is slow and memory-intensive. Gradle's tooling API > > > provides faster, > > > more reliable IDE synchronization. > > > > > > # Goals > > > > > > ## In Scope > > > > > > - **1:1 functional equivalence with Maven.** The Gradle build produces > > > identical artifacts: > > > - Server distribution tarball (`apache-pulsar-X.Y.Z-bin.tar.gz`) > > > with the same JARs > > > - Shell distribution tarball > > > - IO connectors distribution (NAR files) > > > - Offloaders distribution (NAR files) > > > - Docker images (`pulsar`, `pulsar-all`, `java-test-image`, > > > `pulsar-test-latest-version`) > > > - Shaded client JARs (`pulsar-client`, `pulsar-client-admin`, > > > `pulsar-client-all`) > > > verified to contain the same classes and relocations as Maven > output > > > > > > - **All CI tests passing.** Unit tests, integration tests, system > > > tests, shade tests (Java 17/21/24), > > > and backward compatibility tests all pass on the Gradle build. > > > > > > - **Enforced dependency management.** A `pulsar-dependencies` platform > > > module (Gradle's equivalent > > > of Maven's `dependencyManagement`) ensures consistent dependency > > > versions across all modules. > > > > > > - **Version catalog.** A single `gradle/libs.versions.toml` file > > > defines all dependency coordinates > > > and versions, replacing scattered version properties across 100+ POM > > > files. > > > > > > - **CI workflow migration.** All GitHub Actions workflows converted > > > from Maven to Gradle commands. > > > > > > ## Out of Scope > > > > > > - Changing the project's module structure or merging/splitting modules > > > - Migrating to Kotlin DSL for production source code > > > - Gradle-specific optimizations beyond what Maven provides (e.g., > > > build cache server, > > > remote caching) — these are future enhancements enabled by the > > migration > > > - Removing the ability to build individual modules in isolation > > > > > > # High Level Design > > > > > > The migration introduces Gradle build scripts alongside (and > > > eventually replacing) the existing > > > Maven POM files. The approach is: > > > > > > 1. **Add Gradle build files** for all modules (`build.gradle.kts`, > > > `settings.gradle.kts`, > > > `gradle/libs.versions.toml`) > > > 2. **Convert CI workflows** from Maven to Gradle commands > > > 3. **Remove Maven files** (`pom.xml`, `mvnw`, `.mvn/`) > > > > > > The Gradle build is structured as: > > > > > > ``` > > > settings.gradle.kts # Module includes and plugin repositories > > > build.gradle.kts # Root build: common config, enforced > > > platform > > > gradle/libs.versions.toml # Version catalog (single source of > > > truth for versions) > > > pulsar-dependencies/ # Enforced platform module (replaces > > > dependencyManagement) > > > <module>/build.gradle.kts # Per-module build script > > > ``` > > > > > > Key design decisions: > > > > > > - **Shadow plugin** for shaded JARs (replaces Maven Shade), with > > > `filesMatching` for > > > property file content relocation > > > - **NAR plugin** (`io.github.merlimat.nar`) for connector packaging > > > - **LightProto plugin** for protobuf/lightproto code generation > > > - **Conditional project includes** for shade test modules (avoids > > > implicit parent project conflicts) > > > - **Enforced platform** (`pulsar-dependencies`) for version pinning > > > across all modules > > > > > > # Detailed Design > > > > > > ## Design & Implementation Details > > > > > > ### Build Performance Improvements > > > > > > | Aspect | Maven | Gradle | > > > |--------|-------|--------| > > > | Incremental compilation | No | Yes — only recompiles changed files | > > > | Task-level caching | No | Yes — skips tasks whose inputs haven't > > changed > > > | > > > | Parallel execution | Module-level only (`-T`) | Task-level > > > (automatic dependency graph) | > > > | Configuration caching | No | Yes — reuses build configuration across > > > runs | > > > | Local build cache | No | Yes — persists across builds | > > > | Remote build cache | No | Yes — shared across CI and developers > > (future) > > > | > > > > > > **Expected impact:** > > > - Local incremental builds (after initial): **seconds** instead of > > minutes > > > - CI with caching: **30-50% faster** (exact numbers depend on cache hit > > > rates) > > > - "Build only what I need to test": `./gradlew :pulsar-broker:test` > > builds > > > only > > > the broker and its dependencies, skipping unrelated modules entirely > > > > > > ### Develocity Integration > > > > > > Gradle provides native integration with > > > [Develocity](https://gradle.com/develocity/) > > > (formerly Gradle Enterprise), hosted by the ASF at > > > `develocity.apache.org`. Every CI > > > build automatically publishes a build scan that provides: > > > > > > - **Test execution details**: per-test timings, pass/fail status, > > > output logs, and > > > stack traces — all searchable and filterable without downloading CI > > > artifacts > > > - **Task execution timeline**: visual breakdown of what ran, what was > > > cached, and what > > > was up-to-date, making it easy to identify bottleneck tasks > > > - **Dependency resolution**: full dependency tree with conflict > > > resolution details > > > - **Build comparison**: diff two builds to see what changed in task > > > execution or outputs > > > - **Failure analysis**: aggregated view of flaky tests across builds > > > > > > Example build scan from the PoC CI run: > > > [ > > > > > > https://develocity.apache.org/s/h6ckzn3nn4w2s](https://develocity.apache.org/s/h6ckzn3nn4w2s) > > > > > > This level of observability is not available with the Maven build > today. > > > > > > ### Dependency Management > > > > > > Maven's `dependencyManagement` in the root POM is replaced by: > > > > > > 1. **Version catalog** (`gradle/libs.versions.toml`): Defines all > > > dependency coordinates > > > and version numbers in one file. Modules reference dependencies as > > > `libs.netty.buffer` > > > instead of hardcoded group:artifact:version strings. > > > > > > 2. **Enforced platform** (`pulsar-dependencies`): A `java-platform` > > > module that creates > > > version constraints from the catalog. Applied to all subprojects via > > > `implementation(enforcedPlatform(project(":pulsar-dependencies")))`. > > > This ensures > > > transitive dependencies are pinned to the same versions Maven would > > > resolve. > > > > > > ### Shaded JAR Configuration > > > > > > The Shadow plugin replaces Maven Shade. Key differences handled: > > > > > > - **AsyncHttpClient properties**: Maven uses Ant `<replace>` to fix > > > property name prefixes > > > in `ahc-default.properties`. Gradle uses `filesMatching { filter { } > > }`. > > > - **Dependency include/exclude**: Shadow's `dependencies { > > > include/exclude }` DSL replaces > > > Maven Shade's `<includes>/<excludes>`. > > > - **Relocation**: Shadow's `relocate()` is functionally identical to > > > Maven Shade's. > > > > > > ### NAR Packaging > > > > > > A custom NAR Gradle plugin (`io.github.merlimat.nar`) handles > > > connector packaging. > > > Global exclusions for platform modules (provided by > > > `java-instance.jar` at runtime) > > > are configured in the root `build.gradle.kts`. > > > > > > ### Module-Specific Overrides > > > > > > Some modules require version overrides that differ from the enforced > > > platform: > > > > > > - **`kinesis-kpl-shaded`**: Forces `protobuf-java:4.29.0` (KPL > > > requires protobuf 4.x, > > > while Pulsar uses 3.x). The protobuf is relocated so no runtime > > conflict. > > > - **`jclouds-shaded`**: Forces Guice 7.0.0, > > `jakarta.annotation-api:3.0.0`, > > > `jakarta.ws.rs-api:3.1.0`, `jakarta.inject-api:2.0.1` (jclouds 2.6.0 > > > requires > > > Jakarta EE 10+ APIs). All are bundled in the shadow JAR. > > > > > > ## Public-facing Changes > > > > > > ### Configuration > > > > > > No new broker/client configuration options. The build system change is > > > transparent to users. > > > > > > ### CLI > > > > > > - `mvn` commands replaced by `./gradlew` commands in documentation and > > > scripts > > > - `src/set-project-version.sh` updated to modify > > > `gradle/libs.versions.toml` > > > > > > ### Binary Artifacts > > > > > > Artifacts are functionally identical. Minor differences: > > > - Some shaded JARs may have slightly different class counts due to > > > Shadow vs Shade plugin > > > differences in handling `package-info.class` files (no runtime > impact) > > > > > > # Security Considerations > > > > > > No security implications. The build system change does not affect > > > Pulsar's runtime > > > security model, authentication, or authorization. > > > > > > The Gradle wrapper (`gradlew`) is committed to the repository with a > > > checksum-verified > > > distribution URL, following the same security model as the Maven > wrapper. > > > > > > # General Notes > > > > > > The implementation PR demonstrates full CI green status across all test > > > suites, > > > confirming functional equivalence with the Maven build. > > > > > > # Links > > > > > > * Proof of Concept PR (CI fully green): > > > https://github.com/merlimat/pulsar/pull/16 > > > > > > > > > > > > -- > > > Matteo Merli > > > <[email protected]> > > > > > >
