+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]>
> >
>

Reply via email to