-module(lightspeed@ops@load_harness). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/lightspeed/ops/load_harness.gleam"). -export([default_scenario/0, run/1, summary_signature/1, run_fixture_scenario/1, run_fixture_matrix/0, fixture_scenario_label/1, fixture_pass_fail_label/1, fixture_signature/1, fixture_deterministic/1, fixture_scenario/1, fixture_outcomes/1, failed_scenarios/1, nondeterministic_failures/1, fixture_report_signature/1, snapshot_signature/0, snapshot_report_markdown/0]). -export_type([scenario/0, session_outcome/0, summary/0, fixture_scenario/0, fixture_outcome/0, fixture_report/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(" Deterministic load-testing harness for session behavior.\n"). -type scenario() :: {scenario, integer(), integer(), boolean(), boolean(), integer()}. -type session_outcome() :: {session_outcome, binary(), integer(), integer(), integer(), integer(), integer(), integer(), integer()}. -type summary() :: {summary, integer(), integer(), integer(), integer(), integer(), integer(), list(session_outcome())}. -type fixture_scenario() :: baseline_load_profile | slow_ack_backlog_profile | clean_ack_profile. -type fixture_outcome() :: {fixture_outcome, fixture_scenario(), boolean(), boolean(), binary()}. -type fixture_report() :: {fixture_report, list(fixture_outcome()), integer(), integer()}. -file("src/lightspeed/ops/load_harness.gleam", 74). ?DOC(" Default scenario tuned for quick local checks.\n"). -spec default_scenario() -> scenario(). default_scenario() -> {scenario, 16, 32, true, true, 2}. -file("src/lightspeed/ops/load_harness.gleam", 405). -spec fold_outcomes( list(session_outcome()), fun((session_outcome(), integer()) -> integer()), integer() ) -> integer(). fold_outcomes(Outcomes, Reducer, Total) -> case Outcomes of [] -> Total; [Outcome | Rest] -> fold_outcomes(Rest, Reducer, Reducer(Outcome, Total)) end. -file("src/lightspeed/ops/load_harness.gleam", 424). -spec clamp_positive(integer(), integer()) -> integer(). clamp_positive(Value, Fallback) -> case Value =< 0 of true -> Fallback; false -> Value end. -file("src/lightspeed/ops/load_harness.gleam", 376). -spec summarize(list(session_outcome()), scenario()) -> summary(). summarize(Outcomes, Scenario) -> {summary, erlang:length(Outcomes), clamp_positive(erlang:element(2, Scenario), 1) * clamp_positive( erlang:element(3, Scenario), 0 ), fold_outcomes( Outcomes, fun(Outcome, Total) -> Total + erlang:element(6, Outcome) end, 0 ), fold_outcomes( Outcomes, fun(Outcome@1, Total@1) -> Total@1 + erlang:element(7, Outcome@1) end, 0 ), fold_outcomes( Outcomes, fun(Outcome@2, Total@2) -> Total@2 + erlang:element(8, Outcome@2) end, 0 ), fold_outcomes( Outcomes, fun(Outcome@3, Total@3) -> Total@3 + erlang:element(9, Outcome@3) end, 0 ), Outcomes}. -file("src/lightspeed/ops/load_harness.gleam", 358). -spec build_outcome(lightspeed@agent@session:session()) -> session_outcome(). build_outcome(State) -> Telemetry_summary = lightspeed@agent@session:telemetry_summary(State), Reconnects = lightspeed@agent@session:summary_session_rehydrated( Telemetry_summary ) + lightspeed@agent@session:summary_session_remounted(Telemetry_summary), {session_outcome, lightspeed@agent@session:id(State), lightspeed@agent@session:counter(State), lightspeed@agent@session:summary_total_events(Telemetry_summary), lightspeed@agent@session:pending_patch_count(State), lightspeed@agent@session:summary_patch_queued(Telemetry_summary), lightspeed@agent@session:summary_patch_acked(Telemetry_summary), lightspeed@agent@session:summary_session_crashed(Telemetry_summary), Reconnects}. -file("src/lightspeed/ops/load_harness.gleam", 416). -spec send( lightspeed@agent@session:session(), binary(), lightspeed@agent@session:inbox_event() ) -> lightspeed@agent@session:session(). send(State, Owner, Event) -> lightspeed@agent@session:handle(State, {inbox_message, Owner, Event}). -file("src/lightspeed/ops/load_harness.gleam", 345). -spec maybe_reconnect( lightspeed@agent@session:session(), binary(), binary(), boolean(), integer() ) -> lightspeed@agent@session:session(). maybe_reconnect(State, Owner, Route, Include_reconnect, Now_ms) -> case Include_reconnect of true -> send(State, Owner, {reconnect, Route, Now_ms}); false -> State end. -file("src/lightspeed/ops/load_harness.gleam", 330). -spec maybe_crash_and_restart( lightspeed@agent@session:session(), binary(), boolean(), integer() ) -> lightspeed@agent@session:session(). maybe_crash_and_restart(State, Owner, Include_crash_restart, Now_ms) -> case Include_crash_restart of true -> _pipe = State, _pipe@1 = send(_pipe, Owner, {crash, <<"load_harness"/utf8>>}), send(_pipe@1, Owner, {restart, Now_ms}); false -> State end. -file("src/lightspeed/ops/load_harness.gleam", 320). -spec should_ack(integer(), integer()) -> boolean(). should_ack(Step, Ack_every) -> case Ack_every =< 1 of true -> true; false -> Remainder = case Ack_every of 0 -> 0; Gleam@denominator -> Step rem Gleam@denominator end, Remainder =:= 0 end. -file("src/lightspeed/ops/load_harness.gleam", 307). -spec maybe_ack( lightspeed@agent@session:session(), binary(), binary(), integer(), integer() ) -> lightspeed@agent@session:session(). maybe_ack(State, Owner, Patch_ref, Step, Ack_every) -> case should_ack(Step, Ack_every) of true -> send(State, Owner, {ack, Patch_ref}); false -> State end. -file("src/lightspeed/ops/load_harness.gleam", 289). -spec apply_increments( lightspeed@agent@session:session(), binary(), integer(), integer(), integer() ) -> lightspeed@agent@session:session(). apply_increments(State, Owner, Remaining, Ack_every, Step) -> case Remaining =< 0 of true -> State; false -> Next = send(State, Owner, increment), Patch_ref = erlang:integer_to_binary(Step + 1), Next@1 = maybe_ack(Next, Owner, Patch_ref, Step, Ack_every), apply_increments(Next@1, Owner, Remaining - 1, Ack_every, Step + 1) end. -file("src/lightspeed/ops/load_harness.gleam", 260). -spec run_one(integer(), scenario()) -> session_outcome(). run_one(Index, Scenario) -> Session_id = <<"load-"/utf8, (erlang:integer_to_binary(Index))/binary>>, Owner = <<"proc-"/utf8, (erlang:integer_to_binary(Index))/binary>>, Route = <<"/counter/"/utf8, (erlang:integer_to_binary(Index))/binary>>, Increments = clamp_positive(erlang:element(3, Scenario), 0), Ack_every = clamp_positive(erlang:element(6, Scenario), 1), Started = lightspeed@agent@session:start( Session_id, Owner, rehydrate, 0, 100 ), Connected = send( Started, Owner, {connect, Route, <<"csrf-"/utf8, Session_id/binary>>, 0} ), Connected@1 = maybe_ack(Connected, Owner, <<"1"/utf8>>, 0, Ack_every), Updated = apply_increments(Connected@1, Owner, Increments, Ack_every, 1), Updated@1 = maybe_crash_and_restart( Updated, Owner, erlang:element(5, Scenario), 500 ), Updated@2 = maybe_reconnect( Updated@1, Owner, Route, erlang:element(4, Scenario), 700 ), build_outcome(Updated@2). -file("src/lightspeed/ops/load_harness.gleam", 244). -spec run_sessions(integer(), integer(), scenario(), list(session_outcome())) -> list(session_outcome()). run_sessions(Index, Max_index, Scenario, Outcomes_rev) -> case Index > Max_index of true -> lists:reverse(Outcomes_rev); false -> run_sessions( Index + 1, Max_index, Scenario, [run_one(Index, Scenario) | Outcomes_rev] ) end. -file("src/lightspeed/ops/load_harness.gleam", 85). ?DOC(" Run a deterministic load scenario.\n"). -spec run(scenario()) -> summary(). run(Scenario) -> Count = clamp_positive(erlang:element(2, Scenario), 1), Outcomes = run_sessions(1, Count, Scenario, []), summarize(Outcomes, Scenario). -file("src/lightspeed/ops/load_harness.gleam", 540). -spec count_nondeterministic_fixture_scenarios(list(fixture_outcome())) -> integer(). count_nondeterministic_fixture_scenarios(Outcomes) -> case Outcomes of [] -> 0; [Outcome | Rest] -> case erlang:element(4, Outcome) of true -> count_nondeterministic_fixture_scenarios(Rest); false -> 1 + count_nondeterministic_fixture_scenarios(Rest) end end. -file("src/lightspeed/ops/load_harness.gleam", 529). -spec count_failed_fixture_scenarios(list(fixture_outcome())) -> integer(). count_failed_fixture_scenarios(Outcomes) -> case Outcomes of [] -> 0; [Outcome | Rest] -> case erlang:element(3, Outcome) of true -> count_failed_fixture_scenarios(Rest); false -> 1 + count_failed_fixture_scenarios(Rest) end end. -file("src/lightspeed/ops/load_harness.gleam", 585). -spec session_outcome_signature(session_outcome()) -> binary(). session_outcome_signature(Outcome) -> <<<<<<<<<<<<<<<<<<<<<<<<<<<<(erlang:element(2, Outcome))/binary, ":"/utf8>>/binary, (erlang:integer_to_binary( erlang:element( 3, Outcome ) ))/binary>>/binary, ":"/utf8>>/binary, (erlang:integer_to_binary( erlang:element(4, Outcome) ))/binary>>/binary, ":"/utf8>>/binary, (erlang:integer_to_binary( erlang:element(5, Outcome) ))/binary>>/binary, ":"/utf8>>/binary, (erlang:integer_to_binary( erlang:element(6, Outcome) ))/binary>>/binary, ":"/utf8>>/binary, (erlang:integer_to_binary(erlang:element(7, Outcome)))/binary>>/binary, ":"/utf8>>/binary, (erlang:integer_to_binary(erlang:element(8, Outcome)))/binary>>/binary, ":"/utf8>>/binary, (erlang:integer_to_binary(erlang:element(9, Outcome)))/binary>>. -file("src/lightspeed/ops/load_harness.gleam", 610). -spec join_with(binary(), list(binary())) -> binary(). join_with(Separator, Values) -> case Values of [] -> <<""/utf8>>; [Value] -> Value; [Value@1 | Rest] -> <<<>/binary, (join_with(Separator, Rest))/binary>> end. -file("src/lightspeed/ops/load_harness.gleam", 578). -spec sum_pending(list(session_outcome())) -> integer(). sum_pending(Outcomes) -> case Outcomes of [] -> 0; [Outcome | Rest] -> erlang:element(5, Outcome) + sum_pending(Rest) end. -file("src/lightspeed/ops/load_harness.gleam", 225). ?DOC(" Stable summary signature for one scenario run.\n"). -spec summary_signature(summary()) -> binary(). summary_signature(Summary) -> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"sessions="/utf8, (erlang:integer_to_binary( erlang:element( 2, Summary ) ))/binary>>/binary, "|increments="/utf8>>/binary, (erlang:integer_to_binary( erlang:element( 3, Summary ) ))/binary>>/binary, "|patch_queued="/utf8>>/binary, (erlang:integer_to_binary( erlang:element(4, Summary) ))/binary>>/binary, "|patch_acked="/utf8>>/binary, (erlang:integer_to_binary( erlang:element(5, Summary) ))/binary>>/binary, "|crashes="/utf8>>/binary, (erlang:integer_to_binary( erlang:element(6, Summary) ))/binary>>/binary, "|reconnects="/utf8>>/binary, (erlang:integer_to_binary(erlang:element(7, Summary)))/binary>>/binary, "|pending_total="/utf8>>/binary, (erlang:integer_to_binary( sum_pending(erlang:element(8, Summary)) ))/binary>>/binary, "|outcomes="/utf8>>/binary, (join_with( <<","/utf8>>, gleam@list:map( erlang:element(8, Summary), fun session_outcome_signature/1 ) ))/binary>>. -file("src/lightspeed/ops/load_harness.gleam", 564). -spec max_pending(list(session_outcome()), integer()) -> integer(). max_pending(Outcomes, Current) -> case Outcomes of [] -> Current; [Outcome | Rest] -> Next = case erlang:element(5, Outcome) > Current of true -> erlang:element(5, Outcome); false -> Current end, max_pending(Rest, Next) end. -file("src/lightspeed/ops/load_harness.gleam", 553). -spec count_pending_sessions(list(session_outcome())) -> integer(). count_pending_sessions(Outcomes) -> case Outcomes of [] -> 0; [Outcome | Rest] -> case erlang:element(5, Outcome) > 0 of true -> 1 + count_pending_sessions(Rest); false -> count_pending_sessions(Rest) end end. -file("src/lightspeed/ops/load_harness.gleam", 498). -spec evaluate_clean_ack_profile() -> {boolean(), binary()}. evaluate_clean_ack_profile() -> Summary = run({scenario, 10, 20, false, false, 1}), Pending_sessions = count_pending_sessions(erlang:element(8, Summary)), Max_pending = max_pending(erlang:element(8, Summary), 0), Passed = ((((((erlang:element(2, Summary) =:= 10) andalso (erlang:element( 3, Summary ) =:= 200)) andalso (erlang:element(6, Summary) =:= 0)) andalso (erlang:element(7, Summary) =:= 0)) andalso (erlang:element(4, Summary) =:= erlang:element(5, Summary))) andalso (Pending_sessions =:= 0)) andalso (Max_pending =:= 0), {Passed, <<<<<<<<(summary_signature(Summary))/binary, "|pending_sessions="/utf8>>/binary, (erlang:integer_to_binary(Pending_sessions))/binary>>/binary, "|max_pending="/utf8>>/binary, (erlang:integer_to_binary(Max_pending))/binary>>}. -file("src/lightspeed/ops/load_harness.gleam", 464). -spec evaluate_slow_ack_backlog_profile() -> {boolean(), binary()}. evaluate_slow_ack_backlog_profile() -> Summary = run({scenario, 12, 24, true, true, 10}), Pending_sessions = count_pending_sessions(erlang:element(8, Summary)), Max_pending = max_pending(erlang:element(8, Summary), 0), Ack_gap = erlang:element(4, Summary) - erlang:element(5, Summary), Passed = ((((((erlang:element(2, Summary) =:= 12) andalso (erlang:element( 3, Summary ) =:= 288)) andalso (erlang:element(6, Summary) =:= 12)) andalso (erlang:element(7, Summary) =:= 12)) andalso (Ack_gap > 0)) andalso (Pending_sessions > 0)) andalso (Max_pending > 0), {Passed, <<<<<<<<<<<<(summary_signature(Summary))/binary, "|ack_gap="/utf8>>/binary, (erlang:integer_to_binary(Ack_gap))/binary>>/binary, "|pending_sessions="/utf8>>/binary, (erlang:integer_to_binary(Pending_sessions))/binary>>/binary, "|max_pending="/utf8>>/binary, (erlang:integer_to_binary(Max_pending))/binary>>}. -file("src/lightspeed/ops/load_harness.gleam", 439). -spec evaluate_baseline_load_profile() -> {boolean(), binary()}. evaluate_baseline_load_profile() -> Summary = run(default_scenario()), Pending_sessions = count_pending_sessions(erlang:element(8, Summary)), Max_pending = max_pending(erlang:element(8, Summary), 0), Passed = (((((((erlang:element(2, Summary) =:= 16) andalso (erlang:element( 3, Summary ) =:= 512)) andalso (erlang:element(6, Summary) =:= 16)) andalso (erlang:element(7, Summary) =:= 16)) andalso (erlang:element(4, Summary) >= erlang:element(5, Summary))) andalso (erlang:element(5, Summary) > 0)) andalso (Pending_sessions >= 0)) andalso (Max_pending >= 0), {Passed, <<<<<<<<(summary_signature(Summary))/binary, "|pending_sessions="/utf8>>/binary, (erlang:integer_to_binary(Pending_sessions))/binary>>/binary, "|max_pending="/utf8>>/binary, (erlang:integer_to_binary(Max_pending))/binary>>}. -file("src/lightspeed/ops/load_harness.gleam", 431). -spec evaluate_fixture_scenario(fixture_scenario()) -> {boolean(), binary()}. evaluate_fixture_scenario(Scenario) -> case Scenario of baseline_load_profile -> evaluate_baseline_load_profile(); slow_ack_backlog_profile -> evaluate_slow_ack_backlog_profile(); clean_ack_profile -> evaluate_clean_ack_profile() end. -file("src/lightspeed/ops/load_harness.gleam", 107). ?DOC(" Run one M34 fixture scenario twice and require deterministic parity.\n"). -spec run_fixture_scenario(fixture_scenario()) -> fixture_outcome(). run_fixture_scenario(Scenario) -> {First_passed, First_signature} = evaluate_fixture_scenario(Scenario), {Second_passed, Second_signature} = evaluate_fixture_scenario(Scenario), Deterministic = (First_passed =:= Second_passed) andalso (First_signature =:= Second_signature), Passed = (First_passed andalso Second_passed) andalso Deterministic, {fixture_outcome, Scenario, Passed, Deterministic, First_signature}. -file("src/lightspeed/ops/load_harness.gleam", 92). ?DOC(" Run the deterministic M34 fixture matrix.\n"). -spec run_fixture_matrix() -> fixture_report(). run_fixture_matrix() -> Outcomes = begin _pipe = [baseline_load_profile, slow_ack_backlog_profile, clean_ack_profile], gleam@list:map(_pipe, fun run_fixture_scenario/1) end, {fixture_report, Outcomes, count_failed_fixture_scenarios(Outcomes), count_nondeterministic_fixture_scenarios(Outcomes)}. -file("src/lightspeed/ops/load_harness.gleam", 123). ?DOC(" Scenario label.\n"). -spec fixture_scenario_label(fixture_scenario()) -> binary(). fixture_scenario_label(Scenario) -> case Scenario of baseline_load_profile -> <<"baseline_load_profile"/utf8>>; slow_ack_backlog_profile -> <<"slow_ack_backlog_profile"/utf8>>; clean_ack_profile -> <<"clean_ack_profile"/utf8>> end. -file("src/lightspeed/ops/load_harness.gleam", 132). ?DOC(" Stable pass/fail label.\n"). -spec fixture_pass_fail_label(fixture_outcome()) -> binary(). fixture_pass_fail_label(Outcome) -> case erlang:element(3, Outcome) of true -> <<"pass"/utf8>>; false -> <<"fail"/utf8>> end. -file("src/lightspeed/ops/load_harness.gleam", 140). ?DOC(" Outcome signature.\n"). -spec fixture_signature(fixture_outcome()) -> binary(). fixture_signature(Outcome) -> erlang:element(5, Outcome). -file("src/lightspeed/ops/load_harness.gleam", 145). ?DOC(" Determinism accessor.\n"). -spec fixture_deterministic(fixture_outcome()) -> boolean(). fixture_deterministic(Outcome) -> erlang:element(4, Outcome). -file("src/lightspeed/ops/load_harness.gleam", 150). ?DOC(" Scenario accessor.\n"). -spec fixture_scenario(fixture_outcome()) -> fixture_scenario(). fixture_scenario(Outcome) -> erlang:element(2, Outcome). -file("src/lightspeed/ops/load_harness.gleam", 155). ?DOC(" Report outcomes.\n"). -spec fixture_outcomes(fixture_report()) -> list(fixture_outcome()). fixture_outcomes(Report) -> erlang:element(2, Report). -file("src/lightspeed/ops/load_harness.gleam", 160). ?DOC(" Failed scenario count.\n"). -spec failed_scenarios(fixture_report()) -> integer(). failed_scenarios(Report) -> erlang:element(3, Report). -file("src/lightspeed/ops/load_harness.gleam", 165). ?DOC(" Nondeterministic scenario count.\n"). -spec nondeterministic_failures(fixture_report()) -> integer(). nondeterministic_failures(Report) -> erlang:element(4, Report). -file("src/lightspeed/ops/load_harness.gleam", 603). -spec bool_label(boolean()) -> binary(). bool_label(Value) -> case Value of true -> <<"true"/utf8>>; false -> <<"false"/utf8>> end. -file("src/lightspeed/ops/load_harness.gleam", 170). ?DOC(" Stable fixture report signature.\n"). -spec fixture_report_signature(fixture_report()) -> binary(). fixture_report_signature(Report) -> Entries = gleam@list:map( erlang:element(2, Report), fun(Outcome) -> <<<<<<<<<<<<(fixture_scenario_label(erlang:element(2, Outcome)))/binary, "="/utf8>>/binary, (fixture_pass_fail_label(Outcome))/binary>>/binary, ":deterministic="/utf8>>/binary, (bool_label(erlang:element(4, Outcome)))/binary>>/binary, ":"/utf8>>/binary, (erlang:element(5, Outcome))/binary>> end ), join_with(<<";"/utf8>>, Entries). -file("src/lightspeed/ops/load_harness.gleam", 186). ?DOC(" Deterministic snapshot signature for M34 drift gates.\n"). -spec snapshot_signature() -> binary(). snapshot_signature() -> <<<<<<"m34.snapshot.v"/utf8, (erlang:integer_to_binary(1))/binary>>/binary, "|"/utf8>>/binary, (fixture_report_signature(run_fixture_matrix()))/binary>>. -file("src/lightspeed/ops/load_harness.gleam", 194). ?DOC(" Deterministic markdown report for M34 fixture scripts.\n"). -spec snapshot_report_markdown() -> binary(). snapshot_report_markdown() -> Report = run_fixture_matrix(), Failed = failed_scenarios(Report), Nondeterministic = nondeterministic_failures(Report), Status = case (Failed =:= 0) andalso (Nondeterministic =:= 0) of true -> <<"OK"/utf8>>; false -> <<"FAIL"/utf8>> end, <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"# Load Fixture Report\n\n"/utf8, "snapshot_version: "/utf8>>/binary, (erlang:integer_to_binary( 1 ))/binary>>/binary, "\n"/utf8>>/binary, "status: "/utf8>>/binary, Status/binary>>/binary, "\n"/utf8>>/binary, "failed_scenarios: "/utf8>>/binary, (erlang:integer_to_binary( Failed ))/binary>>/binary, "\n"/utf8>>/binary, "nondeterministic_failures: "/utf8>>/binary, (erlang:integer_to_binary(Nondeterministic))/binary>>/binary, "\n\n"/utf8>>/binary, "snapshot_signature: "/utf8>>/binary, (snapshot_signature())/binary>>/binary, "\n\n"/utf8>>/binary, "report_signature: "/utf8>>/binary, (fixture_report_signature(Report))/binary>>/binary, "\n"/utf8>>.