Skip to content

schema_registry: load _schemas once across shards on startup - #31137

Merged
sjust-redpanda merged 3 commits into
redpanda-data:devfrom
sjust-redpanda:sjust/sr-startup-race
Jul 18, 2026
Merged

schema_registry: load _schemas once across shards on startup#31137
sjust-redpanda merged 3 commits into
redpanda-data:devfrom
sjust-redpanda:sjust/sr-startup-race

Conversation

@sjust-redpanda

@sjust-redpanda sjust-redpanda commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Schema Registry recovery of the internal _schemas topic on startup could
run multiple redundant full replays concurrently on the reader shard, making
cold start of a large registry take much longer than necessary (recovery
time scaled with the number of racing shards).

service is a per-shard peering_sharded_service, so each shard owns its
own _ensure_started one_shot, and do_start() only stamps _is_started
(its re-entry guard) from inside its invoke_on_all, after
create_internal_topic(). Previously, when first requests landed on several
shards within that window, each shard ran its own do_start() and launched
an independent replay.

This PR instead funnels every shard through a single one_shot on the reader
shard so the topic is loaded exactly once; the per-shard one_shot still
caches completion, so only the first request on each shard pays a
cross-shard hop and steady state is unchanged.

Recovery is also lazy (nothing loads the topic until the first request), so
the first request after a restart can block for the whole replay. This PR
adds a cluster config, schema_registry_replay_on_startup (default off),
that proactively drives the single-flight load at startup, fire-and-forget
under the gate, so the store hydrates ahead of traffic without blocking
broker start-up.

Covered by ducktape tests: a single replay per restart under concurrent
multi-shard load, and the eager/lazy behavior of the new config.

Backports Required

  • v26.2.x
  • v26.1.x

Release Notes

Improvements

  • Schema Registry now replays the internal _schemas topic exactly once
    when recovering on startup, instead of running redundant concurrent
    replays; cold start of a large registry is significantly faster.
  • Added a schema_registry_replay_on_startup cluster property (default off)
    that hydrates the Schema Registry store at broker start-up rather than
    lazily on the first request.

@sjust-redpanda
sjust-redpanda requested a review from a team as a code owner July 16, 2026 20:55
@sjust-redpanda
sjust-redpanda force-pushed the sjust/sr-startup-race branch from 4653902 to 0e4165b Compare July 16, 2026 21:16
@sjust-redpanda
sjust-redpanda marked this pull request as draft July 16, 2026 21:44
@sjust-redpanda
sjust-redpanda force-pushed the sjust/sr-startup-race branch from 0e4165b to 714c62d Compare July 16, 2026 21:52
@sjust-redpanda
sjust-redpanda marked this pull request as ready for review July 16, 2026 22:15
}

