Skip to content

[fix](routineload) Persist the current load definition - #66634

Open
0AyanamiRei wants to merge 13 commits into
apache:masterfrom
0AyanamiRei:feature/routine-load-definition-persistence
Open

[fix](routineload) Persist the current load definition#66634
0AyanamiRei wants to merge 13 commits into
apache:masterfrom
0AyanamiRei:feature/routine-load-definition-persistence

Conversation

@0AyanamiRei

@0AyanamiRei 0AyanamiRei commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: N/A

Related PR: #64878

Problem Summary:

Routine Load historically persists the original CREATE statement in origStmt and reparses it when an image is loaded. However, ALTER ROUTINE LOAD changes the effective load definition stored in RoutineLoadJob without changing the original CREATE statement. After ALTER followed by follower replay, checkpoint, or FE restart, the job could therefore recover the original CREATE semantics instead of the definition currently used to create tasks.

This PR treats the existing RoutineLoadJob runtime fields as the authoritative effective state and fixes both persistence paths:

  • Image/checkpoint: persist partitions, column mappings, preceding/where/delete filters, separators, sequence column, merge type, execution memory limit, and memtable_on_sink_node directly on RoutineLoadJob.
  • ALTER journal: persist a nullable RoutineLoadDesc delta in AlterRoutineLoadJobOperationLog, apply it under the job write lock on the leader, and apply properties and the delta in the same order during follower replay.

The design deliberately does not introduce a LoadDefinition wrapper or duplicate job/data-source snapshots:

  • The fields consumed by Kafka/Kinesis task construction are the image source of truth.
  • jobProperties remains authoritative for cached properties such as max filter ratio, batch parallelism, single-tablet loading, partial-update mode/policy, and CSV enclose/escape/empty_field_as_null.
  • New-format images restore direct state without creating a SQL context or accessing the catalog.
  • origStmt remains persisted for downgrade readability and legacy image migration; ALTER does not rewrite it.
  • Old images are identified by the absence of the newly persisted merge type. The new FE reparses origStmt once, restores load clauses and exec_mem_limit, and the next checkpoint writes the direct-state format.
  • Legacy memtable_on_sink_node cannot be reconstructed and retains its historical post-restart value of false.

The ALTER journal stores only clauses changed by that ALTER. Nullable RoutineLoadDesc fields preserve clauses not modified by the delta. Old logs do not contain the new field and therefore leave the current load definition unchanged during replay.

CSV ALTER properties are validated only on the leader before mutation. Replay trusts persisted journal values so validation added by a newer FE cannot block a journal written by an older FE. Runtime CSV caches are updated together with jobProperties and rebuilt from that map after image recovery.

Expr serialization dependency

Routine Load directly persists Expr objects from column mappings, preceding filters, where filters, and delete conditions. These expressions are converted back to SQL before each Nereids task is built, so every field that affects ExprToSqlVisitor output must survive Gson round-trip.

This PR hardens the shared legacy Expr serialization contract:

  • Adds stable serialization for SQL-relevant state in PlaceHolderExpr, TimeV2Literal, MatchPredicate, SearchPredicate, SlotRef, and VariableExpr.
  • Persists FunctionCallExpr ORDER BY metadata and nested OrderByElement state.
  • Requires all concrete Expr subtypes to be present in both Gson registries and in a non-default sample set.
  • Reflectively requires every Expr instance field to have @SerializedName or be explicitly classified as non-durable derived state.
  • Verifies that Gson round-trip preserves both ExprToSqlVisitor(WITH_TABLE) and ExprToSqlVisitor(WITHOUT_TABLE) output for every registered concrete subtype.
  • Adds analysis/AGENTS.md review guidance so future Expr changes account for metadata persistence.

Compatibility boundary:

  • Old image -> new FE: supported through one-time origStmt migration. ALTER clauses that were never recorded by the old image or old journal are not recoverable; the original CREATE semantics are restored.
  • New image/log -> old FE: unknown JSON fields are structurally ignorable and origStmt is still available, but semantic downgrade is not guaranteed. An old FE may restore the original CREATE clauses and ignore new ALTER deltas.
  • Avoid ALTERing Routine Load load clauses until every FE has been upgraded. During a mixed-version rolling upgrade, an old follower can ignore the new journal delta, while a new follower cannot reconstruct a delta omitted by an old leader.
  • Rolling back after a new-version checkpoint can lose post-upgrade ALTER semantics.

Out of scope:

  • Target-table ALTER.
  • Full failed-ALTER/edit-log-write atomicity refactoring.
  • Pause/cancel reason persistence, automatic-recovery backoff, state-transition timestamps, and unrelated Routine Load lifecycle issues.

Tests added or extended:

  • Complete direct-state image round-trip with invalid origStmt, non-default task configuration, all load clauses, and SQL-sensitive Expr values.
  • Empty load-definition recovery proving new images do not fall back to SQL parsing.
  • Fixed image and ALTER-log fixtures generated by the real serializer at merge base a8928245.
  • Kafka/Kinesis durable-versus-derived state recovery.
  • ALTER journal serialization for every load-clause category and complex nested Expr values.
  • Kafka/Kinesis leader-to-replay delta application, CSV cache synchronization, legacy journal replay, and checkpoint parity.
  • Full concrete Expr subtype coverage, field-classification enforcement, and SQL-semantic Gson round-trip.
  • A three-FE Docker case covering follower journal replay before checkpoint, leader failover, a subsequent ALTER on the new leader, checkpoint creation, and FE restart recovery.

Validation status for the final head:

  • git diff --check: passed.
  • FE compile, testCompile, and checkstyle: passed.
  • Focused FE unit tests: 36 passed, 0 failures, 0 errors.
  • The three-FE Docker regression case was added but has not been executed locally.

Release note

Routine Load jobs now preserve the effective load definition after ALTER across follower replay, leader failover, checkpoint, and FE restart.

During a rolling upgrade, do not ALTER Routine Load load clauses until all FEs are running the new version. Rolling back after a new-version checkpoint can restore the original CREATE clauses instead of post-upgrade ALTER semantics.

Check List (For Author)

  • Test

    • Regression test
      • Added a three-FE Routine Load journal replay, leader failover, checkpoint, and restart case.
      • Not executed locally.
    • Unit Test
      • 36 focused FE tests passed.
      • FE compile, testCompile, and checkstyle passed.
    • Manual test
    • No need to test or manual test
  • Behavior changed:

    • No.
    • Yes. New images and ALTER journals retain the current effective Routine Load definition; legacy images migrate once from origStmt.
  • Does this need documentation?

    • No.
    • Yes. Rolling-upgrade and rollback limitations must be documented.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#64878

Problem Summary: Routine Load image recovery reparsed the immutable original CREATE statement, so CREATE semantics changed by ALTER were not represented in the image. Persist the current load definition, retain the original statement as the legacy-image fallback, journal altered load clauses, and validate failure-prone Kafka and Kinesis changes before mutating runtime state.

### Release note

Routine Load jobs now recover the current effective load definition after ALTER.

### Check List (For Author)

- Test: Unit Test
    - ./run-fe-ut.sh --run org.apache.doris.load.routineload.KafkaRoutineLoadJobTest,org.apache.doris.load.routineload.KinesisRoutineLoadJobTest,org.apache.doris.persist.AlterRoutineLoadOperationLogTest
- Behavior changed: Yes. Image recovery uses the current effective Routine Load definition while legacy images continue to use origStmt.
- Does this need documentation: No
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#64878

Problem Summary: Keep the original CREATE statement in new images so an older FE can ignore the new load definition field and use its existing recovery path. ALTERed load clauses are outside the downgrade compatibility guarantee and may not survive rollback.

### Release note

Document that Routine Load ALTER semantics are not guaranteed after rolling back to an older FE.

### Check List (For Author)

