Skip to content

feature: add GraalVM native image build support for seata-server#8162

Open
xuxiaowei-com-cn wants to merge 65 commits into
apache:2.xfrom
xuxiaowei-com-cn:xuxiaowei/Seata-Server-GraalVM
Open

feature: add GraalVM native image build support for seata-server#8162
xuxiaowei-com-cn wants to merge 65 commits into
apache:2.xfrom
xuxiaowei-com-cn:xuxiaowei/Seata-Server-GraalVM

Conversation

@xuxiaowei-com-cn

@xuxiaowei-com-cn xuxiaowei-com-cn commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Ⅰ. Describe what this PR did

This PR adds GraalVM Native Image packaging support to the Seata Server, enabling users to build and deploy the Seata Server as a self-contained native binary with sub-second startup time, lower memory footprint, and no JDK dependency at runtime.

Changes Overview (14 files, +3636 / −105)

1. GraalVM Native Image Build Profiles (server/pom.xml)

  • Added native Maven profile that configures spring-boot-maven-plugin (AOT processing) and native-maven-plugin (GraalVM native image compilation).
  • Added nativeTest profile for AOT-based native testing with junit-platform-launcher.
  • Note: The OS/arch auto-activation profiles (native-linux-amd64, native-linux-aarch64, native-darwin-x86_64, native-darwin-aarch64, native-windows-amd64) that set the native.platform classifier were later promoted to the root pom.xml so the property is available to all submodules.
  • Configured build-time initialization for logback, slf4j, fastjson2 and run-time initialization for netty.channel.kqueue.
  • Set mainClass to org.apache.seata.server.ServerApplication.
  • Added Spring-Boot-Native-Processed: true manifest entry.
  • Excluded spring-boot-devtools from native compilation.

2. GraalVM Reachability Metadata (3 new config files)

  • Added reflect-config.json (~2801 lines) — registers classes, methods, and fields for reflective access at native image build time, covering Seata's core components (serializers, codecs, RPC handlers, store managers, configuration providers, discovery providers, etc.).
  • Added resource-config.json (~463 lines) — registers resource bundles and configuration files (Spring factories, SPI service descriptors, SQL scripts, configuration templates) for inclusion in the native image.
  • Added proxy-config.json — registers dynamic proxy interfaces used by Seata (e.g., Spring AOP proxies, configuration binding interfaces).

All metadata files are placed under META-INF/native-image/org.apache.seata/seata-server/ for GraalVM 25 compatibility.

3. CI/CD: Multi-Platform Native Build Workflow (.github/workflows/native.yml)

  • Added a new GitHub Actions workflow (Native Build) that builds GraalVM native images for the Seata Server across 5 platforms: ubuntu-24.04 (amd64), ubuntu-24.04-arm (arm64), macos-26-intel (x86_64), macos-26 (apple silicon), windows-latest (amd64).
  • Uses GraalVM JDK 25 distribution via actions/setup-java@v5.5.0.
  • Includes Maven repository caching (actions/cache/restore@v4 / actions/cache/save@v4) with SNAPSHOT cleanup.
  • Uploads native binaries as workflow artifacts via actions/upload-artifact@v7.0.1.
  • Triggers on push and PR to 2.x, develop, master branches (ignores markdown-only changes).

4. logback-spring.xml Simplification (GraalVM Compatibility)

  • Removed all Janino <if> conditional logic (e.g., <if condition='property("LOGSTASH_APPENDER_ENABLED").equals("true")'>) because Janino's dynamic bytecode compilation is not supported in GraalVM native images.
  • Simplified the configuration to always include console-appender and file-appender by default.
  • Removed the LOG_BASH_DIR external directory path logic.
  • Conditional appenders (logstash, kafka, metric) can still be enabled by uncommenting the corresponding <appender-ref> elements — these are documented with inline comments.

5. Spring AOT Compatibility: @Resource@Autowired Setter Injection

  • AbstractSeataInstanceStrategy: Replaced @Resource field injection with @Autowired setter-based injection for registryProperties, serverProperties, and registryNamingServerProperties. Removed ApplicationContext dependency and @PostConstruct initialization — properties are now injected directly via setters, which is compatible with Spring AOT's closed-world analysis.
  • ServerInstanceStrategyConfig: Changed seataInstanceStrategy() bean method to accept dependencies via constructor parameters and pass them to the strategy via setters, eliminating the implicit ApplicationContext.getBean() lookup.
  • SpringBootConfigurationProvider: Adjusted for AOT compatibility.

