Skip to content

Refactor LocalExecutorService and enhance testing coverage - #967

Open
qianmoQ wants to merge 17 commits into
devlive-community:devfrom
qianmoQ:2026.0.0
Open

Refactor LocalExecutorService and enhance testing coverage#967
qianmoQ wants to merge 17 commits into
devlive-community:devfrom
qianmoQ:2026.0.0

Conversation

@qianmoQ

@qianmoQ qianmoQ commented Sep 4, 2026

Copy link
Copy Markdown
Member

Changelog category (leave one)

  • New Feature
  • Bug Fix
  • Documentation (changelog entry is not required)
  • Other

Changelog entry (Details of this change)

If there is an issue connection, write it to the end of the question
e.g: issue-7
Please delete this information when submitting

  • e.g: Support xxxx

Affected version

  • e.g: latest version

Pin current behaviour of LocalExecutorService before refactoring:
streaming/legacy paths, pre-count, three cancel routes, NULL handling
and column projection. Backed by a network-free FakePluginService so
the suite runs without a real database.

Two assertions intentionally capture known quirks (JDBC-abort cancel
reporting count=0, and STOPPED state after all rows committed) to be
fixed in a later phase.
Extract the 576-line class into focused units without behaviour change:
- SyncStrategy + SyncContext with StreamingSyncStrategy / LegacySyncStrategy
- TaskRegistry (object) for the running-task map and cancel executor,
  kept a singleton so stop() still works across service instances
- TaskHandle / TaskCancelledException and ValueCodec for value/SQL codecs

start() now only resolves inputs, runs pre-count and picks a strategy;
stop() delegates to TaskRegistry. Characterization suite stays green and
spotbugs reports no findings.
Unify cancellation behind a single CancellationToken and a runCancelable
writer wrapper, collapsing the three former detection points (in-loop
throw, post-loop flag, driver-abort exception) into one path that always
surfaces TaskCancelledException with the committed count.

Previously a stop that aborted the JDBC fetch reported count=0 because it
read the stale rowsAtStop counter (updated only every 1000 rows); it now
reports the rows actually committed to the target. Row counts are unified
on writtenCount() across success and cancellation.

Also precompute source-column indexes at onSchema instead of a per-row
map lookup in the streaming path.
Add a per-executor timeout (seconds) to the local executor. A watchdog
cancels the running sync once the limit is exceeded, surfacing it as
RunState.TIMEOUT with the committed row count via the shared
CancellationToken (USER stop vs TIMEOUT).

The timeout defaults to 0 (disabled) in LocalExecutor's config, so
existing syncs keep running to completion. DataSetServiceImpl now reads
the value from each executor's effective config; executors without the
field (Seatunnel) still fall back to 600s. TIMEOUT is treated as a
terminal state like STOPPED so it is not overwritten to FAILURE.
Every plugin is reached through the datacap JDBC-conversion driver
(type() defaults to JDBC, supportsStreaming()==true) and the sync target
is always the JDBC dataset store, so the both-ends-non-streaming branch
that hand-built INSERT strings was unreachable in practice.

Remove that branch along with ValueCodec.formatSqlLiteral /
escapeSqlString (manual quoting was an injection and type-coercion
hazard, and carried a stray NUL byte in a comment). The legacy path now
requires a batch-write-capable target and fails fast with a clear message
otherwise; the streaming path is unaffected.
Drive LocalExecutorService against a real PostgreSQL container (source and
target) through a real-JDBC PluginService, verifying an actual source->target
sync: streaming copy with column projection/rename, NULL and numeric handling,
pre-count totals, and a real Statement.cancel() stop.

Pin the docker-java API version (1.41) in-code so Testcontainers negotiates
with newer Docker Desktop engines, and skip gracefully when Docker is absent.
Add spotbugs suppressions on the test helpers for false positives (Kotlin
use{} obligations, list casts, test data exposure).
Extract SeaTunnelCommander construction from SeatunnelExecutorService.start()
into a package-visible buildCommander(request) (no behaviour change) and add
tests asserting the resulting command line for the Spark and SeaTunnel engines.

