Skip to content

[Feature] Provide GraalVM Native Image packaging for Seata Server #8161

Description

@xuxiaowei-com-cn

Check Ahead

  • I have searched the issues of this repository and believe that this is not a duplicate.

  • I am willing to try to implement this feature myself.

Why you need it?

Background

The Seata Server has been upgraded to Spring Boot 4.0.6 (see #8137), which is built on Spring Framework 7.0.7. As a result, the server module now requires JDK 17+ (previously JDK 8). This JDK version jump introduces a deployment friction: users must ensure the target environment has the correct JDK version installed.

Problem

  1. JDK dependency management burden: Currently, users who deploy the Seata Server need to manually install and manage JDK 17+ on their servers or container images. This adds complexity to deployment and operation, especially in environments where multiple Java applications with different JDK requirements coexist.

  2. Startup time and resource footprint: The standard JVM-based Seata Server has non-trivial startup time and memory overhead. For cloud-native and serverless scenarios (e.g., Kubernetes pods scaling up rapidly), faster startup and lower memory consumption are highly desirable.

  3. Spring Boot 4 already supports it: Spring Boot 4 (via Spring Framework 7) provides first-class AOT (Ahead-of-Time) compilation and GraalVM Native Image support out of the box. The spring-boot-maven-plugin can produce native executables with minimal configuration. However, Seata Server currently does not leverage this capability.

Benefits of Native Image

Aspect JVM Mode Native Image Mode
JDK requirement Must install JDK 17+ Self-contained binary, no JDK needed
Startup time Seconds (JVM warmup) Milliseconds to sub-second
Memory footprint Higher (JVM overhead) Lower (no JVM, no class metadata)
Container image size Larger (JDK base image) Smaller (distroless / scratch base)
Deployment Requires Java runtime Single static binary

How it could be?

Proposal: Add GraalVM Native Image packaging support to seata-server

Add a Maven profile (e.g., native) to the server/pom.xml that uses spring-boot-maven-plugin with the native classifier (or the native-maven-plugin) to produce a GraalVM native executable of the Seata Server.

High-level Steps

  1. Add AOT/Native build configuration to server/pom.xml:

    • Introduce a native Maven profile that triggers GraalVM native image compilation via spring-boot-maven-plugin's process-aot goal and GraalVM's native-maven-plugin.
    • Configure native image build args (e.g., --enable-url-protocols=http, --add-opens for reflection-heavy libraries).
  2. Provide GraalVM reachability metadata (if needed):

    • Some Seata components (e.g., Netty, Kryo serializer, dynamic proxies, reflection-based config loading) may require explicit GraalVM reflect-config.json, resource-config.json, or proxy-config.json.
    • Spring Boot 4's AOT engine can auto-generate most of these, but manual hints may be required for non-Spring-managed components.
  3. Container image integration (optional but recommended):

    • Extend the existing jib-maven-plugin configuration (or add a Dockerfile.native) to build a minimal container image containing only the native binary (e.g., based on gcr.io/distroless/cc-debian12 or scratch).
  4. CI/CD and distribution:

    • Add a CI job to build and publish native binaries as release artifacts (e.g., seata-server-{version}-linux-amd64, seata-server-{version}-linux-arm64).
    • Optionally publish native container images (e.g., apache/seata-server:{version}-native).

Example Pseudo-configuration

<!-- In pom.xml -->
<plugin>
    <groupId>com.diffplug.spotless</groupId>
    <artifactId>spotless-maven-plugin</artifactId>
    <version>${spotless-maven-plugin.version}</version>
    <configuration>
        <json>
            <includes>
                <include>src/main/resources/META-INF/native-image/${project.groupId}/${project.artifactId}/*.json
                </include>
            </includes>
            <jackson/>
        </json>
    </configuration>
</plugin>
<!-- In build/pom.xml -->
<properties>
    <native-maven-plugin.version>1.1.3</native-maven-plugin.version>
</properties>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <version>${native-maven-plugin.version}</version>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<!-- In server/pom.xml -->
<profiles>
    <profile>
        <id>native-linux-amd64</id>
        <activation>
            <os>
                <name>linux</name>
                <arch>amd64</arch>
            </os>
        </activation>
        <properties>
            <native.platform>linux-amd64</native.platform>
        </properties>
    </profile>
    <profile>
        <id>native-linux-aarch64</id>
        <activation>
            <os>
                <name>linux</name>
                <arch>aarch64</arch>
            </os>
        </activation>
        <properties>
            <native.platform>linux-arm64</native.platform>
        </properties>
    </profile>
    <profile>
        <id>native-darwin-x86_64</id>
        <activation>
            <os>
                <name>mac os x</name>
                <arch>x86_64</arch>
            </os>
        </activation>
        <properties>
            <native.platform>darwin-amd64</native.platform>
        </properties>
    </profile>
    <profile>
        <id>native-darwin-aarch64</id>
        <activation>
            <os>
                <name>mac os x</name>
                <arch>aarch64</arch>
            </os>
        </activation>
        <properties>
            <native.platform>darwin-arm64</native.platform>
        </properties>
    </profile>
    <profile>
        <id>native-windows-amd64</id>
        <activation>
            <os>
                <name>windows</name>
                <arch>amd64</arch>
            </os>
        </activation>
        <properties>
            <native.platform>windows-amd64</native.platform>
        </properties>
    </profile>
    <profile>
        <id>native</id>
        <build>
            <pluginManagement>
                <plugins>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jar-plugin</artifactId>
                        <configuration>
                            <archive>
                                <manifestEntries>
                                    <Spring-Boot-Native-Processed>true</Spring-Boot-Native-Processed>
                                </manifestEntries>
                            </archive>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <mainClass>org.apache.seata.server.ServerApplication</mainClass>
                        </configuration>
                        <executions>
                            <execution>
                                <id>process-aot</id>
                                <goals>
                                    <goal>process-aot</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.graalvm.buildtools</groupId>
                        <artifactId>native-maven-plugin</artifactId>
                        <configuration>
                            <mainClass>org.apache.seata.server.ServerApplication</mainClass>
                            <classesDirectory>${project.build.outputDirectory}</classesDirectory>
                            <imageName>${project.artifactId}-${project.version}-${native.platform}</imageName>
                            <metadataRepository>
                                <enabled>true</enabled>
                            </metadataRepository>
                            <buildArgs>
                                <arg>--enable-url-protocols=http</arg>
                                <arg>-H:+ReportExceptionStackTraces</arg>
                            </buildArgs>
                            <exclusions>
                                <exclusion>
                                    <groupId>org.springframework.boot</groupId>
                                    <artifactId>spring-boot-devtools</artifactId>
                                </exclusion>
                            </exclusions>
                        </configuration>
                        <executions>
                            <execution>
                                <id>add-reachability-metadata</id>
                                <goals>
                                    <goal>add-reachability-metadata</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </pluginManagement>
        </build>
    </profile>
    <profile>
        <id>nativeTest</id>
        <dependencies>
            <dependency>
                <groupId>org.junit.platform</groupId>
                <artifactId>junit-platform-launcher</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <pluginManagement>
                <plugins>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <mainClass>org.apache.seata.server.ServerApplication</mainClass>
                        </configuration>
                        <executions>
                            <execution>
                                <id>process-test-aot</id>
                                <goals>
                                    <goal>process-test-aot</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.graalvm.buildtools</groupId>
                        <artifactId>native-maven-plugin</artifactId>
                        <configuration>
                            <classesDirectory>${project.build.outputDirectory}</classesDirectory>
                            <mainClass>org.apache.seata.server.ServerApplication</mainClass>
                            <imageName>${project.artifactId}-${project.version}-${native.platform}</imageName>
                            <metadataRepository>
                                <enabled>true</enabled>
                            </metadataRepository>
                            <buildArgs>
                                <arg>--enable-url-protocols=http</arg>
                                <arg>-H:+ReportExceptionStackTraces</arg>
                            </buildArgs>
                            <classesDirectory>${project.build.outputDirectory}</classesDirectory>
                            <exclusions>
                                <exclusion>
                                    <groupId>org.springframework.boot</groupId>
                                    <artifactId>spring-boot-devtools</artifactId>
                                </exclusion>
                            </exclusions>
                        </configuration>
                        <executions>
                            <execution>
                                <id>native-test</id>
                                <goals>
                                    <goal>test</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </pluginManagement>
        </build>
    </profile>
</profiles>

Users would then build the native image with:

mvn clean package -Pnative -pl server

And run it directly without a JDK:

./seata-server/target/seata-server

Other related information

Compatibility Considerations

  1. Reflection-heavy libraries that Seata depends on may need special handling in native mode:

    • Netty (used for RPC communication): Netty has existing GraalVM metadata. Spring Boot 4 should handle this via its reachability metadata repository.
    • Kryo / other serializers: Serialization frameworks often rely on reflection. AOT hints or @RegisterReflectionForBinding may be required.
    • Dynamic proxies (e.g., java.lang.reflect.Proxy): Seata's configuration binding and interceptor chains may use proxies.
    • JCommander: Command-line argument parsing may need reflection config.
  2. Profile-guided optimization (PGO) can be added later to further improve native image performance.

  3. Not all features need to work in native mode initially — a phased approach is acceptable:

    • Phase 1: Basic server startup, configuration loading, and core transaction coordination in native mode.
    • Phase 2: Full feature parity with JVM mode (all serializers, all store modes, all discovery modes).

Related Links

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions