Skip to content

feat(clp-tdl-package): Implement the clp-s compression commit task. - #2415

Merged
LinZhihao-723 merged 24 commits into
y-scope:mainfrom
LinZhihao-723:commit-task-impl
Jul 28, 2026
Merged

feat(clp-tdl-package): Implement the clp-s compression commit task.#2415
LinZhihao-723 merged 24 commits into
y-scope:mainfrom
LinZhihao-723:commit-task-impl

Conversation

@LinZhihao-723

@LinZhihao-723 LinZhihao-723 commented Jul 22, 2026

Copy link
Copy Markdown
Member

Description

This PR depends on #2411.

This PR fills in the commit task registered by clp-tdl-package: once every compression task of a job has succeeded, it publishes the produced archives to CLP's metadata DB and marks the job succeeded. This is the worker behind the compression::commit TDL task; with it, the compress + commit pair is complete.

Commit flow

commit runs the whole finalization in a single DB transaction, mirroring the Python commit path:

  • Looks up the CLP compression job by reverse-lookup on spider_id, selecting it FOR UPDATE.
  • Idempotently registers the dataset (a None dataset resolves to the clp-s default) in the datasets table.
  • Inserts every archive's metadata into <prefix><dataset>_archives.
  • CAS-transitions the job from RUNNING to SUCCEEDED, recording total uncompressed/compressed sizes and a duration derived from the DB clock, then commits.

The transition is a compare-and-set: the job must be RUNNING. A job already SUCCEEDED is a no-op (so a retried commit is safe), and any other state is refused. Because everything runs in one transaction, a failure at any step (e.g. an archive insert) rolls back the dataset registration too, leaving no partial state.

Wrapper

The commit_task wrapper deserializes the msgpack CompressionTaskOutputs from the task-graph outputs, validates that every output belongs to the same dataset (a job spans exactly one dataset), flattens their archives, and drives commit.

Status enum encoding

CompressionJobStatus gains #[derive(sqlx::Type)] (over its existing #[repr(i32)]), so it binds and decodes directly as the integer status column rather than through a hand-rolled conversion.

Observability

Following the task's logging decision, the async commit never logs errors — it returns them with context — while the (non-async) commit_task wrapper logs a failure once, with the full error chain. commit emits info lines for start, completion, and the already-committed no-op.

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

  • Ensure all workflows pass.
  • Exercised commit and the commit_task wrapper (outside Spider) against a local MariaDB, with a JSON tracing subscriber capturing logs. Each scenario asserts the return value, the DB side effects, and the logging; the failure cases additionally confirm that the async worker logs no error (only the wrapper does):
Scenario Input Output Logging DB side effects
Happy path RUNNING job, Some("default"), 3 archives Ok started + completed successfully info lines job → SUCCEEDED with total sizes and a non-null duration; datasets row written; 3 archive rows (creator_id='', creation_ix=0)
Default dataset RUNNING job, dataset = None Ok started + completed successfully None resolves to default; datasets row name=default; archives in <prefix>default_archives
Idempotent re-commit job already SUCCEEDED Ok, no-op started + already committed info lines none — no duplicate rows, job untouched
Wrong state job PENDING/FAILED Err (refuses to commit) started info only; no error line none; job unchanged
No job for spider_id no matching row Err started info only; no error line none
Transaction atomicity RUNNING job, archives table absent Err (archive insert fails) started info only rolled back — the earlier datasets insert is gone; job stays RUNNING
DB unreachable port pointed at a closed port Err (pool creation) started info only; no error line none
Missing DB credentials CLP_DB_USER/CLP_DB_PASS unset Err started info only; no error line none (bails before the pool)
Empty archives archives = [] Err started info only none (bails before any DB call)
Invalid dataset name Some("bad name!") Err started info only none (bails before any DB call)
Multi-dataset rejection (wrapper) two outputs with different datasets Err (more than one dataset) wrapper started only none — rejected before commit
Multi-output aggregation (wrapper) several outputs, same dataset, RUNNING job Ok wrapper + worker started, completed successfully every output's archives flattened and inserted; job SUCCEEDED
Non-commit context (wrapper) a non-commit task with no outputs Err (commit must run as a commit task) wrapper started only none
Logging split a worker failure driven through commit_task Err wrapper emits CLP commit task failed. with the full error chain, while the same failure called on commit directly emits no error line none