6. Build Infrastructure (build/pom.xml, root pom.xml, Makefile)

  • build/pom.xml:
    • Added native-maven-plugin version (1.1.3) to pluginManagement.
    • Added default native.platform property (promoted from server/pom.xml) so it is available to all submodules for GraalVM native-image builds.
  • Root pom.xml:
    • Added Spotless Maven plugin configuration for JSON formatting of native-image metadata files.
    • Added OS/arch auto-activation profiles (native-linux-*, native-darwin-*, native-windows-*) — promoted from server/pom.xml so the native.platform property is available to all submodules for GraalVM native-image builds.
  • Makefile:
    • Added package-server-native-pre, package-server-native, package-server-native-only targets for building native images.
    • Added spotless-check and spotless-apply targets.
    • Added -e flag to all Maven commands for consistent error output.
    • Widened help text column width.

7. Dependency Update

  • Upgraded zstd-jni from 1.5.0-4 to 1.5.7-3 (dependencies/pom.xml).

8. Test Adjustment

  • Disabled AppenderTest (@Disabled) due to the logback configuration simplification that removed Janino-based conditional appender logic.

Usage

Regenerate GraalVM Reachability Metadata (e.g., after adding new components that use reflection):

  1. Run the Seata Server with the Native Image Agent to generate metadata into a temporary directory:

    # Ensure GRAALVM_HOME points to your GraalVM installation directory
    $GRAALVM_HOME/bin/java -agentlib:native-image-agent=config-output-dir=./target/native-image-config -jar ./server/target/seata-server.jar
    # By default, the server starts in `file` config/registry mode, and the agent
    # will only capture reachability metadata for that mode. To generate metadata
    # for other modes, set the `seata.config.type` and/or `seata.registry.type`
    # properties:
    #
    #   seata.config.type=nacos
    #   seata.config.type=consul
    #   seata.config.type=apollo
    #   seata.config.type=zk
    #   seata.config.type=etcd3
    #
    #   seata.registry.type=nacos
    #   seata.registry.type=eureka
    #   seata.registry.type=redis
    #   seata.registry.type=zk
    #   seata.registry.type=consul
    #   seata.registry.type=etcd3
    #   seata.registry.type=sofa
    #   seata.registry.type=seata
    #
    # Example — generate metadata for nacos config + registry:
    #   $GRAALVM_HOME/bin/java -agentlib:native-image-agent=config-output-dir=./target/native-image-config \
    #     -jar ./server/target/seata-server.jar \
    #     --seata.config.type=nacos --seata.registry.type=nacos
  2. Update the generated metadata into the project resource directory: server/src/main/resources/META-INF/native-image/org.apache.seata/seata-server/

    Note: The native-image-agent generates a single merged JSON file (e.g., reachability-metadata.json) in the config-output-dir. This file contains all reachability metadata in one place. It must be split into the following separate configuration files before committing to the project.

    Important: The JSON file is only written to disk after the application terminates normally (i.e., after a graceful shutdown, not a forced kill). You must exercise all relevant code paths (config loading, service registration, transaction coordination, serialization, etc.) during the run, then stop the server cleanly (e.g., Ctrl+C or kill -TERM) to trigger the agent to flush and write the metadata output.

    ⚠️ Important: All business flows must be exercised during the agent run; otherwise the generated metadata will be incomplete, which may cause certain business flows to fail at runtime.

    File Content
    reflect-config.json Classes, methods, and fields registered for reflective access (e.g., serializers, codecs, RPC handlers, store managers, configuration/discovery providers)
    resource-config.json Resource bundles and configuration files for inclusion in the native image (e.g., Spring factories, SPI service descriptors, SQL scripts, configuration templates)
    serialization-config.json Classes registered for Java serialization
    proxy-config.json Dynamic proxy interfaces (e.g., Spring AOP proxies, configuration binding interfaces)
    jni-config.json JNI-related configuration (if applicable; may be empty if no JNI calls are used)

    Use the top-level JSON keys in the generated file to identify which entries belong to each category, then distribute them into the corresponding files.

  3. Compile the Native Image

Build the Native Image:

# One-step: build all dependencies and compile native image
make package-server-native

# Or manually:
mvn clean install -DskipTests -pl server -am
mvn clean package -DskipTests -pl server -Pnative spring-boot:process-aot native:compile

Run the native binary (no JDK required):

./server/target/seata-server-{version}-{platform}

Ⅱ. Does this pull request fix one issue?

#8161

This PR implements the feature proposal described in issue #8137 (Spring Boot 4 upgrade) follow-up — adding GraalVM Native Image support to the Seata Server for improved deployment efficiency and cloud-native compatibility.