- Test: Not run (per request; compatibility coverage was added)
- Behavior changed: No. This records and tests the intended rollback compatibility boundary.
- Does this need documentation: Yes. The rollback limitation must be called out in the feature documentation.
@0AyanamiRei
0AyanamiRei marked this pull request as draft August 11, 2026 03:16
### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#64878

Problem Summary: Routine Load now persists its effective load definition directly, but nested legacy Expr SQL-carrier fields also need stable serialization, legacy image/log fixtures must come from the merge base, and ALTER replay must not apply new leader-side CSV validation to historical journals. Complete those compatibility requirements and add three-FE failover coverage for leader journal write, follower replay, checkpoint, and restart recovery.

### Release note

Routine Load jobs preserve effective load clauses across ALTER, follower replay, checkpoint, and FE restart. Legacy images continue to migrate from the original CREATE statement; rollback to an older FE remains structurally readable but does not preserve new ALTER semantics.

### Check List (For Author)

- Test: Not run per requested handoff; FE unit and Docker regression coverage were added.
- Behavior changed: Yes. Routine Load persistence and ALTER replay retain the current effective definition.
- Does this need documentation: Yes. Document rolling-upgrade and rollback limitations.
@0AyanamiRei
0AyanamiRei marked this pull request as ready for review August 20, 2026 02:02
### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#64878

Problem Summary: Directly persisting legacy Expr makes Routine Load metadata depend on complete Gson coverage for a broad and evolving expression graph. Keep origStmt as the durable load-definition carrier instead. ALTER logs persist the original ALTER SQL, leader and follower replay merge each delta into a canonical CREATE statement, and image recovery reparses that current effective statement.

### Release note

Routine Load ALTER clauses are persisted by updating the stored CREATE statement and replaying ALTER SQL, avoiding a durable dependency on legacy Expr Gson serialization.

### Check List (For Author)

- Test: Unit Test
    - RoutineLoadJobPersistenceTest and KafkaRoutineLoadJobTest: 16 tests passed.
    - AlterRoutineLoadOperationLogTest and KinesisRoutineLoadJobTest: 13 tests passed in the preceding targeted run.
- Behavior changed: Yes. Routine Load load clauses use canonical origin SQL for image and journal recovery.
- Does this need documentation: Yes. Document ALTER recovery and rolling-upgrade compatibility.
### What problem does this PR solve?

Issue Number: close apache#66633

Related PR: apache#66634

Problem Summary: Routine Load cannot safely use the legacy Expr object graph as an image or journal compatibility surface. Keep the current effective load definition in origStmt, remove the duplicate execMemLimit JSON source, and cover all SQL-representable load clauses through CREATE image restore, ALTER merge, and a second image restore.

### Release note

Routine Load now persists ALTERed load clauses in the effective origin SQL used during FE recovery.

### Check List (For Author)

- Test: Unit Test
    - ./run-fe-ut.sh --run org.apache.doris.load.routineload.RoutineLoadJobPersistenceTest
    - ./run-fe-ut.sh --run org.apache.doris.load.routineload.KafkaRoutineLoadJobTest,org.apache.doris.load.routineload.KinesisRoutineLoadJobTest,org.apache.doris.persist.AlterRoutineLoadOperationLogTest
    - Docker regression case added but not run locally
- Behavior changed: Yes, ALTERed Routine Load definitions survive journal replay and image recovery
- Does this need documentation: Yes, the existing design document and PR description must be updated
### What problem does this PR solve?

Issue Number: close apache#66633

Related PR: apache#66634

Problem Summary: Routine Load images already persist and replay origStmt. The persistence bug is that ALTER load clauses changed runtime fields without updating that statement. Keep the existing gsonPostProcess recovery path unchanged, persist the original ALTER SQL in the journal, and rewrite origStmt to a complete effective CREATE statement after leader and follower ALTER application. Remove direct-field persistence, cache hydration, CSV validation, and other adjacent changes from this PR.

### Release note

Routine Load now preserves ALTERed load clauses across follower replay, checkpoints, and FE restart by maintaining the effective CREATE statement.

### Check List (For Author)

- Test: Unit Test
    - ./run-fe-ut.sh --run org.apache.doris.load.routineload.RoutineLoadJobPersistenceTest
    - ./run-fe-ut.sh --run org.apache.doris.load.routineload.KinesisRoutineLoadJobTest
    - KafkaRoutineLoadJobTest and AlterRoutineLoadOperationLogTest passed in the combined targeted run
    - Docker regression case added but not run locally
- Behavior changed: Yes, ALTERed load clauses update the persisted origin statement
- Does this need documentation: Yes, document mixed-version ALTER limitations
### What problem does this PR solve?

Issue Number: close apache#66633

Related PR: apache#66634

Problem Summary: Treat the effective Routine Load fields as authoritative metadata instead of rewriting origStmt after ALTER. Persist the load-definition fields directly in images, persist RoutineLoadDesc deltas in ALTER journals, and use the original CREATE statement only when reading legacy images whose nullable effective fields are absent. Empty new definitions may also use the fallback safely because ALTER cannot unset all load clauses.

### Release note

Routine Load now preserves ALTERed load clauses across journal replay and FE restart through direct metadata persistence.

### Check List (For Author)

- Test: Unit Test
    - ./run-fe-ut.sh --run org.apache.doris.load.routineload.RoutineLoadJobPersistenceTest,org.apache.doris.load.routineload.KafkaRoutineLoadJobTest,org.apache.doris.load.routineload.KinesisRoutineLoadJobTest,org.apache.doris.persist.AlterRoutineLoadOperationLogTest
- Behavior changed: Yes, image and ALTER journal persist effective Routine Load definitions directly
- Does this need documentation: Yes, document mixed-version ALTER limitations
### What problem does this PR solve?

Issue Number: close apache#66633

Related PR: apache#66634

Problem Summary: Metadata consumers now persist legacy Expr objects directly, but the existing Expr Gson test only checked subtype and JSON idempotence. Add stable serialization for SQL-relevant fields that were silently dropped, persist function ORDER BY metadata, require every Expr instance field to be serialized or explicitly classified as non-durable, and verify SQL output with and without table names for every concrete registered subtype. Add an analysis review guide for future Expr changes.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - ./run-fe-ut.sh --run org.apache.doris.analysis.ExprGsonSerializationTest
    - ./run-fe-ut.sh --run org.apache.doris.analysis.ExprGsonSerializationTest,org.apache.doris.load.routineload.RoutineLoadJobPersistenceTest,org.apache.doris.persist.AlterRoutineLoadOperationLogTest
- Behavior changed: No user-facing SQL behavior; metadata Expr round trips now preserve SQL semantics
- Does this need documentation: No, contributor guidance is included in analysis/AGENTS.md
### What problem does this PR solve?

Issue Number: close apache#66633

Related PR: apache#66634

Problem Summary: The first restoration of direct Routine Load persistence omitted parts of the previously reviewed design. Restore the exact persistence implementation from commit 4394fa3, including execMemLimit and memtableOnSinkNode image fields, jobProperties cache hydration, CSV ALTER cache synchronization, leader-only validation, legacy image migration, and the original Kafka/Kinesis persistence tests. Keep the separate Expr serde hardening on top.

### Release note

Routine Load persists its effective load definition and non-default task configuration directly across FE recovery.

### Check List (For Author)

- Test: Unit Test
    - ./run-fe-ut.sh --run org.apache.doris.analysis.ExprGsonSerializationTest,org.apache.doris.load.routineload.RoutineLoadJobPersistenceTest,org.apache.doris.load.routineload.KafkaRoutineLoadJobTest,org.apache.doris.load.routineload.KinesisRoutineLoadJobTest,org.apache.doris.persist.AlterRoutineLoadOperationLogTest
- Behavior changed: Yes, restore the complete direct-state image and ALTER journal persistence contract
- Does this need documentation: Yes, document mixed-version ALTER limitations
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.

2 participants