Summary by CodeRabbit

  • New Features

    • Added compression tasks for processing log data from S3, including support for structured and unstructured logs.
    • Added automatic archive creation, upload, and metadata registration.
    • Added optional dataset support with dataset-specific archive storage.
    • Added commit processing to record completed compression jobs and archive details.
    • Added default staging and temporary storage locations for task execution.
  • Bug Fixes

    • Improved handling of repeated or already-completed compression jobs.
    • Added validation for dataset names and compression task outputs.

The initial and maximum job-state poll backoffs were fixed constants inside the
Spider implementation, so a caller had no way to tune how aggressively a job is
polled. Both are now `Duration` parameters of
`run_s3_compression_job_to_completion`, leaving the pacing decision with the
caller that knows how long its jobs typically run.
…asks need:

* Add `ArchiveOutputStorage` and the archive output's `storage` and `retention_period` fields.
* Add `Database`'s fixed `table_prefix`.
* Fill in `SpiderTaskExecutorConfig` and resolve its relative paths against `CLP_HOME`.
* Add `resolve_dataset_name` and the default dataset name.
* Add `generate_s3_url`.
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

CLP compression package

Layer / File(s) Summary
Configuration and shared contracts
components/clp-rust-utils/src/clp_config/package/config.rs, components/clp-rust-utils/src/job_config/compression.rs
Adds executor path resolution, dataset archive storage and table-name helpers, S3 staging defaults, deserialization tests, and sqlx::Type for compression job status.
Package bootstrap and task wiring
components/clp-tdl-package/Cargo.toml, components/clp-tdl-package/src/task/compression/mod.rs
Adds the crate manifest and Spider task wrappers for compression and commit operations.
S3 compression worker
components/clp-tdl-package/src/task/compression/compress.rs
Stages structured or unstructured inputs, runs clp-s, uploads archives to S3, indexes them, cleans temporary paths, and validates helper arguments and output parsing.
Compression commit transaction
components/clp-tdl-package/src/task/compression/commit.rs
Validates outputs and datasets, registers dataset metadata, inserts archive records in batches, and conditionally marks the compression job succeeded.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

  • y-scope/clp#2401 — Provides the compression task I/O types consumed by this worker.
  • y-scope/clp#2402 — Builds task graphs that invoke the compression and commit tasks.
  • y-scope/clp#2404 — Introduces the shared executor configuration evolved by this change.

Suggested reviewers: jackluo923

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding the clp-s compression commit task in clp-tdl-package.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sitaowang1998 sitaowang1998 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious. How do we handle empty table if the commit fail right after table creation?

.into_iter()
.flat_map(|output| output.archives)
.collect();
crate::common::runtime()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we allow this import style?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry which import style?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the impression that we do not allow crate:: and should use import instead.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm I didn't explicitly add such rules in the standard.