Ⅲ. Why don't you add test cases (unit test/integration test)?

  • The native image build itself serves as an integration test — the CI workflow (native.yml) builds native binaries on 5 platforms on every PR/push, and a successful native image compilation validates the GraalVM reachability metadata and AOT compatibility.
  • AppenderTest has been disabled because it tested Janino <if> conditional logic in logback-spring.xml, which was removed for GraalVM compatibility. The logback appender behavior (console, file) is implicitly verified by the native image build succeeding and the server starting correctly.
  • A nativeTest Maven profile is provided for future AOT-based native testing using junit-platform-launcher.
  • Config, Registry & Store Mode Tests: The following test matrix tracks verification of each config/registry/store mode against the GraalVM native image. These are follow-up tasks to ensure full feature parity as described in "Phased approach" (Phase 2+).

Config Mode Tests

Config Mode Manual Test CI Test Notes
file ✅ Done ✅ Done Default mode, verified via CI native build
nacos ❌ Blocked ⬜ TODO Requires Nacos server
consul ⬜ TODO ⬜ TODO Requires Consul server
apollo ⬜ TODO ⬜ TODO Requires Apollo server
zk ⬜ TODO ⬜ TODO Requires ZooKeeper server
etcd3 ⬜ TODO ⬜ TODO Requires Etcd3 server

Registry Mode Tests

Registry Mode Manual Test CI Test Notes
file ✅ Done ✅ Done Default mode, verified via CI native build
nacos ❌ Blocked ⬜ TODO Requires Nacos server
eureka ⬜ TODO ⬜ TODO Requires Eureka server
redis ⬜ TODO ⬜ TODO Requires Redis server
zk ⬜ TODO ⬜ TODO Requires ZooKeeper server
consul ⬜ TODO ⬜ TODO Requires Consul server
etcd3 ⬜ TODO ⬜ TODO Requires Etcd3 server
sofa ⬜ TODO ⬜ TODO Requires SOFARegistry server
seata ⬜ TODO ⬜ TODO Requires Seata registry server

Store Mode Tests

Store Mode Manual Test CI Test Notes
file ✅ Done ✅ Done Default mode, verified via CI native build
db ⬜ TODO ⬜ TODO Requires database (MySQL/PostgreSQL/etc.)
redis ⬜ TODO ⬜ TODO Requires Redis server
raft ⬜ TODO ⬜ TODO Requires Seata Server cluster (Raft)

Test Plan for Each Mode

Each mode test should verify:

  1. Startup: Seata Server native image starts successfully with the target config/registry/store mode.
  2. Configuration Loading (config modes): Configuration properties are correctly loaded from the remote config center.
  3. Service Registration (registry modes): Seata Server registers itself to the registry center and is discoverable by clients.
  4. Service Discovery (registry modes): Seata Server can discover other registered services.
  5. Session Persistence (store modes): Transaction session data (global sessions, branch sessions) is correctly persisted to and loaded from the target store.
  6. Session Query (store modes): Session data can be queried and listed from the target store.
  7. Health Check / Heartbeat: The registered service maintains heartbeat and is not evicted.
  8. Graceful Shutdown: Service is deregistered on normal shutdown.
  9. GraalVM Reachability: No ClassNotFoundException, NoSuchMethodException, or reflection errors at runtime for the mode-specific components.

Ⅳ. Describe how to verify it

Option 1: Local verification (requires GraalVM JDK 25)

# Build dependencies
mvn clean install -DskipTests -pl server -am

# Compile native image
mvn clean package -DskipTests -pl server -Pnative spring-boot:process-aot native:compile

# Run the native server
./server/target/seata-server-*-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')

Expected: The Seata Server starts in milliseconds (vs. seconds in JVM mode) and is ready to accept transactions.

Option 2: CI verification

Check the Native Build workflow results on this PR — it builds native images on ubuntu-24.04 (amd64), ubuntu-24.04-arm (arm64), macos-26-intel, macos-26 (apple silicon), and windows-latest.

