Skip to content

[CORE-16776] cluster_link: prefer the source-reported deleted flag for SR sync#31076

Merged
pgellert merged 2 commits into
redpanda-data:devfrom
mnajda-redpanda:mnajda/sr-sync-prefer-body-deleted
Jul 13, 2026
Merged

[CORE-16776] cluster_link: prefer the source-reported deleted flag for SR sync#31076
pgellert merged 2 commits into
redpanda-data:devfrom
mnajda-redpanda:mnajda/sr-sync-prefer-body-deleted

Conversation

@mnajda-redpanda

@mnajda-redpanda mnajda-redpanda commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Schema Registry API-mode sync propagates a source version's soft-delete state
to the destination. Until now the imported deleted-state was taken solely from
the listing set-difference (all − active): the per-version body could not be
trusted because a Confluent source omitted the deleted flag from it by
default.

Now that the SR REST client opts into Confluent's extended schema fields, the
per-version body carries deleted reliably for both Redpanda and Confluent
sources. This PR prefers that source-reported flag over the listing-derived
set: it is fresher (read on a later call than the listing) and avoids the
listing set-difference, which can race with a concurrent (un)delete on the
source. The listing-derived set is kept as a fallback for a source that still
omits the flag from the body.

Backports Required

  • none - not a bug fix
  • none - this is a backport
  • none - issue does not exist in previous branches
  • none - papercut/not impactful enough to backport
  • v26.1.x
  • v25.3.x
  • v25.2.x

Release Notes

  • none

Copilot AI review requested due to automatic review settings July 13, 2026 10:38
@mnajda-redpanda mnajda-redpanda marked this pull request as draft July 13, 2026 10:38
@mnajda-redpanda mnajda-redpanda changed the title tests/rm_stm: wait for STM replay before LSO check after restart [CORE-16776] cluster_link: prefer the source-reported deleted flag for SR sync Jul 13, 2026
@mnajda-redpanda mnajda-redpanda self-assigned this Jul 13, 2026

Copilot AI 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.

Pull request overview

This PR updates schema-registry sync behavior to distinguish an explicitly reported deleted flag from an omitted one, enabling the reconciler to prefer the per-version body’s deleted state when present and fall back to the listing-derived soft-delete set only when the source omits the flag.

Changes:

  • Add source_schema_read::reported_deleted (std::optional<is_deleted>) to capture whether deleted was present in the source response body.
  • Update JSON parsing to preserve “absent vs explicit false” and keep schema.deleted defaulting to no when absent.
  • Adjust schema-registry sync reconciler logic and tests to prefer reported_deleted over the listing-derived soft-delete set, with explicit fallback behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/v/pandaproxy/schema_registry/types.h Adds reported_deleted to represent presence/absence of the deleted flag.
src/v/pandaproxy/schema_registry/rest_client/parse.cc Parses deleted into an optional and wires it into source_schema_read.
src/v/pandaproxy/schema_registry/rest_client/tests/parse_test.cc Adds/updates tests validating reported_deleted semantics for present/absent cases.
src/v/cluster_link/schema_registry_sync/reconciler.cc Prefers reported_deleted when present; otherwise falls back to listing-derived soft-deletes.
src/v/cluster_link/schema_registry_sync/mirroring_task.cc Updates comments to reflect the new “fallback” role of the listing-derived set.
src/v/cluster_link/schema_registry_sync/http_source_reader.cc Updates comments clarifying that reported_deleted is carried through to the reconciler.
src/v/cluster_link/schema_registry_sync/tests/sr_sync_test_fixtures.h Extends the fake source to model sources that omit the deleted flag.
src/v/cluster_link/schema_registry_sync/tests/reconciler_test.cc Adds/adjusts tests for precedence rules between body-reported vs listing-derived deleted state.