ss::future<> service::do_start() {
if (_is_started) {

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.

is it worth keeping _is_started around? maybe as an extra vassert(!_is_started) sanity check?

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.

I don't think so, but I don't feel strongly about it. Want me to re-add it?

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.

There is a difference in behavior related to one_shot. If the fetch fails, the one_shot will fail all waiters (transitively to the per-shard one_shot) and then rearm. A subsequent attempt could then retry and actually succeed. Don't have tests for that case, and do_start already retries, so this may not be important.

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.

ohhh sorry i see now the special one_shot typedef. I was missing that. SGTM

WillemKauf
WillemKauf previously approved these changes Jul 17, 2026

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

LGTM, but i'd defer to @pgellert signing off on this too. Are there other SR experts we can start pulling into these reviews so we aren't overloading poor Gellert? 🙂

@vbotbuildovich

vbotbuildovich commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

CI test results

test results on build#87257
test_status test_class test_method test_arguments test_kind job_url passed reason test_history
FLAKY(PASS) ShadowLinkUnfinalizedUpgradeTest test_full_lifecycle null integration https://buildkite.com/redpanda/redpanda/builds/87257#019f6d13-0d8b-478a-985f-773431ba6136 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0385, p0=1.0000, reject_threshold=0.0100. adj_baseline=0.1110, p1=0.3083, trust_threshold=0.5000) https://redpanda.metabaseapp.com/dashboard/87-tests?tab=142-dt-individual-test-history&test_class=ShadowLinkUnfinalizedUpgradeTest&test_method=test_full_lifecycle
test results on build#87352
test_status test_class test_method test_arguments test_kind job_url passed reason test_history
FLAKY(PASS) ShadowLinkTopicFailoverTests test_link_failover {"source_cluster_spec": {"cluster_type": "kafka", "kafka_quorum": "COMBINED_KRAFT", "kafka_version": "3.8.0"}, "storage_mode": "tiered_v2", "with_failures": false} integration https://buildkite.com/redpanda/redpanda/builds/87352#019f7240-bd7b-4858-9d5f-37769de5697c 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0000, 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=ShadowLinkTopicFailoverTests&test_method=test_link_failover
FLAKY(PASS) NodesDecommissioningTest test_decommission_status null integration https://buildkite.com/redpanda/redpanda/builds/87352#019f7240-bd7f-469d-a935-5da333243820 10/11 Test PASSES after retries.No significant increase in flaky rate(baseline=0.0000, 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=NodesDecommissioningTest&test_method=test_decommission_status

pgellert
pgellert previously approved these changes Jul 17, 2026
Comment on lines +846 to +847
"eager _schemas replay failed at start-up; will retry on "
"the first request: {}",

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.

will retry on the first request

Is this bit true? The load once ensures that we will not retry reloading (which is fine), so I guess we may only "retry" in the sense that we will continue to try to replay the topic from the read syncs on the http handler path. Perhaps that's what you meant?

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.

If the fetch fails, the one_shot will fail all waiters (transitively to the per-shard one_shot) and then rearm. A subsequent attempt triggered by a request could then retry and actually succeed. Don't have tests for that case, and do_start already retries, so this may not be important.

@pgellert

Copy link
Copy Markdown
Contributor

Are there other SR experts we can start pulling into these reviews so we aren't overloading poor Gellert?

@nguyen-andrew has done a bunch of work on SR, so feel free to throw him on reviews, too. Or anyone from the enterprise team really (@redpanda-data/core-enterprise, we're about to get everyone into this GH team) can do at least a single pass and bubble specific questions up to me. :)

sjust-redpanda and others added 2 commits July 17, 2026 11:42
service is a per-shard peering_sharded_service, so each shard has its own
_ensure_started one_shot. do_start() only stamps _is_started (its re-entry
guard) from inside its invoke_on_all, after create_internal_topic(), so
first requests arriving on several shards concurrently each run do_start()
and launch an independent, redundant full replay of the _schemas topic on
the reader shard - slowing recovery by up to the shard count.

Funnel every shard through a single one_shot on the reader shard so the
topic is loaded exactly once. The per-shard one_shot still caches
completion, so only the first request on each shard pays a cross-shard hop;
failure/retry semantics are unchanged.

Covered by a ducktape test that restarts the registry under concurrent
multi-shard load and asserts exactly one replay runs per restart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_load_once makes do_start() run exactly once on the reader shard, so its
old cross-shard re-entry guard is redundant. Drop the _is_started flag,
assert do_start() runs on the reader shard, and run the fetch inline rather
than via invoke_on_all (whose only remaining job was setting _is_started on
every shard).

Removing _is_started also fixes a latent case: a fetch that exhausted its
retries left _is_started set, so the _load_once retry short-circuited and
the store never loaded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sjust-redpanda
sjust-redpanda dismissed stale reviews from pgellert and WillemKauf via a553561 July 17, 2026 18:54
@sjust-redpanda
sjust-redpanda force-pushed the sjust/sr-startup-race branch from 714c62d to a553561 Compare July 17, 2026 18:54
nguyen-andrew
nguyen-andrew previously approved these changes Jul 17, 2026

@nguyen-andrew nguyen-andrew left a comment

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.

🙌

Comment thread src/v/config/configuration.cc Outdated
*this,
"schema_registry_replay_on_startup",
"Replay the internal `_schemas` topic into the Schema Registry store at "
"broker start-up instead of lazily on the first request. Makes recovery "

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.

nit: I think instead of "at broker start-up", we could say "at Schema Registry start-up", since I believe we can restart the schema registry service (and have this new config take effect) without having to restart the broker

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.

Good point, fixed.

Recovery of the _schemas store is lazy: nothing loads the topic until the
first request (or an internal schema::registry access) reaches
ensure_started(). On a large registry the first request then blocks for the
whole replay, and there is no way to hydrate the store ahead of traffic.

Add a cluster config (default off) that, when set, proactively drives the
replay on the reader shard at service start-up, fire-and-forget under the
gate so it does not block broker start-up. It reuses the single-flight
ensure_started() path, so a request that races the eager trigger still
results in exactly one replay.

Covered by ducktape tests for both settings: with the config set the store
hydrates on restart with no client request, and with it unset recovery stays
lazy until the first request.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sjust-redpanda
sjust-redpanda merged commit 0dc8147 into redpanda-data:dev Jul 18, 2026
19 checks passed
@vbotbuildovich

Copy link
Copy Markdown
Collaborator

/backport v26.2.x

@vbotbuildovich

Copy link
Copy Markdown
Collaborator

/backport v26.1.x

@vbotbuildovich

Copy link
Copy Markdown
Collaborator

Branch name "v26.2.x" not found.

Workflow run logs.

Comment thread src/v/pandaproxy/schema_registry/service.cc Outdated
@sjust-redpanda

Copy link
Copy Markdown
Contributor Author

/backport v26.2.x

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.

6 participants