Ⅴ. Special notes for reviews

  1. GraalVM reachability metadata (reflect-config.json, resource-config.json, proxy-config.json): These files were generated through iterative native image builds and testing. They register all classes, methods, resources, and proxies that Seata accesses via reflection at runtime. Reviewers should focus on whether any critical Seata components are missing from these configs.

  2. Injection style change: @Resource field injection was replaced with @Autowired setter/constructor injection in AbstractSeataInstanceStrategy and ServerInstanceStrategyConfig. This is required because Spring AOT's closed-world analysis cannot resolve @Resource-injected fields during native compilation. The runtime behavior is identical.

  3. logback-spring.xml breaking change: The removal of Janino <if> conditions means that LOGSTASH_APPENDER_ENABLED, KAFKA_APPENDER_ENABLED, and METRIC_APPENDER_ENABLED properties no longer dynamically control appender inclusion at runtime. Users who need these appenders must manually uncomment the corresponding <appender-ref> elements in logback-spring.xml. This is a known limitation of GraalVM native images (no runtime bytecode generation). A follow-up PR could add programmatic appender registration via Spring's @Conditional beans.

  4. Phased approach: This PR focuses on Phase 1 — basic server startup, configuration loading, and core transaction coordination in native mode. Full feature parity with JVM mode (all serializers, all store modes, all discovery modes) will be addressed in follow-up PRs as testing coverage expands.

  5. PGO (Profile-Guided Optimization): Not included in this PR. Can be added later to further improve native image runtime performance.

- Add native-maven-plugin version management in build/pom.xml
- Add native and nativeTest profiles in server/pom.xml for GraalVM AOT compilation
- Add Makefile targets: package-server-native, package-server-native-pre, package-server-native-only
…5.5.0 in native workflow

1. actions/setup-java@v3.12.0: No supported distribution was found for input graalvm
2. Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: actions/checkout@v3, actions/setup-java@v3.12.0. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
- Add ubuntu-latest-arm to native build matrix alongside ubuntu-latest
- Update step names to include OS identifier for clarity
- Add -am flag to Makefile package-server-native-pre target to also build server module dependencies
…runners

Add matrix.os to concurrency group and cancel-in-progress to avoid
ubuntu-latest and ubuntu-latest-arm builds cancelling each other.
Comment out concurrency group and cancel-in-progress to avoid
interference between multiple native build workflow runs.
Replace ubuntu-latest with ubuntu-24.04 for more reproducible builds.
Add macos-26-intel, macos-26-large, and windows-latest to the
native build CI matrix alongside existing Linux runners.
…ve profiles

Add --initialize-at-run-time=io.netty.channel.kqueue to both
native and nativeTest profiles to fix GraalVM native image
issues related to Netty's kqueue transport on non-macOS platforms.
- Add OS/arch-specific Maven profiles (linux-amd64, linux-arm64, darwin-amd64,
  darwin-arm64, windows-amd64) to set native.platform property
- Configure native-maven-plugin imageName to
  seata-server-{version}-{platform}, e.g. seata-server-2.8.0-SNAPSHOT-linux-amd64
- Add upload-artifact step to native CI workflow
- Exclude .jar files from uploads to only ship the native binary
…server

$GRAALVM_HOME/bin/java -agentlib:native-image-agent=config-output-dir=./target/native-image-config -jar ./server/target/seata-server.jar
- reflect-config.json: rename 'type' to 'name' key per GraalVM 25 schema
- reflect-config.json: remove deprecated 'jniAccessible' attribute
- reflect-config.json: extract proxy entries to separate proxy-config.json
- resource-config.json: convert array to object format with 'resources.includes'
- resource-config.json: rename 'glob' to 'pattern' key
- server/pom.xml: add --initialize-at-build-time=com.alibaba.fastjson2
Add Spotless Jackson JSON formatter in pom.xml and reformat
proxy-config.json, reflect-config.json, resource-config.json
Add constructor and method entries for Apollo config classes
(ConfigPropertySourceFactory, PlaceholderHelper, SpringValueRegistry),
Iterable.iterator(), Iterator.hasNext()/next(), and comprehensive
StringBuilder methods to ensure proper reflection access in native image.
…H_DIR logic

Remove the if/else branch that switched between resource and file
includes based on LOG_BASH_DIR property. Unify to always use resource
includes. Also remove conditional appender-refs for logstash, kafka,
and metric appenders, replacing them with comments.

logback-spring.xml: remove <if> conditionals that require janino
runtime compilation (not supported in native image)
…o build/pom.xml

Move the fallback native.platform property to the build module POM,
keeping it alongside the native-maven-plugin version definition for
better co-location of native-image build configuration.
…che#8162)

Add PR apache#8162 to the 2.x changelog under the feature section in both
English and Chinese versions.
@xuxiaowei-com-cn

xuxiaowei-com-cn commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

WIP

/cc @funky-eyes

@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.83%. Comparing base (b57d96c) to head (e5bd4ce).

Additional details and impacted files
@@             Coverage Diff              @@
##                2.x    #8162      +/-   ##
============================================
+ Coverage     72.79%   72.83%   +0.03%     
  Complexity     1141     1141              
