Skip to content

Refactor LocalExecutorService and enhance testing coverage - #967

Merged
qianmoQ merged 72 commits into
devlive-community:devfrom
qianmoQ:2026.0.0
Sep 19, 2026
Merged

qianmoQ merged 72 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.
…position

Migrate the user create/edit flow: ShadcnDrawer -> a-drawer (UserInfo) and
the inner ShadcnForm -> a-form with a-input / a-input-password + submit
(UserForm). Both converted to <script setup> (defineProps/withDefaults,
defineEmits, computed visible, useI18n, antd message); the confirm-password
validator uses antd's (rule, value) signature. Behaviour unchanged.
Migrate the whole system/role module: RoleHome list (a-table #bodyCell +
a-pagination), RoleInfo dialog (a-modal + a-form with a-input / a-textarea),
and RoleMenu assign-menu dialog (a-modal + a-tree checkable with field-names
mapping value/label to key/title). RoleUtils emits antd columns. All pages
converted to <script setup> composition with useI18n and antd message.
Migrate system/menu: MenuHome list (adds a-switch in the active column via
#bodyCell) and MenuInfo drawer form using a-row/a-col grid, a-input,
a-input-number, a-select (type/parent/redirect), a-switch and a-textarea.
MenuUtils emits antd columns; both pages are <script setup> with useI18n and
antd message. Grid spans doubled for antd's 24-column system.
…osition

Migrate ConfigureHome (custom two-pane layout kept): a-card / a-spin,
a-input / a-input-number / a-switch / a-input-password for the dynamic
field editor, a-button. Convert to <script setup> composition with useI18n
and antd message; behaviour unchanged.
… API

Migrate system/function: FunctionHome list (a-table with a-avatar-group in
the plugin column, a-tag, a-pagination), FunctionInfo drawer form (a-select
multiple via :options, a-textarea), and FunctionImport drawer with a-tabs
(content vs import-from-url), a-alert warnings and danger button. FunctionUtils
emits antd columns. All <script setup> composition with useI18n / antd message.
antd a-pagination renders show-total content before the pager, so the raw
total count showed up as a leading number. Remove :show-total from the user,
role and menu list pages; the page-size selector already conveys volume.
antd Select dropdown items (page-size changer, all a-select) sat flush
against each other, so the hovered and selected options touched. Add a small
margin + rounded corners to .ant-select-item globally for clearer separation.
Migrate admin/snippet: SnippetHome list adds the last common components -
a-dropdown (#overlay a-menu/a-menu-item) for the row action menu and
router-link (hash-router aware, target=_blank) for the quote link, plus
a-avatar and a-pagination. SnippetInfo drawer form (a-input / a-textarea /
AceEditor) and SnippetDelete confirm dialog (a-modal + a-alert list + name
match validator). All <script setup> with useI18n / antd message.
Introduce a generic DataTable (components/table/DataTable.vue) that lays out
the shared list-page structure: an optional filter slot on top, a toolbar
with left page-action slot and built-in right tools (refresh, column
show/hide popover, CSV download), then the antd table and pagination.
Columns, data, paging and loading are props; refresh / page-change /
size-change / download are events; #bodyCell passes through.

Refactor UserHome to consume it as the reference usage (create button in
#actions, cells via #bodyCell), removing its hand-written table/pagination.
Replace the hand-written a-table + a-pagination + toolbar in RoleHome,
MenuHome, FunctionHome and SnippetHome with the shared DataTable component.
Page create/import actions move into #actions; cell rendering stays in
#bodyCell; refresh / paging are wired to the component's events. Consolidates
the list-page structure and gives every list the built-in refresh, column
toggle and CSV download tools for free.
Move the DataTable toolbar/filter/pagination layout from Tailwind utility
classes (provided by view-shadcn-ui, which will be removed) to scoped CSS,
so the component renders consistently now and survives the library removal.
Add scroll.x=max-content so table columns size to their content instead of
being stretched equally across wide viewports.
Migrate admin/history: HistoryHome list on DataTable (state a-tag with
success/error color, plugin/format a-avatar, mode a-tag, a-dropdown row menu
with router-link) and HistoryData dialog (a-modal + footer cancel wrapping
VisualTable). Both <script setup> with useI18n / antd message. HistoryUtils
emits antd columns.
Migrate admin/report on DataTable: ReportHome list (a-switch realtime,
a-avatar/a-tag source, a-dropdown row menu with a disabled modify item and
router-link), ReportView (a-modal + VisualView) and ReportDelete confirm
(a-modal + a-alert list + name match validator). All <script setup> with
useI18n / antd message; ReportUtils emits antd columns.
- replace remaining Shadcn* components (icon, logger, contribution,
  workflow editor/view, data builder, code editor) with local antd-based
  implementations under src/views/components
- reference @ant-design/icons-vue components directly at every call
  site and drop the DcIcon wrapper; backend menu icon names keep a
  small name-to-component map in LayoutHeader
- vendor the tailwind utilities used by templates into
  src/assets/utilities.local.css and restore antd icon baseline
  alignment broken by the preflight svg rule
- convert table toolbar and row action icon buttons to text style
- replace the monaco based shadcn code editor in query page with the
  shared AceEditor plus metadata suggest auto completion
- drop the router loading bar and legacy setLocale calls
- pin typescript to 5.4.5 for vue-tsc 1.8 compatibility
The ant layout renders a gray background behind the transparent header
row, which showed as gray bands on both sides on wide screens
- rebuild profile sidebar as a card with section header, per-item icons
  and a bottom promo panel
- add page header with description and slogan, a profile hero card with
  avatar upload (inline cropper) and role/status badges
- show basic info fields, a language preference selector and four
  account overview tiles (role, status, register time with day count,
  last login with ip from login logs)
- add matching i18n keys for en and zh-cn
The ml-20 utility never existed in the vendored styles so the content
column sat flush against the sidebar; use the available ml-6 spacing
and render the breadcrumb like the prototype
Apply the soft green wave artwork as the fixed page backdrop, offset
below the header so its curves show through the content margins
The default gray ant-layout background painted over the body backdrop
image, hiding it on every page including the user settings
- add DataCap wordmark next to the logo
- render horizontal menu items as rounded pills with tinted selected
  and hover states instead of the antd underline style
- highlight the active menu by longest path-prefix match so child
  pages mark their parent menu
- add a Help label beside the feedback icon and show the username with
  a chevron in the account dropdown trigger
- introduce --dc-* css variables from design/ui-color-system.md covering
  brand scale, backgrounds, text hierarchy, borders, status colors,
  radius, shadows and gradients, with dark-mode variable overrides
- expose brand palette and antd theme tokens through the useTheme
  composable (colorPrimary green, transparent layout background) and a
  brand registry so user preferences can switch themes later
- migrate header, profile sidebar and profile page styles to consume
  tokens; add hero gradient band and info/success status tags
- replace the body backdrop image with css radial-gradient ambience per
  the design document
- align log viewer level colors and contribution heat scale with tokens
The image url is exposed as --dc-body-image so user preferences can
switch it later; dark theme falls back to the gradient ambience
Menu items were stretched and used oversized 18px icons; pin items to
their natural width with tight padding and 16px icons
The ci installed the latest pnpm while the lockfile was still in the
v6 format produced by pnpm 8, breaking pnpm install with
ERR_PNPM_BROKEN_LOCKFILE. Upgrade the lockfile to v9 and pin the ci
pnpm to 9.13.2, matching the frontend-maven-plugin version; declare
packageManager for corepack users
antd derives colorPrimaryBg from the seed color and produced a muted
sage for the green brand, which leaked into selected menu items; pin
colorPrimaryBg/BgHover/Border/BorderHover to the documented values for
light and dark
The header bar collapsed to the menu height with no vertical padding;
give it a 64px height with centered content. The 1400px centered
container cap also fought the prototype proportions; switch to a
1520px cap with 40px side padding (roughly full-width like the mock)
Add the documented per-state colors: neutral item text, hover surface,
selected mint background with darker green text and the 3px brand bar
on the leading edge; expose menu hover/text tokens for dark mode
@qianmoQ
qianmoQ merged commit 534ba4a into devlive-community:dev Sep 19, 2026
5 checks passed
@qianmoQ
qianmoQ deleted the 2026.0.0 branch September 19, 2026 04:38
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