This locks the ExecutorRequest-to-command mapping so the upcoming ExecutorRequest
restructure can be verified against a stable expected command.
Collapse the executor-specific typed fields (executorHome, startScript,
runWay, runMode, runEngine, fetchSize, batchSize, preCount) into a single
options string map, leaving only executor-agnostic fields on ExecutorRequest
(taskName, userName, input, output, workHome, timeout, transform,
progressListener, pluginManager). Reduce the six telescoping constructors to
the primary plus one workHome convenience constructor.

Each executor now reads what it needs from options: the local executor takes
fetchSize/batchSize/preCount, seatunnel takes home/startScript/way/mode/engine.
Callers pass the executor's effective config map straight through as options.
Adding a new executor no longer requires touching this shared class.

Behaviour is preserved: local characterization + PostgreSQL E2E tests stay
green, and the seatunnel command-mapping test asserts the same command line.
Introduce ant-design-vue 4 and wire it globally next to the existing
view-shadcn-ui so components can be migrated page by page without breaking
untouched pages. Add composables for cross-cutting concerns:
- useTheme: single-source dark/light switch (persisted, toggles the html
  `dark` class and drives antd's dark/default algorithm)
- useAntdLocale: maps the vue-i18n locale to antd's built-in locale

App.vue now wraps the router view in a-config-provider (theme + locale).
No reset.css yet to avoid affecting not-yet-migrated pages. Pin pnpm
onlyBuiltDependencies to keep pnpm 10 CI from failing on ignored builds.
Replace ShadcnSelect with a-select (grouped options) and drop the
view-shadcn-ui setLocale call; antd's built-in strings now follow the
vue-i18n locale through a-config-provider. loadLocale already updates the
global locale, so switching language reactively updates both app and antd
text.
Replace ShadcnException with a-result (status 404/403/500, title/sub-title,
extra + icon slots) across NotFound, NotAuthorized and NotNetwork. Swap
ShadcnButton to a-button and its `to` navigation to router pushes on click;
NotNetwork's custom icon now uses antd's WarningOutlined.
Rebuild the sign-in form on antd: a-form (:model + @finish/@finishFailed)
with a-form-item rules, a-input / a-input-password, submit via
html-type=submit, and a-card / a-avatar / a-divider / a-space / a-spin.
Global $Message calls become antd message; validation-error reporting adapts
to antd's errorFields shape. Field rules and behaviour are unchanged.
Mirror the sign-in migration for sign-up: antd a-form with a-input /
a-input-password, submit via html-type=submit, a-card / a-avatar / a-divider
/ a-space / a-spin, and antd message. The confirm-password custom validator
now uses antd's (rule, value) signature; rules and behaviour unchanged.
First list-page migration proving the hard patterns:
- ShadcnTable -> a-table with #bodyCell slot (role tags, action buttons);
  useHeaders now emits antd columns (title/dataIndex/key)
- ShadcnPagination -> a-pagination (v-model:current, show-size-changer,
  change / show-size-change events)
- ShadcnSpin -> a-spin wrapper (:spinning); a-card / a-tag / a-tooltip /
  a-button / a-space

Also convert the page from Options API to <script setup> composition style
(refs + functions + onMounted). Icons keep ShadcnIcon for now (migrated in a
later dedicated pass). UserRole / UserInfo dialogs untouched (coexistence).
Convert NotFound / NotAuthorized / NotNetwork from Options API to
<script setup> (defineOptions for name), completing their composition-style
refactor alongside the earlier ant-design-vue swap. Templates unchanged.
Convert AuthSignin / AuthSignup from Options API to <script setup>
(refs + functions, useI18n for translations, defineOptions for name),
finishing the composition-style refactor of the already-migrated pages.
Templates and behaviour unchanged.
…n API

Proves the modal pattern: ShadcnModal -> a-modal (v-model:open, footer=null
with the form's own submit button); ShadcnCheckboxGroup/Checkbox ->
a-checkbox-group / a-checkbox; a-spin wrapper. Convert to <script setup>
(defineProps/withDefaults, defineEmits, computed visible get/set, useI18n,
antd message). Behaviour and validation unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant