@@ -12134,3 +12134,108 @@ def hammer():
1213412134 )
1213512135 assert r .status_code == requests .codes .ok , r .text
1213612136 assert len (r .json ()) >= self .N_SUBJECTS
12137+
12138+ @cluster (num_nodes = 3 , log_allow_list = _SR_STARTUP_RECOVERY_LOG_ALLOW_LIST )
12139+ def test_replay_on_startup_without_request (self ):
12140+ """
12141+ With schema_registry_replay_on_startup enabled, the store must hydrate
12142+ proactively at startup without any client request driving it.
12143+
12144+ admin restart_service re-runs service::start() (via api::restart ->
12145+ start -> service::start), which is where the eager trigger lives, and
12146+ does not restart the broker process, so the log persists and we can
12147+ watch for the replay without touching the SR API.
12148+ """
12149+ node = self .redpanda .nodes [0 ]
12150+ host = node .account .hostname
12151+
12152+ # Populate _schemas so a restart has a topic to replay.
12153+ self ._register_schemas (host )
12154+ wait_until (
12155+ lambda : self ._all_subjects_served (host ),
12156+ timeout_sec = 30 ,
12157+ backoff_sec = 1 ,
12158+ err_msg = "subjects not visible after registration" ,
12159+ )
12160+
12161+ self .redpanda .set_cluster_config ({"schema_registry_replay_on_startup" : True })
12162+
12163+ init_before = self .redpanda .count_log_node (node , self .INIT_MARKER )
12164+
12165+ admin = Admin (self .redpanda )
12166+ result = admin .restart_service (rp_service = "schema-registry" , node = node )
12167+ assert result .status_code == requests .codes .ok , (
12168+ f"restart_service failed: { result .status_code } { result .text } "
12169+ )
12170+
12171+ # Deliberately issue NO SR request. The eager startup trigger must drive
12172+ # the replay on its own. count_log_node greps the broker log over ssh,
12173+ # so polling here does not hit the SR API and cannot trigger the lazy
12174+ # path.
12175+ wait_until (
12176+ lambda : self .redpanda .count_log_node (node , self .INIT_MARKER ) > init_before ,
12177+ timeout_sec = 60 ,
12178+ backoff_sec = 1 ,
12179+ err_msg = "eager startup replay did not run without any request" ,
12180+ )
12181+
12182+ replays = self .redpanda .count_log_node (node , self .INIT_MARKER ) - init_before
12183+ assert replays == 1 , (
12184+ f"expected exactly one eager replay, saw { replays } "
12185+ f"('{ self .INIT_MARKER } ' delta)."
12186+ )
12187+
12188+ # The store hydrated without a request; a request now succeeds
12189+ # immediately rather than blocking on a cold replay.
12190+ assert self ._all_subjects_served (host )
12191+
12192+ @cluster (num_nodes = 3 , log_allow_list = _SR_STARTUP_RECOVERY_LOG_ALLOW_LIST )
12193+ def test_no_replay_on_startup_by_default (self ):
12194+ """
12195+ With schema_registry_replay_on_startup unset (the default), recovery
12196+ stays lazy: a restart does not replay _schemas until a request (or an
12197+ internal access) arrives. The complement of
12198+ test_replay_on_startup_without_request, and a guard that the config
12199+ actually gates the eager path.
12200+ """
12201+ node = self .redpanda .nodes [0 ]
12202+ host = node .account .hostname
12203+
12204+ self ._register_schemas (host )
12205+ wait_until (
12206+ lambda : self ._all_subjects_served (host ),
12207+ timeout_sec = 30 ,
12208+ backoff_sec = 1 ,
12209+ err_msg = "subjects not visible after registration" ,
12210+ )
12211+
12212+ # Deliberately leave schema_registry_replay_on_startup at its default
12213+ # (off). do_start() logs INIT_MARKER once per run, so a flat count after
12214+ # a restart with no request means recovery did not run.
12215+ init_before = self .redpanda .count_log_node (node , self .INIT_MARKER )
12216+
12217+ admin = Admin (self .redpanda )
12218+ result = admin .restart_service (rp_service = "schema-registry" , node = node )
12219+ assert result .status_code == requests .codes .ok , (
12220+ f"restart_service failed: { result .status_code } { result .text } "
12221+ )
12222+
12223+ # No SR request is issued. Give recovery ample time to (not) happen;
12224+ # a real replay of this topic completes in well under a second.
12225+ time .sleep (15 )
12226+ assert self .redpanda .count_log_node (node , self .INIT_MARKER ) == init_before , (
12227+ "recovery ran at startup without the config set and without a "
12228+ "request; it should be lazy by default"
12229+ )
12230+
12231+ # The first request now lazily triggers exactly one replay and serves.
12232+ wait_until (
12233+ lambda : self ._all_subjects_served (host ),
12234+ timeout_sec = 30 ,
12235+ backoff_sec = 1 ,
12236+ err_msg = "lazy recovery did not serve after the first request" ,
12237+ )
12238+ replays = self .redpanda .count_log_node (node , self .INIT_MARKER ) - init_before
12239+ assert replays == 1 , (
12240+ f"expected exactly one lazy replay after the first request, saw { replays } ."
12241+ )
0 commit comments