============================================
  Files          1151     1151              
  Lines         42272    42272              
  Branches       5045     5045              
============================================
+ Hits          30773    30789      +16     
+ Misses         9023     9009      -14     
+ Partials       2476     2474       -2     
Files with missing lines Coverage Δ
...gure/provider/SpringBootConfigurationProvider.java 20.43% <100.00%> (ø)

... and 9 files with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…le documentation

- Add package-server-native-metadata-file / package-server-native-metadata-file-only targets
  in Makefile for collecting GraalVM native-image reflection/proxy/resource metadata
- Add release-seata-jar Maven profile (simplified repackage without ZIP layout and
  classifier, suitable for native-image agent metadata collection)
- Add detailed comments for Makefile variables, .PHONY declarations, and Maven args
- Fix indentation consistency in .PHONY target list
- Add reflection entries for Nacos client (config, naming, remote APIs),
  gRPC/Netty shaded classes, Spring Boot config classes, and more
- Add resource patterns for SPI service files, Nacos API packages,
  and nacos configuration files
- Add missing methods and fields for existing reflect entries
- reflect-config: add java.nio.Bits.UNALIGNED field, Nacos shaded Netty classes
  (AbstractByteBufAllocator, UnpooledByteBufAllocator, ReferenceCountUtil,
  ResourceLeakDetector, AbstractChannel, AbstractChannelHandlerContext,
  DefaultChannelPipeline)
- reflect-config: add Nacos naming remote request/response classes
  (BatchInstanceRequest, PersistentInstanceRequest, BatchInstanceResponse)
- reflect-config: add Nacos remote SetupAckRequest/SetupAckResponse
- reflect-config: add Seata serializer classes (Serializer, SeataSerializer,
  SeataSerializerV0, SeataSerializerV1)
- resource-config: add Seata SPI services (Serializer, DistributedLocker,
  DataSourceProvider, RateLimiter)
- resource-config: add Nacos SPI services (Payload, ServerListProvider,
  FailoverDataSource, AbstractAbilityControlManager, LabelsCollector,
  NacosLoggingAdapter, NacosLoggingAdapterBuilder, AbstractParamChecker,
  PathEncoder, AbstractClientAuthService, ServerProvider)
…nary

- Add test job that downloads and verifies the native binary across all platforms
- Verify native binary starts successfully with PID liveness check
- Add separate port 8091 listening check step using ss/netstat
- Clean up native process after verification
- Update upload artifact path pattern from seata-server-* to seata-*-*
- Remove single-value java matrix, hardcode java-version to 25
- Add type matrix to test job for future extensibility
- Add conditional if on verify step based on matrix.type
- Move native binary verification and port 8091 check from separate test job into build job
- Remove chmod, file, and kill commands
- Reorder delete snapshots and save cache steps after verification
- Replace runner.temp with worktree-relative pid file path to avoid Windows backslash escaping issues in shell

- Replace ss/netstat/lsof with curl-based HTTP health check for cross-platform port verification on Windows/macOS/Linux

- Add retry loop (6x5s) to allow native image sufficient startup time
Root path returns 404, causing curl to fail. Use /health endpoint which returns 200 ok.
@WangzJi WangzJi added the type: feature Category issues or prs related to feature request. label Jul 10, 2026
Add a new test-suite/test-native module containing:
- Spring Boot application with early database initialization
- Entity classes (Account, Order, Storage, UndoLog)
- DAO layer (AccountDAO, OrderDAO, StorageDAO, UndoLogDAO)
- Service layer with AT mode business logic
- Integration tests (DataSourceProxyModeAtTests, SeataTestNativeApplicationTests)
- SQL schema and test data setup

Also update native workflow, Makefile, and POM files to include
the new module in the build.
… prevent UnsupportedFeatureError on shutdown
- Add REST controllers (Account, Order, Storage, Seata) and DTOs for test-native module
- Replace DataSourceProxyModeAtTests with focused service/controller test classes
- Add test-native app startup step in native build CI workflow
- Add GraalVM native-image agent config generation to Makefile
- Add Apache License 2.0 headers to all test-native Java source files
- Restrict native.yml 'Start test-native app' step to Linux only
- Remove unused imports, Logger fields, and unused injected dependencies
  in service implementations
…tting

- Remove unused test data (U002, U003 accounts; C001, C002 storage)
- Update all test references from U003 to U001
- Adjust U001 initial balance from 10000 to 100
- Clean up empty lines in BusinessServiceImpl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature Category issues or prs related to feature request.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants