Skip to content

Gate lz4-sys behind an optional lz4-compression feature#397

Open
humancto wants to merge 3 commits intotikv:masterfrom
humancto:feat/feature-gate-lz4-sys
Open

Gate lz4-sys behind an optional lz4-compression feature#397
humancto wants to merge 3 commits intotikv:masterfrom
humancto:feat/feature-gate-lz4-sys

Conversation

@humancto
Copy link
Copy Markdown

@humancto humancto commented Apr 24, 2026

Summary

Make the lz4-sys C FFI crate optional behind a new lz4-compression Cargo feature (default-on). Consumers who build with default-features = false and without lz4-compression get a pure-Rust build graph with lz4-sys fully absent; existing consumers on defaults see zero behavior change.

Motivation

Projects with a strict pure-Rust policy (no C dependencies in the build graph) currently cannot consume raft-engine at all because lz4-sys = "=1.9.5" is unconditional. Making the C dep opt-in-by-default removes that adoption blocker without affecting anyone who stays on defaults.

Changes

  • Cargo.toml: lz4-sys marked optional = true; new feature lz4-compression = ["dep:lz4-sys"]; default = ["internals", "scripting", "lz4-compression"] preserves existing BC.
  • src/util.rs: pub mod lz4 split into two #[cfg]-gated variants with identical public surface (DEFAULT_LZ4_COMPRESSION_LEVEL, append_compress_block, decompress_block). Real implementation behind #[cfg(feature = "lz4-compression")]. Stub under #[cfg(not(...))] returns Error::Other with a message naming the feature flag. Call sites in src/log_batch.rs compile unchanged under both arms.
  • src/config.rs: Config::sanitize rejects batch_compression_threshold > 0 when the feature is off, matching the existing reject patterns for enable_log_recycle/prefill_for_recycle. Rustdoc on batch_compression_threshold spells out the constraint. New unit test covers both reject and accept paths.
  • CHANGELOG.md + README.md: entry under [Unreleased] and a line in the "Available Cargo features" list.
  • .github/workflows/rust.yml: new cargo build --no-default-features --verbose step in the stable job to regression-guard the feature gate.

Operator contract

Callers who disable the feature:

  • MUST set batch-compression-threshold = 0 in Config (enforced by sanitize).
  • CANNOT decode log files written by a build that had LZ4 compression active — those entries surface Error::Other on read, naming the feature flag.

Test plan

  • cargo check --lib passes (0 errors).
  • cargo check --lib --no-default-features passes (0 errors).
  • cargo tree --no-default-features --edges no-dev | grep lz4 → empty.
  • cargo tree --edges no-dev | grep lz4lz4-sys v1.9.5 still present (defaults unchanged).
  • Build verification covered in CI by the new --no-default-features step.
  • Adversarial review by an outside Rust reviewer: two rounds (REVISE → APPROVE_WITH_NITS). Both rounds' feedback folded in.

Pre-existing warning flagged in review

The new CI step will surface a pre-existing warning: Patch cc v1.0.98 ... was not used in the crate graph. This is the [patch.crates-io] cc = { ... tag = "1.0.98" } pin added for rust-lang/cc-rs#984, which no longer matches the resolved cc 1.2.61 in the lockfile. The warning is orthogonal to this change and predates it — left as a follow-up since the original #984 workaround should be re-evaluated on its own merits rather than bundled into a feature-gate patch.

Background

Part of the humancto/mango project's adoption of raft-engine as its WAL. Tracking the fork's retirement against this upstream PR's merge.

Summary by CodeRabbit

  • New Features

    • Added an optional, default-enabled LZ4 compression feature that toggles native compression; builds without it remain usable but will error if compression is requested or compressed data is encountered.
  • Documentation

    • Updated README and CHANGELOG with feature behavior, compatibility notes, and required configuration when disabling compression.
  • Chores

    • CI now runs an additional build-and-test step with default features disabled.
  • Tests

    • Added tests validating configuration rejection when compression is disabled.

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Apr 24, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign sunxiaoguang for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added the contribution This PR is from a community contributor. label Apr 24, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Apr 24, 2026

Hi @humancto. Thanks for your PR.

I'm waiting for a tikv member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@ti-chi-bot ti-chi-bot Bot added needs-ok-to-test Indicates a PR created by contributors and need ORG member send '/ok-to-test' to start testing. first-time-contributor Indicates that the PR was contributed by an external member and is a first-time contributor. labels Apr 24, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Apr 24, 2026

Welcome @humancto!

It looks like this is your first PR to tikv/raft-engine 🎉.

I'm the bot to help you request reviewers, add labels and more, See available commands.

We want to make sure your contribution gets all the attention it needs!



Thank you, and welcome to tikv/raft-engine. 😃

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

📝 Walkthrough

Walkthrough

Adds an optional, default-on Cargo feature lz4-compression that gates the lz4-sys C FFI dependency; provides a runtime stub lz4 module and shared compression constant when the feature is disabled; enforces batch_compression_threshold = 0 in that configuration and adds a CI step to build/test with --no-default-features.

Changes

LZ4 Feature and Runtime API

Layer / File(s) Summary
Cargo Feature / Manifest
Cargo.toml
Adds lz4-compression = ["dep:lz4-sys"], marks lz4-sys optional, and includes lz4-compression in the crate default features.
Documentation
README.md, CHANGELOG.md
Documents the new lz4-compression feature, its default-on status, and compatibility requirements when disabled (must set batch-compression-threshold = 0).
Shared Constant
src/util.rs
Adds pub const DEFAULT_LZ4_COMPRESSION_LEVEL: usize = 1 and re-exports it from the feature-enabled lz4 module.
Enabled LZ4 Module
src/util.rs (feature = "lz4-compression")
lz4 module now re-exports the shared default constant instead of defining its own.
Disabled LZ4 Stub Module
src/util.rs (not(feature = "lz4-compression"))
Adds a stub lz4 module exposing the same public API (append_compress_block, decompress_block, and the default constant) but returning Error::Other for compression or when encountering LZ4-compressed data.
Configuration Validation
src/config.rs
Adds #[cfg(not(feature = "lz4-compression"))] check in Config::sanitize that errors on non-zero batch_compression_threshold; updates docs and adds a unit test under the same cfg.

CI: No-default-features Build/Test

Layer / File(s) Summary
Workflow Step
.github/workflows/rust.yml
In the stable job, inserts a step after Clippy that runs cargo check --no-default-features --all-targets --verbose and cargo test --no-default-features --verbose.
Existing CI / Coverage
.github/workflows/rust.yml
Existing Run tests (make test) and the coverage job are unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A carrot-sized compression tweak,
Optional kernels hide the leak,
Stubs keep signatures standing tall,
Pure-Rust builds answer the call.
Hop, test, and ship without a stall!

🚥 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 pull request title clearly and concisely summarizes the main change: making lz4-sys an optional dependency behind a feature flag.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@ti-chi-bot ti-chi-bot Bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. dco-signoff: no Indicates the PR's author has not signed dco. labels Apr 24, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Apr 24, 2026

Thanks for your pull request. Before we can look at it, you'll need to add a 'DCO signoff' to your commits.

📝 Please follow instructions in the contributing guide to update your commits with the DCO

Full details of the Developer Certificate of Origin can be found at developercertificate.org.

The list of commits missing DCO signoff:

  • 4e8a088 feat(features): make lz4-sys optional behind lz4-compression feature
  • e1d738d fix(lz4-feature): address rust-expert REVISE findings
Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
.github/workflows/rust.yml (1)

41-42: No-default-features step doesn't exercise the new gated test.

This step only runs cargo build on the default target, so the #[cfg(not(feature = "lz4-compression"))] test added in src/config.rs (test_sanitize_rejects_nonzero_threshold_without_lz4_feature) is never compiled or executed anywhere in CI — it's excluded from the default-feature make test run and skipped here. Also, cargo build won't compile test/bench/example targets, so regressions in stub-related code paths under feature-off can slip through.

Consider upgrading the step to cargo check --no-default-features --all-targets and/or cargo test --no-default-features so the sanitize-rejection test and any feature-off test code actually run.

🔧 Suggested change
-      - name: Build (no default features)
-        run: cargo build --no-default-features --verbose
+      - name: Build and test (no default features)
+        run: |
+          cargo check --no-default-features --all-targets --verbose
+          cargo test --no-default-features --verbose
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/rust.yml around lines 41 - 42, The "Build (no default
features)" CI step currently runs only `cargo build --no-default-features
--verbose` so tests gated by the lz4 feature (e.g., the #[cfg(not(feature =
"lz4-compression"))] test
test_sanitize_rejects_nonzero_threshold_without_lz4_feature in src/config.rs)
are never compiled or run; update that step to run a command that compiles
and/or runs tests for all targets without default features such as `cargo check
--no-default-features --all-targets` and/or `cargo test --no-default-features`
so the gated test and any non-default-feature test code are exercised in CI.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.github/workflows/rust.yml:
- Around line 41-42: The "Build (no default features)" CI step currently runs
only `cargo build --no-default-features --verbose` so tests gated by the lz4
feature (e.g., the #[cfg(not(feature = "lz4-compression"))] test
test_sanitize_rejects_nonzero_threshold_without_lz4_feature in src/config.rs)
are never compiled or run; update that step to run a command that compiles
and/or runs tests for all targets without default features such as `cargo check
--no-default-features --all-targets` and/or `cargo test --no-default-features`
so the gated test and any non-default-feature test code are exercised in CI.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0bbd4ff3-2505-4eec-a480-3a1e0e448cb8

📥 Commits

Reviewing files that changed from the base of the PR and between f0fcebe and e1d738d.

📒 Files selected for processing (6)
  • .github/workflows/rust.yml
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • src/config.rs
  • src/util.rs

humancto added a commit to humancto/mango that referenced this pull request Apr 24, 2026
* chore(adr): record raft-engine fork for lz4-sys feature-gate (W5)

ADR 0002 §W5 previously assumed upstream raft-engine's lz4-sys C FFI
dependency could be mitigated purely at the data path - by disabling
raft-engine's own compression and doing it above the engine in
mango-raft using lz4_flex. That remains true for the data path, but
the build-graph concern - upstream's unconditional lz4-sys dependency
pulling the C toolchain into mango's supply chain - was unsolved by
the data-path mitigation alone.

This commit records the build-time fix:

1. ADR 0002 §W5 expanded to document the active fork
   (humancto/raft-engine at SHA e1d738d), the upstream PR that tracks
   its retirement (tikv/raft-engine#397), and the explicit consumer
   contract (default-features = false, feature list, and the
   Config::sanitize reject enforcing batch-compression-threshold = 0).

2. New file .planning/fork-raft-engine-lz4-verification.md records
   the full fork state: SHA pins, PR URL, commit footprint, how
   mango consumes the fork, rebase policy while the fork lives,
   retirement plan when #397 merges, and supply-chain audit posture.

The fork itself was adversarially reviewed by rust-expert across two
rounds (REVISE -> APPROVE_WITH_NITS). Upstream PR #397 is open.
No mango Cargo.toml changes here - the workspace dep entry lands
with PR-1 (mango-storage skeleton) which pins the fork SHA.

* docs(fork-tracking): address rust-expert APPROVE_WITH_NITS must-fix items

rust-expert's review of PR #48 returned APPROVE_WITH_NITS with two
must-fix items on .planning/fork-raft-engine-lz4-verification.md.
Both are text-only and localized; applying them here to close out the
review without another round.

1. Retirement branches for non-merge endings.
   Original text assumed upstream PR #397 merges. Added a new
   "What if upstream rejects the approach or stalls" section with two
   branches:
   (a) #397 closed without merge, upstream proposes a different shape -
       swap the tracked PR, follow the same 6-step retirement once the
       replacement merges.
   (b) #397 stalls for 12+ months - escalate to ADR 0002 §W4 escape
       hatch (b) and vendor the pinned fork SHA under
       crates/vendored-raft-engine/.

2. Supply-chain audit posture corrected.
   Original paragraph conflated cargo-vet's exemption-identity behavior
   with SHA pinning and review-by annotations. Rewritten to spell out
   the actual semantics:
   - Exemptions key on (crate_name, crate_version); the fork and
     upstream both ship package.version = "0.4.2", so one exemption
     line covers both while the fork is active and after retirement.
   - Patch-version bumps (raft-engine historically bumps on master
     without publishing) break matching and need a new version line.
   - SHA swaps are silent to vet - no re-exemption needed on rebase.
   - review-by is a mango convention, not a vet behavior; dates need
     manual refresh on rebase and on retirement.

Per rust-expert's explicit guidance, no further review round is needed
for these text-only edits.

---------

Co-authored-by: Archith <archith.rapaka@atomtickets.com>
humancto added a commit to humancto/mango that referenced this pull request Apr 24, 2026
…k (ROADMAP:815) (#49)

* feat(storage): add mango-storage skeleton with redb + raft-engine fork deps

Adds the Phase 1 storage crate skeleton per ROADMAP:815. Scaffolding
only — no trait definitions yet (those land in ROADMAP:816's own PR),
no implementations. The goal of this PR is to prove the two load-bearing
storage dependencies integrate cleanly end to end (resolver, cargo-deny,
cargo-vet, cargo-geiger) before the trait and impl PRs land on top.

What ships:

- crates/mango-storage skeleton: publish = false, deny(missing_docs),
  matches the shape of the existing mango crate. Single
  VERSION-matches-manifest smoke test.

- Cargo.toml workspace deps:
    redb = "4.1.0"
    raft-engine = humancto/raft-engine at e1d738d, default-features = false,
                  features = ["internals"]
  Consumers inherit the feature set verbatim via `.workspace = true`.

  Dropped the `scripting` feature from the original ADR 0002 §W5 plan:
  scripting pulls rhai → smartstring (MPL-2.0), which would require a
  license-allowlist widening for a raft-engine surface mango does not
  use (the TiKV-admin CLI path). `internals` is the only feature mango-
  raft actually needs.

- deny.toml:
  - allow-git allowlist entry for humancto/raft-engine with a retirement
    trigger (tikv/raft-engine#397 merging → drop the entry).
  - skip entries for four raft-engine-transitive major-version gaps
    (bitflags 1.x, getrandom 0.2, hashbrown 0.14, syn 1.x). Each names
    the retirement trigger (raft-engine upstream refresh).
  - ignore entry for RUSTSEC-2024-0437 (protobuf 2.28 uncontrolled
    recursion via raft-engine → prometheus). Skeleton exercises zero
    protobuf parsing; mango-raft impl PRs will carry fuzz corpora for
    the crafted-input class per ROADMAP's Reviewer's contract.

- supply-chain/config.toml:
  - [policy.mango-storage] first-party stanza.
  - [policy.raft-engine] audit-as-crates-io = true so one
    [[exemptions.raft-engine]] version = "0.4.2" entry covers both the
    active fork and post-retirement upstream, matching the posture
    recorded in .planning/fork-raft-engine-lz4-verification.md.
  - ~90 new [[exemptions.*]] entries for the transitive graph of both
    deps, each with review-by: 2026-10-23 in notes per house convention.

- unsafe-baseline.json: add mango-storage (all zeros — skeleton has no
  unsafe, no #![allow(unsafe_code)], no miri enrollment).

- supply-chain/imports.lock: auto-bumped by `cargo vet regenerate
  exemptions`.

- .planning/mango-storage-skeleton.plan.md: the revised plan after
  rust-expert's REVISE review (addressed two showstoppers, three bugs,
  and eight missing items from the first-round findings).

Verification (local):
  cargo check --workspace                              OK
  cargo clippy --workspace --all-targets -- -D warnings OK
  cargo fmt --check                                     OK
  cargo nextest run -p mango-storage                    1 passed
  cargo deny check (advisories/bans/licenses/sources)   all OK
  cargo vet check                                       Succeeded
  cargo run -q -p xtask-vet-ttl                         PASS
  cargo tree -p mango-storage -i lz4-sys                lz4-sys absent

Closes ROADMAP:815. Next item: ROADMAP:816 (Backend + RaftLogStore
trait definition).

* fix(storage): address rust-expert REVISE must-fix items on PR #49

Addresses the five must-fix items from rust-expert's final diff
review before the mango-storage skeleton can merge:

1. audit.yml: add `ignore: RUSTSEC-2024-0437` — rustsec/audit-check
   does not read deny.toml, so the protobuf recursion advisory must
   be suppressed separately here. Cross-reference comment points at
   deny.toml as the single source of truth for the justification.

2. fork-raft-engine-lz4-verification.md: rewrite the manifest snippet
   and retirement snippet to show `features = ["internals"]`, add a
   "scripting dropped" rationale section (MPL-2.0 via rhai /
   smartstring, avoided by not taking a feature mango does not use),
   and bump "Last updated" to reflect that PR #49 wires the dep.

3. supply-chain/config.toml raft-engine exemption: keep the
   SHA-qualified `version = "0.4.2@git:<SHA>"` form — verified by
   running `cargo vet check` that plain `version = "0.4.2"` does NOT
   match a git source, even with `audit-as-crates-io = true`. Rewrite
   the exemption notes AND the `[policy.raft-engine]` notes to
   honestly describe the actual cargo-vet behavior (exemptions key on
   fully-resolved source, not `package.version` alone) and the
   rebase/retirement implications. Fork-tracking doc §"Supply-chain
   audit posture" rewritten to match.

4. supply-chain/config.toml crc32fast exemption: add the missing
   `notes = "review-by: 2026-10-23 — raft-engine 0.4.2 transitive,
   audit pending"`. xtask-vet-ttl would have caught this on any next
   run.