for archives in archives.chunks(1000) {
let mut builder = sqlx::QueryBuilder::<sqlx::MySql>::new(format!(
"INSERT INTO `{archives_table}` (id, begin_timestamp, end_timestamp, \
uncompressed_size, size, creator_id, creation_ix);"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uncompressed_size, size, creator_id, creation_ix);"
uncompressed_size, size, creator_id, creation_ix)"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to remove it, but the formatter will append an empty space after ).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it should be a space. My bad. The query builder will append values into this query.

@LinZhihao-723

Copy link
Copy Markdown
Member Author

Just curious. How do we handle empty table if the commit fail right after table creation?

  • To clairfy: The table is created in compression coordinator.
  • The desired behavior for now is: if the compression job fails, we just leave the table empty there. The proper solution is like what Kirk mentioned today: we should enforce the dataset creation as a separate step executed before compression jobs.

@LinZhihao-723
LinZhihao-723 marked this pull request as ready for review July 23, 2026 01:12
@LinZhihao-723
LinZhihao-723 requested a review from a team as a code owner July 23, 2026 01:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/clp-rust-utils/src/s3/url.rs`:
- Around line 18-26: Update the URL construction around endpoint_url and
region_code to percent-encode bucket and object_key path components before
interpolation, preserving any slash separators within object_key while encoding
reserved characters such as spaces, #, ?, %, and newlines. Apply the same
encoding for both endpoint and AWS-hosted URL branches, and add coverage for
these reserved-character cases.

In `@components/clp-rust-utils/src/task_io/compression.rs`:
- Line 14: Update the task payload struct containing the unstructured field to
apply serde’s defaulting behavior, so omitted values deserialize as false while
explicit values remain unchanged. Add a deserialization test for a payload that
lacks unstructured and assert it produces false.

In `@components/clp-tdl-package/src/task/compression/commit.rs`:
- Around line 67-69: Update the commit flow around create_clp_db_mysql_pool so
the MySQL pool is initialized once and reused across commit() invocations, using
an appropriate process-wide OnceCell or Lazy consistent with the cached
spider_task_executor_config pattern. Preserve the existing configuration,
credentials, pool size, and error context while avoiding per-commit pool
creation.
- Around line 187-208: Optionally make mark_job_succeeded’s UPDATE explicitly
guard the state transition by adding an AND status = ? predicate and binding the
expected in-progress status. Preserve the existing success updates and ensure
the query remains compatible with the current MySQL bind order.

In `@components/clp-tdl-package/src/task/compression/compress.rs`:
- Around line 782-799: Update the stdout line-reading loop in the compression
flow so errors from BufRead::lines are routed through the same child kill, wait,
stderr_reader join, logging, and contextual error cleanup used when
parse_archive_stats or on_archive fails. Ensure a failed line read never returns
directly while clp-s remains running or the stderr reader remains unjoined.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2d496d62-fcca-48fb-9f78-b656a9fbe614

📥 Commits

Reviewing files that changed from the base of the PR and between d1248be and d119cbd.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • Cargo.toml
  • components/clp-rust-utils/src/clp_config/package/config.rs
  • components/clp-rust-utils/src/dataset.rs
  • components/clp-rust-utils/src/job_config/compression.rs
  • components/clp-rust-utils/src/s3.rs
  • components/clp-rust-utils/src/s3/url.rs
  • components/clp-rust-utils/src/task_io/compression.rs
  • components/clp-tdl-package/Cargo.toml
  • components/clp-tdl-package/src/common.rs
  • components/clp-tdl-package/src/lib.rs
  • components/clp-tdl-package/src/task/compression/commit.rs
  • components/clp-tdl-package/src/task/compression/compress.rs
  • components/clp-tdl-package/src/task/compression/mod.rs
  • components/clp-tdl-package/src/task/mod.rs
  • taskfile.yaml

Comment thread components/clp-rust-utils/src/s3/url.rs Outdated
Comment thread components/clp-rust-utils/src/task_io/compression.rs
Comment thread components/clp-tdl-package/src/task/compression/commit.rs
Comment thread components/clp-tdl-package/src/task/compression/commit.rs
Comment thread components/clp-tdl-package/src/task/compression/compress.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/clp-tdl-package/src/task/compression/commit.rs`:
- Around line 195-199: Remove the trailing comma after the duration expression
in the SQL string used by the query call, so the SET clause transitions directly
to WHERE while preserving the existing parameter order and conditions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0304dd4e-b979-44b2-9636-8f1f8ab44953

📥 Commits

Reviewing files that changed from the base of the PR and between d119cbd and 3746f16.

📒 Files selected for processing (1)
  • components/clp-tdl-package/src/task/compression/commit.rs

Comment on lines +195 to +199
let query_result = sqlx::query(
"UPDATE compression_jobs SET status = ?, uncompressed_size = ?, compressed_size = ?, \
duration = TIMESTAMPDIFF(MICROSECOND, start_time, CURRENT_TIMESTAMP(3)) / 1000000, WHERE \
id = ? AND status = ?",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove the trailing comma before WHERE.

duration = ... / 1000000, WHERE is invalid MySQL syntax, so every commit transaction fails when marking the job succeeded.

Proposed fix
-         duration = TIMESTAMPDIFF(MICROSECOND, start_time, CURRENT_TIMESTAMP(3)) / 1000000, WHERE \
+         duration = TIMESTAMPDIFF(MICROSECOND, start_time, CURRENT_TIMESTAMP(3)) / 1000000 WHERE \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let query_result = sqlx::query(
"UPDATE compression_jobs SET status = ?, uncompressed_size = ?, compressed_size = ?, \
duration = TIMESTAMPDIFF(MICROSECOND, start_time, CURRENT_TIMESTAMP(3)) / 1000000, WHERE \
id = ? AND status = ?",
)
let query_result = sqlx::query(
"UPDATE compression_jobs SET status = ?, uncompressed_size = ?, compressed_size = ?, \
duration = TIMESTAMPDIFF(MICROSECOND, start_time, CURRENT_TIMESTAMP(3)) / 1000000 WHERE \
id = ? AND status = ?",
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/clp-tdl-package/src/task/compression/commit.rs` around lines 195 -
199, Remove the trailing comma after the duration expression in the SQL string
used by the query call, so the SET clause transitions directly to WHERE while
preserving the existing parameter order and conditions.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
components/clp-tdl-package/src/task/compression/compress.rs (2)

856-1119: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider a regression test for the run_clp_s cleanup path.

The tests cover pure helper functions (build_s3_logs_list, parse_archive_stats, argument builders, etc.), but none exercise run_clp_s's kill/reap behaviour on a stdout read failure — the exact path just fixed for the previously-flagged critical issue. A test that points clp_s_bin at a small script producing malformed stdout (or exiting non-zero) would guard against this regressing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/clp-tdl-package/src/task/compression/compress.rs` around lines 856
- 1119, Extend the tests around run_clp_s with a regression test that forces
stdout reading to fail, using a temporary executable or script configured
through clp_s_bin to emit malformed output or exit non-zero. Assert the
operation returns an error and still completes child-process cleanup by killing
and reaping the process, covering the cleanup path without changing the existing
helper tests.

246-286: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Run the indexer only after the S3 upload succeeds.

ArchiveFinisher::finish currently uses tokio::join! to run run_indexer alongside upload_file_to_s3. run_indexer directly passes MySQL --db-host/--db-name arguments to indexer, which persists column metadata during update_metadata; this write is not rolled back if the upload subsequently fails. Sequence the indexing after a successful upload, or otherwise make the DB metadata write part of the archive-commit transaction.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/clp-tdl-package/src/task/compression/compress.rs` around lines 246
- 286, Update ArchiveFinisher::finish so upload_file_to_s3 completes
successfully before starting run_indexer; remove the concurrent tokio::join!
flow while preserving the existing upload and indexer error handling and
logging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@components/clp-tdl-package/src/task/compression/compress.rs`:
- Around line 856-1119: Extend the tests around run_clp_s with a regression test
that forces stdout reading to fail, using a temporary executable or script
configured through clp_s_bin to emit malformed output or exit non-zero. Assert
the operation returns an error and still completes child-process cleanup by
killing and reaping the process, covering the cleanup path without changing the
existing helper tests.
- Around line 246-286: Update ArchiveFinisher::finish so upload_file_to_s3
completes successfully before starting run_indexer; remove the concurrent
tokio::join! flow while preserving the existing upload and indexer error
handling and logging.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dce5a681-6ddb-4b72-8345-774b2e491c89

📥 Commits

Reviewing files that changed from the base of the PR and between 3746f16 and 2fc9707.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • components/clp-tdl-package/src/task/compression/compress.rs

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