# Apache StreamPark — Agent Instructions Project conventions, architecture, and coding patterns for the StreamPark codebase. ## Architecture ### Module Boundaries StreamPark is a Maven multi-module project with five top-level reactor modules. Each has a clear responsibility boundary. - **`streampark-common`** (`streampark-common/`): Engine-API-free foundation layer. It owns the immutable configuration model (`ConfigOption`, `Configuration`, `ConfigurationLoader`, and `GlobalConfiguration`), shared option catalogs, workspace layout, utilities, file system abstractions, and common enums. It may understand external formats such as Flink YAML, but must not depend on Flink or Spark runtime APIs. - **`streampark-scala`** (`streampark-scala/`): Small Scala compatibility layer containing shared Scala logging and implicit utilities. Java-only common code belongs in `streampark-common`; reusable Scala code belongs here instead of being embedded in an engine module. - **`streampark-flink`** (`streampark-flink/`): Flink runtime integration. Its reactor contains the version-isolated shims, SQL client, submission client, packer, and Kubernetes integration. Connector modules are enabled by the module's `shaded` profile. `FlinkShimsProxy` in the client API is the entry point for executing version-specific code. - **`streampark-spark`** (`streampark-spark/`): Spark runtime integration. It contains Spark configuration and SQL utilities, submission client API/core, and SQL client. It is a regular root reactor module; the retained `spark` profile is not required to include it in a normal build. - **`streampark-console`** (`streampark-console/`): Web management platform. Its Maven reactor contains the Spring Boot service. The sibling `streampark-console-webapp/` Vue 3 application is built into the service only when the service's `webapp` profile is enabled; it is not a Maven child module. The strongest compatibility contracts are the common configuration keys and value semantics, Flink client request/response types, shims proxy serialization boundary, database schema, and REST API. Changes to these surfaces require cross-module impact analysis and focused compatibility tests. ### High-Sensitivity Areas - **Configuration model** (`org.apache.streampark.common.configuration`): `ConfigOption` declares typed metadata, `ConfigurationParser` parses raw documents, `ConfigurationLoader` composes ordered sources, and immutable `Configuration` snapshots perform typed reads. `GlobalConfiguration` is the process-boundary atomic reference, not a mutable property bag. Option keys and fallback keys are user-facing contracts. - **Configuration ownership**: Common option catalogs contain only settings owned by common infrastructure. Console and engine settings stay in their owning modules. `SpringConfigurationInitializer` is the Console composition root and owns the explicit list of options bound from Spring; do not add global `ALL` registries to option catalogs. - **Workspace initialization**: `Workspace.LOCAL`, `Workspace.REMOTE`, and derived constants are initialized from one immutable snapshot. Spring bootstrap must publish its completed configuration before workspace paths are first accessed, and must retain the `Workspace.verifyInitializedFrom` guard. - **Flink YAML compatibility** (`FlinkConfigurationLoader`): Before Flink 1.19, load only `flink-conf.yaml` with the legacy line parser. Flink 1.19 and 1.20 support both names and prefer `flink-conf.yaml` when both exist; parser selection follows the selected filename. Flink 2.0 and later load only `config.yaml` with standard nested YAML parsing. Directory-based loading must always receive the target Flink version. This common utility returns engine-neutral maps; conversion to Flink's `Configuration` belongs in a Flink module. - **`FlinkShimsProxy`**: The multi-version classloader isolation mechanism uses `ChildFirstClassLoader` and serializes request/response objects across the boundary. Classloaders are cached by concrete Flink version. Never pass target-runtime Flink objects into the parent classloader or introduce target-specific static state that can leak across classloaders. - **Shims modules**: `streampark-flink-shims-base` contains only contracts and implementation shared by every supported shim. Supported concrete modules are Flink 1.18, 1.19, 1.20, 2.0, 2.1, 2.2, and 2.3. APIs removed from Flink 2.x, such as legacy `registerDataStream` and table-function `registerFunction` overloads, belong only in the compatible 1.x implementations. Do not add classes or methods anywhere under `streampark-flink-shims` without an explicit shims architecture decision. - **Flink submission flow**: `FlinkClient` and its request/response packages form the stable API. `FlinkClientEntrypoint`, `SubmitRequestResolver`, `FlinkConfigurationBuilder`, and deployment-specific clients own request normalization, configuration assembly, and submission. Keep Flink-version calls behind the shims boundary. - **`FlinkApplicationController` / `FlinkApplicationManageService` / `FlinkApplicationActionService`**: The core application management flow. Operations (start, stop, cancel, deploy) must be idempotent and handle all Flink states correctly. The `AppChangeEvent` annotation triggers state synchronization. - **Persistence entities** (`console/core/entity`): These classes map database rows. Keep configuration discovery, parsing, filesystem access, and other business logic in assemblers, services, or utility classes such as `FlinkEnvUtils` and `FlinkApplicationConfigUtils`. Existing small mapping operations may remain, but new domain workflows must not be added to entities. - **SQL parsing and validation**: Flink SQL validation is version-aware and executes through the selected 1.18-2.3 shim. `FlinkSql`, `FlinkSqlService`, and the SQL client have different persistence, orchestration, and execution responsibilities. `sql-rev.dict` handles database SQL differences between MySQL and PostgreSQL; it is unrelated to Flink SQL syntax compatibility. - **Kubernetes integration** (`FlinkKubernetesWatchController`): Uses Caffeine caches (`TrackIdCache`, `JobStatusCache`, `MetricCache`) for tracking K8s-deployed Flink jobs. Cache invalidation and TTL must be correct to avoid stale state. - **HTTP client and proxying**: `OkHttpUtils` in common owns the shared connection pool, timeouts, and bounded retries for idempotent requests. Console proxy code in `WebUtils` owns servlet adaptation, hop-by-hop header filtering, response streaming, and response closure. Do not create ad hoc clients per request or retry non-idempotent methods implicitly. - **Database schema changes**: All schema changes must have corresponding upgrade scripts under `streampark-console/streampark-console-service/src/main/assembly/script/upgrade/` for both `mysql/` and `pgsql/`. Update `sql-rev.dict` when a mapper or initialization statement needs a MySQL-to-PostgreSQL rewrite. - **Authentication & Authorization**: `ShiroConfig`, `JWTUtil`, `ShiroRealm` — changes here affect all user access. The `@Permission` annotation and `PermissionAspect` enforce team-level resource isolation. Never weaken RBAC checks. ## Design Patterns - **Immutable configuration pipeline**: Declare typed options in the owning module, parse external input without a central registry, compose sources by precedence, and capture one immutable snapshot at the start of a multi-step operation. Do not repeatedly read mutable global state inside a workflow. - **Shims / Proxy pattern**: `FlinkShimsProxy.proxy(flinkVersion, function)` loads the target installation and matching shim behind a child-first classloader. Shared behavior stays in shims-base; version-only API calls stay in the concrete version module. Cross-boundary values must use stable serializable StreamPark request/response types. - **Initializer pattern**: `FlinkStreamInitializer` and `FlinkTableInitializer` assemble application configuration namespaces and construct Flink environments inside the target classloader. Native `flink.property.*`, user `app.*`, and command-line sources remain distinct until their documented composition point. - **Service layer separation**: Console services are split by responsibility — `FlinkApplicationManageService` (CRUD), `FlinkApplicationActionService` (start/stop/cancel), `FlinkApplicationInfoService` (query/info). Follow this pattern when adding new application operations. - **Scala compatibility layer**: Shared Scala helpers live in `streampark-scala`. New implicits must be narrowly scoped and must not introduce engine dependencies into the common foundation. - **MyBatis-Plus entity pattern**: Mappers extend MyBatis-Plus `BaseMapper`; entities use `@TableName` and related mapping annotations. Only entities that need the shared audit fields extend `BaseEntity`. Pagination uses `MybatisPager` and `PaginationInterceptor`. Keep entities focused on persistence data. - **Typed request and option APIs**: Prefer typed enums, request objects, `ConfigOption`, and immutable maps over unstructured string bags. Preserve serialized field names across the Console-to-client and client-to-shims boundaries. - **REST response pattern**: Normal API endpoints return `RestResponseBody` created through `RestResponseBody.success(...)` or `RestResponseBody.fail(...)`. The map-based `RestResponse` is a deprecated compatibility type and must not be introduced at new controller boundaries. Streaming proxy endpoints may write directly to `HttpServletResponse`. Use `@Permission` for protected resources and `@AppChangeEvent` for audited application changes. - **File system abstraction**: Use `FsOperator` (with `HdfsOperator` / `LfsOperator` implementations) for file operations. Never use raw `java.io.File` or Hadoop `FileSystem` directly in business logic. ## Coding Conventions ### Java (Backend) - **Formatting**: Eclipse formatter via Spotless (`tools/checkstyle/spotless_streampark_formatter.xml`). Run `./mvnw spotless:apply` before committing. - **Import order**: `org.apache.streampark`, `org.apache.streampark.shaded`, `org.apache`, `javax`, `java`, `scala`, `\#` (all others). - **Static checks**: Checkstyle (`tools/checkstyle/checkstyle.xml`) + Spotless. No wildcard imports. No `@author` tags. No JUnit 4 imports. - **Lombok**: Use `@Slf4j`, `@Data`, `@Builder` where appropriate. Do not use `@EqualsAndHashCode` on JPA/Hibernate entities. - **Testing**: JUnit 5 (`org.junit.jupiter`) + AssertJ. Use `@Test` (not `@Test` from JUnit 4). Use `assertThat(...).isEqualTo(...)` style. Test classes should be in the same package as the code under test. - **Method names**: New production and test method names must be concise, readable, and no longer than 40 characters. - **Package structure**: Controllers in `controller/`, service interfaces in `service/`, implementations in `service/impl/`, entities in `entity/`, mappers in `mapper/`, enums in `enums/`. ### Scala (Scala Support / Spark) - **Formatting**: Scalafmt 3.7.5 (`tools/checkstyle/.scalafmt.conf`). Max column 160. Run `./mvnw spotless:apply` to format. - **Import ordering**: `org.apache.streampark.*` first, then other third-party, then `javax.*`, `java.*`, `scala.*`. - **Static checks**: Scalastyle (`tools/checkstyle/scalastyle-config.xml`). No wildcard imports. No `println` statements (use `Logger` trait). - **Testing**: ScalaTest 3.2.9. Use `FlatSpec` or `FunSuite` style consistent with existing tests. - **Style**: Use `val` over `var`. Prefer `Option` over `null` in public APIs. Use `lazy val` for expensive initialization. Use pattern matching instead of `isInstanceOf`/`asInstanceOf`. ### TypeScript / Vue (Frontend) - **Formatting**: ESLint + Prettier. Run `pnpm lint:eslint` and `pnpm lint:prettier`. - **Vue 3 Composition API**: Use `