Comment thread src/v/pandaproxy/schema_registry/types.h Outdated
@mnajda-redpanda mnajda-redpanda marked this pull request as ready for review July 13, 2026 10:51
@mnajda-redpanda mnajda-redpanda requested review from a team, bartoszpiekny-redpanda, dotnwat, nguyen-andrew and pgellert and removed request for a team July 13, 2026 10:52
pgellert
pgellert previously approved these changes Jul 13, 2026
Comment on lines +775 to +783
struct source_schema_read {
stored_schema schema;
chunked_vector<unsupported_feature> unsupported;
/// The soft-delete state the source explicitly reported for this version,
/// or nullopt when the source omitted `deleted` from the body. When
/// nullopt, `schema.deleted` is only the `no` default and the real state
/// must be derived from the active-vs-deleted listing partition rather than
/// trusted from the body.
std::optional<is_deleted> reported_deleted;

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.

What do you think about an alternative type definition that looks something like this, which would avoid the potential inconsistency between schema.deleted and reported_deleted altogether?

struct source_schema_read {
    subject_schema schema;
    schema_version version{invalid_schema_version};
    schema_id id{invalid_schema_id};
    /// As reported by the source; nullopt when the body omitted `deleted`.
    std::optional<is_deleted> deleted;
    chunked_vector<unsupported_feature> unsupported;

    stored_schema into_stored(is_deleted fallback = is_deleted::no) && {
        return {std::move(schema), version, id, deleted.value_or(fallback)};
    }
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a much better way to do it, implemented :)

@vbotbuildovich

Copy link
Copy Markdown
Collaborator

Retry command for Build#87003

please wait until all jobs are finished before running the slash command

/ci-repeat 1
skip-redpanda-build
skip-units
skip-rebase
tests/rptest/tests/cloud_topics/e2e_test.py::EndToEndCloudTopicsStorageModeToggleTest.test_toggle_storage_mode

@mnajda-redpanda mnajda-redpanda added this to the v26.2.1-RC3 milestone Jul 13, 2026
@mnajda-redpanda mnajda-redpanda force-pushed the mnajda/sr-sync-prefer-body-deleted branch from 93822d4 to c6d1f6a Compare July 13, 2026 14:40
@mnajda-redpanda

mnajda-redpanda commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

force-push 1: implement @pgellert suggestion

  • source_schema_read: flatten schema/version/id to top level; drop the
    reported_deleted sidecar + eager schema.deleted write in favor of a
    single deleted optional resolved by a new into_stored(fallback).
  • reconciler import_body: materialize via std::move(read).into_stored(...)
    instead of mutating read.schema.deleted; resolve_refs/body_size now take
    subject_schema.

force-push 2: clean up a comment

force-push 3: formatting

force-push 4: align missed tests

force-push 5: clang-tidy fix

@mnajda-redpanda mnajda-redpanda force-pushed the mnajda/sr-sync-prefer-body-deleted branch 2 times, most recently from 7fc13d1 to e1824c2 Compare July 13, 2026 14:50
parse_subject_version now distinguishes an absent `deleted` field from an
explicit false via source_schema_read::deleted (a std::optional), so a caller
can tell "source said active" from "source said nothing". The concrete deleted
state is resolved by into_stored(), which applies a caller-supplied fallback
when the source omitted the flag.
@mnajda-redpanda mnajda-redpanda force-pushed the mnajda/sr-sync-prefer-body-deleted branch from e1824c2 to 01e85e2 Compare July 13, 2026 15:07
import_body now trusts the version body's `deleted` when the source reports it
(fresher, read on a later call than the listing, and free of the listing
set-difference race) and falls back to the listing-derived soft_deleted set
only when the source omits it. Tests cover body-wins in both directions and
the omit fallback.
@mnajda-redpanda mnajda-redpanda force-pushed the mnajda/sr-sync-prefer-body-deleted branch from 01e85e2 to 3d345eb Compare July 13, 2026 15:34
@pgellert pgellert enabled auto-merge July 13, 2026 15:53
@vbotbuildovich

Copy link
Copy Markdown
Collaborator

CI test results

test results on build#87013
test_status test_class test_method test_arguments test_kind job_url passed reason test_history
FLAKY(PASS) ShutdownTest test_timely_shutdown_with_failures null integration https://buildkite.com/redpanda/redpanda/builds/87013#019f5c48-00cf-4c58-b293-63b29e6227b7 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0159, p0=1.0000, reject_threshold=0.0100. adj_baseline=0.1000, p1=0.3487, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=ShutdownTest&test_method=test_timely_shutdown_with_failures

@pgellert pgellert merged commit 8cfd648 into redpanda-data:dev Jul 13, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants