Skip to content

feat(connectors): add OpenDAL sink - #4123

Open
George-Miao wants to merge 2 commits into
apache:masterfrom
George-Miao:master
Open

George-Miao wants to merge 2 commits into
apache:masterfrom
George-Miao:master

Conversation

@George-Miao

@George-Miao George-Miao commented Sep 10, 2026

Copy link
Copy Markdown

Which issue does this PR address?

Closes #4071

Rationale

OpenDAL provides an uniformed interface for writing to various services, which is useful for iggy to support multiple new sink types with a very small amount of change & maintainance effort.

What changed?

Added opendal-sink crate that implemnets a sink adapter for OpenDAL. I intentionally didn't touch the other part, e.g., CI. Those can be done in another follow-up PR.

Local Execution

  • Passed
  • Pre-commit hooks ran

AI Usage

Uses codex & omp for reviewing the changes & write some part of the doc.

@github-actions

Copy link
Copy Markdown

Thanks for the PR. It is labeled S-waiting-on-review and queued for review.

Slash commands (own line, regular comment) move it around the queue:

  • /ready - back to S-waiting-on-review after addressing feedback
  • /author - flip to S-waiting-on-author while you finish changes
  • /request-review @user-or-team - request a reviewer
  • /pin - exempt the PR from the stale bot, /unpin to undo

See CONTRIBUTING.md for details.

@github-actions github-actions Bot added the S-waiting-on-review PR is waiting on a reviewer label Sep 10, 2026
@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.75969% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.06%. Comparing base (d693eb9) to head (fa60b24).

Files with missing lines Patch % Lines
core/connectors/sinks/opendal_sink/src/lib.rs 89.36% 16 Missing and 4 partials ⚠️
core/connectors/sinks/opendal_sink/src/path.rs 87.14% 8 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #4123      +/-   ##
============================================
+ Coverage     86.03%   86.06%   +0.02%     
  Complexity     1449     1449              
============================================
  Files          1248     1250       +2     
  Lines        195841   196099     +258     
  Branches     161139   161397     +258     
============================================
+ Hits         168501   168776     +275     
+ Misses        23206    23164      -42     
- Partials       4134     4159      +25     
Components Coverage Δ
Rust Core 86.98% <88.75%> (+0.02%) ⬆️
Java SDK 67.54% <ø> (ø)
C# SDK 77.06% <ø> (+0.05%) ⬆️
Python SDK 91.33% <ø> (ø)
PHP SDK 85.65% <ø> (ø)
Node SDK 96.23% <ø> (-0.02%) ⬇️
Go SDK 69.43% <ø> (+0.03%) ⬆️
Files with missing lines Coverage Δ
core/connectors/sinks/opendal_sink/src/path.rs 87.14% <87.14%> (ø)
core/connectors/sinks/opendal_sink/src/lib.rs 89.36% <89.36%> (ø)

... and 44 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

));
}

opendal::init_default_registry();

@ryankert01 ryankert01 Sep 13, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

install_default calles init_default_registry() then install transport, so s3 would not fail. Maybe consider to add a test against minio container.

Suggested change
opendal::init_default_registry();
opendal::install_default();

@mattp5657 mattp5657 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.

2 things:

  1. This connector is missing from the release/version-bump tooling. Absent from scripts/bump-version.sh, the connector_plugins default in _build_rust_artifacts.yml, and the connector list in edge-release.yml. It'll pass CI but won't ship in edge or tagged release artifacts, and a future version bump will silently skip it. Not unique to this PR (a handful of existing sinks are missing from these same lists too), but worth fixing here or in an immediate follow-up rather than adding a fourth thing to that pile.

  2. I think we could use more unit and integration test coverage, happy and unhappy paths for every function. Untested today: path.rs's render_template and sanitize_path_segment beyond one edge case, lib.rs's consume()/write_message() unhappy path, the retry policy past one attempt, and S3/GCS/Azure end-to-end (fs is the only backend any test exercises)

Ok(())
}

async fn consume(

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.

Should these be batched instead of one operator.write() per message?
s3_sink batches and flushes a combined object per batch, this sink
issues a separate write per message, so at the default batch_length = 100 that's 100 S3 PUTs instead of 1. Worth aligning with s3_sink's
approach for cost/throughput at scale?

#[serde(default = "default_path_template")]
pub path_template: String,
#[serde(default)]
pub options: BTreeMap<String, String>,

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.

Credentials (S3 access keys, etc.) flow
through options: BTreeMap<String, String> instead of SecretString,
diverging from this codebase's own stated convention for
credential-bearing config. A hand-rolled Debug impl redacts values in
one specific logging path but gives no type-level guarantee anywhere
else.

let seconds = (micros / 1_000_000) as i64;
let nanoseconds = ((micros % 1_000_000) * 1_000) as u32;
DateTime::<Utc>::from_timestamp(seconds, nanoseconds).ok_or_else(|| {
Error::CannotStoreData(format!(

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.

Should an out-of-range timestamp return Error::InvalidRecordValue
instead of Error::CannotStoreData?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review PR is waiting on a reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement OpenDAL connector

3 participants