5. mango-storage-skeleton.plan.md: update the Goal section and the
   raft-engine pin snippet to show `features = ["internals"]`, add
   a "Deviations from plan" section at the bottom enumerating the
   four real implementation departures from the revised plan
   (scripting drop, explicit version on the git dep, four dedup-skip
   entries, RUSTSEC-2024-0437 ignore).

Verified locally:
- cargo vet check — PASS
- cargo deny check — advisories/bans/licenses/sources all ok
- cargo run -q -p xtask-vet-ttl — PASS (149/149 exemptions have
  review-by)
- cargo clippy --workspace --all-targets -- -D warnings — clean
- cargo fmt --check — clean

imports.lock drift is cargo-vet's auto-regeneration; included in the
commit to keep the working tree clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(storage): cargo-vet fmt — triple-quote the raft-engine policy notes

CI surfaced: cargo-vet 0.10.2 store-consistency check rejects a
single-quoted multi-line string that contains an inner escape-quote.
cargo vet fmt (which CI runs with --locked --frozen, and which
rejects any reformatting delta) wanted the notes rewritten as a TOML
triple-quoted literal.

This is a formatting-only change: the text content is unchanged.
Local cargo vet check PASSES.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Archith <archith.rapaka@atomtickets.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
humancto pushed a commit to humancto/raft-engine that referenced this pull request May 3, 2026
CodeRabbit review on PR tikv#397 flagged that the new
'Build (no default features)' CI step ran only 'cargo build', which
neither compiles nor runs the
test_sanitize_rejects_nonzero_threshold_without_lz4_feature test in
src/config.rs (gated #[cfg(not(feature = "lz4-compression"))]). The
test silently never ran in any CI job, defeating the point of adding it.

Replaces the step with:

  cargo check --no-default-features --all-targets --verbose
  cargo test  --no-default-features --verbose

--all-targets covers tests/benches/examples in the no-default-features
graph; the test invocation actually exercises the gated reject + accept
paths in Config::sanitize.

Signed-off-by: Archith <archith.rapaka@gmail.com>
@humancto
Copy link
Copy Markdown
Author

humancto commented May 3, 2026

Addressed the CodeRabbit nitpick on the no-default-features CI step in ea4e85c.

The previous cargo build --no-default-features --verbose step neither compiled nor ran the new test_sanitize_rejects_nonzero_threshold_without_lz4_feature test in src/config.rs (gated under #[cfg(not(feature = "lz4-compression"))]), so the test silently never executed. Replaced with:

- name: Build and test (no default features)
  run: |
    cargo check --no-default-features --all-targets --verbose
    cargo test --no-default-features --verbose

--all-targets covers tests/benches/examples in the feature-off graph, and the explicit cargo test invocation actually exercises both the reject (threshold > 0Err) and accept (threshold == 0Ok) paths in Config::sanitize.

Archith and others added 3 commits May 2, 2026 18:42
Gate the `lz4-sys` C FFI dependency behind a new `lz4-compression`
Cargo feature, default-on so existing downstream behaviour is preserved
bit-for-bit.

Motivation
----------
Consumers who wish to build raft-engine as part of a pure-Rust tree
currently cannot: `lz4-sys = "=1.9.5"` is a hard, unconditional
dependency that pulls in a C compiler step at build time. Making the
C dependency feature-gated lets such consumers opt out via
`default-features = false` (plus any other features they need),
without affecting anyone who stays on defaults.

Mechanism
---------
- `Cargo.toml`: mark `lz4-sys` as `optional = true` and expose a new
  `lz4-compression` feature that enables it via the namespaced
  `dep:lz4-sys` syntax. `default = [..., "lz4-compression"]` keeps
  existing users on the same code path with no action required.
- `src/util.rs`: split `pub mod lz4` into two cfg-gated variants.
  The real implementation stays behind `#[cfg(feature =
  "lz4-compression")]`; a stub module with an identical public
  surface (`DEFAULT_LZ4_COMPRESSION_LEVEL`, `append_compress_block`,
  `decompress_block`) is provided under
  `#[cfg(not(feature = "lz4-compression"))]`. The stub returns
  `Error::Other` on attempted compression (with guidance to rebuild
  or set `compression_threshold = 0`) and `Error::Corruption` on
  attempted decompression (reader cannot interpret LZ4 payload).
  Call sites in `log_batch.rs` compile unchanged under both arms.

Operator contract
-----------------
Callers who disable this feature:
- MUST set `compression_threshold = 0` in their `Config` to avoid
  the runtime `append_compress_block` error path.
- MUST NOT open data files written by a build that had LZ4
  compression active; those entries will surface as `Corruption`
  on read.

Verified
--------
- `cargo build --no-default-features`: compiles, `cargo tree
  --no-default-features --edges no-dev | grep lz4` is empty
  (lz4-sys fully absent from the build graph).
- `cargo build` (defaults): compiles, `cargo tree --edges no-dev
  | grep lz4` still shows `lz4-sys v1.9.5` — unchanged behaviour
  for existing consumers.
- Full `cargo test` run blocked locally by a pre-existing grpcio-sys
  cmake toolchain issue on this workstation unrelated to this patch;
  relying on upstream CI to exercise the lz4 test module under the
  default feature set.

Signed-off-by: Archith <archith.rapaka@gmail.com>
rust-expert's adversarial review of 4e8a088 flagged six changes needed
before the patch is upstream-acceptable. This commit applies all six.

1. Fail-fast on misconfigured threshold (Showstopper)
   Config::sanitize now returns Err when
   batch_compression_threshold > 0 while the lz4-compression
   feature is disabled, instead of letting every >= 8KB log batch
   silently fail at write time. Matches the existing error patterns
   for enable_log_recycle / prefill_for_recycle. New unit test
   test_sanitize_rejects_nonzero_threshold_without_lz4_feature
   gated under cfg(not(feature = "lz4-compression")) covers both
   the reject and the 0-threshold accept paths.

2. Read-stub error variant (Bug)
   decompress_block on the feature-off stub returned
   Error::Corruption, which operators read as disk damage and
   triggers the repair flow. The data isn't corrupt - this build
   cannot decode it. Switched to Error::Other with a message that
   names the feature flag, matching the write-path stub.

3. Deduplicate DEFAULT_LZ4_COMPRESSION_LEVEL (Nit)
   Hoisted the constant to parent scope in util.rs; both lz4
   modules now pub use super::DEFAULT_LZ4_COMPRESSION_LEVEL.
   Eliminates drift risk.

4. Document on-disk compat in Config rustdoc (Risk)
   The rustdoc on batch_compression_threshold now calls out the
   feature-off constraint (must be 0 / cannot decode feature-on
   data) so callers don't need to read the changelog to discover it.

5. CHANGELOG + README feature entry (Risk)
   Added an [Unreleased] bullet and a new entry under "Available
   Cargo features" in the README explaining what the feature does,
   what disabling costs, and the forward-compat constraint.

6. CI coverage for --no-default-features (Risk)
   Added a cargo build --no-default-features --verbose step to
   the stable job in .github/workflows/rust.yml, right after
   clippy. Without this, the next unrelated PR that adds a
   use crate::util::lz4::X reference outside the real module
   would silently regress the feature-off build.

Verification: cargo check --lib --no-default-features and
cargo check --lib both pass (0 errors). Same two-arm cargo tree
grep check as the prior commit - lz4-sys absent without the feature,
present with it.

The [patch.crates-io] cc dead-patch warning on feature-off builds
noted in the review is left as a separate concern; the pin is a
workaround for a cc-rs issue whose applicability in 2026 warrants
a separate PR, not a bundled change here.

Signed-off-by: Archith <archith.rapaka@gmail.com>
CodeRabbit review on PR tikv#397 flagged that the new
'Build (no default features)' CI step ran only 'cargo build', which
neither compiles nor runs the
test_sanitize_rejects_nonzero_threshold_without_lz4_feature test in
src/config.rs (gated #[cfg(not(feature = "lz4-compression"))]). The
test silently never ran in any CI job, defeating the point of adding it.

Replaces the step with:

  cargo check --no-default-features --all-targets --verbose
  cargo test  --no-default-features --verbose

--all-targets covers tests/benches/examples in the no-default-features
graph; the test invocation actually exercises the gated reject + accept
paths in Config::sanitize.

Signed-off-by: Archith <archith.rapaka@gmail.com>
@humancto humancto force-pushed the feat/feature-gate-lz4-sys branch from ea4e85c to 4e01f4d Compare May 3, 2026 01:42
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Cargo.toml`:
- Around line 89-94: The doc comment in the lz4-compression feature block refers
to the wrong config key name ("compression_threshold = 0"); update the comment
to reference the actual TOML/serde key "batch-compression-threshold = 0"
(matching #[serde(rename_all = "kebab-case")]) so examples in the Cargo.toml doc
are correct and users won't copy an incorrect field name.

In `@src/config.rs`:
- Around line 216-221: Config::default() currently sets
batch_compression_threshold to ReadableSize::kb(8) which becomes the serde
default and causes sanitize() to error when the lz4-compression feature is
disabled; modify Config::default() to set batch_compression_threshold to zero
when the lz4 feature is not enabled (use the same conditional pattern already
applied to memory_limit in Config::default()), so deserialising TOML that omits
batch-compression-threshold yields a valid default; ensure you reference the
field name batch_compression_threshold and keep the ReadableSize type (set to
zero) and leave the sanitize() behavior unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: e27ebc26-718e-4764-af14-f6c2009731d4

📥 Commits

Reviewing files that changed from the base of the PR and between ea4e85c and 4e01f4d.

📒 Files selected for processing (6)
  • .github/workflows/rust.yml
  • CHANGELOG.md
  • Cargo.toml
  • README.md
  • src/config.rs
  • src/util.rs
✅ Files skipped from review due to trivial changes (4)
  • .github/workflows/rust.yml
  • CHANGELOG.md
  • README.md
  • src/util.rs

Comment thread Cargo.toml
Comment on lines +89 to +94
# Optional LZ4 compression for log batches via the `lz4-sys` C FFI crate.
# Default-on preserves historical behavior; disabling it removes the C
# dependency entirely from the build graph. Callers who disable this
# feature must set `compression_threshold = 0` in their `Config`, and
# must not open existing data written with compression enabled.
lz4-compression = ["dep:lz4-sys"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Doc comment uses the wrong field name: compression_threshold → should be batch-compression-threshold.

Line 92 references compression_threshold = 0, but the actual TOML key is batch-compression-threshold (due to #[serde(rename_all = "kebab-case")]). Users copying from this comment will either hit a silent no-op key or a deserialization error depending on their strictness settings.

📝 Proposed fix
-# feature must set `compression_threshold = 0` in their `Config`, and
+# feature must set `batch-compression-threshold = 0` in their `Config`, and
📝 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
# Optional LZ4 compression for log batches via the `lz4-sys` C FFI crate.
# Default-on preserves historical behavior; disabling it removes the C
# dependency entirely from the build graph. Callers who disable this
# feature must set `compression_threshold = 0` in their `Config`, and
# must not open existing data written with compression enabled.
lz4-compression = ["dep:lz4-sys"]
# Optional LZ4 compression for log batches via the `lz4-sys` C FFI crate.
# Default-on preserves historical behavior; disabling it removes the C
# dependency entirely from the build graph. Callers who disable this
# feature must set `batch-compression-threshold = 0` in their `Config`, and
# must not open existing data written with compression enabled.
lz4-compression = ["dep:lz4-sys"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Cargo.toml` around lines 89 - 94, The doc comment in the lz4-compression
feature block refers to the wrong config key name ("compression_threshold = 0");
update the comment to reference the actual TOML/serde key
"batch-compression-threshold = 0" (matching #[serde(rename_all = "kebab-case")])
so examples in the Cargo.toml doc are correct and users won't copy an incorrect
field name.

Comment thread src/config.rs
Comment on lines +216 to +221
#[cfg(not(feature = "lz4-compression"))]
if self.batch_compression_threshold.0 > 0 {
return Err(box_err!(
"batch-compression-threshold must be 0 when the `lz4-compression` Cargo feature is disabled"
));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Config::default() produces an invalid config when lz4-compression is disabled, breaking four existing tests under --no-default-features.

Config::default() always sets batch_compression_threshold: ReadableSize::kb(8). Because #[serde(default)] on the struct uses the Default implementation for missing fields, if the value is not present when deserializing, Default::default() is used — so any TOML config that omits batch-compression-threshold will silently inherit 8 KB. With the new sanitize() guard in place, this causes every existing test that (a) deserialises TOML without that key and (b) calls sanitize() to panic under --no-default-features:

Test Failing line Why
test_custom 302 .unwrap() TOML omits the field → default 8 KB → Err
test_invalid (soft case) 326 .unwrap() TOML omits the field → default 8 KB → Err
test_backward_compactibility 367 .unwrap() TOML omits the field → default 8 KB → Err
test_prefill_for_recycle 381, 390 .is_ok() TOML omits the field → default 8 KB → Err

The newly added CI step (cargo test --no-default-features) will immediately fail on all four.

The clean fix is to zero out batch_compression_threshold inside Config::default() under the same pattern already used for memory_limit in test mode (lines 155–159), which would also make Config::default() produce a valid config for library consumers who build without lz4-compression:

🐛 Proposed fix in Config::default() (lines 133–162)
         // Test-specific configurations.
         #[cfg(test)]
         {
             cfg.memory_limit = Some(ReadableSize(0));
         }
+        // When lz4-compression is disabled, force a valid default so that
+        // Config::default() does not immediately fail sanitize().
+        #[cfg(not(feature = "lz4-compression"))]
+        {
+            cfg.batch_compression_threshold = ReadableSize(0);
+        }
         cfg

The new test_sanitize_rejects_nonzero_threshold_without_lz4_feature test (lines 350–358) explicitly overrides the threshold to ReadableSize::kb(8) before the first assert!, so it remains correct after this fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.rs` around lines 216 - 221, Config::default() currently sets
batch_compression_threshold to ReadableSize::kb(8) which becomes the serde
default and causes sanitize() to error when the lz4-compression feature is
disabled; modify Config::default() to set batch_compression_threshold to zero
when the lz4 feature is not enabled (use the same conditional pattern already
applied to memory_limit in Config::default()), so deserialising TOML that omits
batch-compression-threshold yields a valid default; ensure you reference the
field name batch_compression_threshold and keep the ReadableSize type (set to
zero) and leave the sanitize() behavior unchanged.

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

Labels

contribution This PR is from a community contributor. dco-signoff: no Indicates the PR's author has not signed dco. first-time-contributor Indicates that the PR was contributed by an external member and is a first-time contributor. needs-ok-to-test Indicates a PR created by contributors and need ORG member send '/ok-to-test' to start testing. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants