mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-09 18:15:50 +00:00
fix(release): mark store-history SLO measurement windows
The failed exact rehearsal buffered the store-history latency assertion, preventing resource telemetry from locating its actual test interval. Extend the existing exact API lifecycle allowlist and cover both targets with synthetic pass/fail fixtures without rerunning product qualification or changing thresholds. This is prospective observability, not clearance of the retained latency or crash evidence. Change-source: pulse-maintainer
This commit is contained in:
@@ -42,7 +42,7 @@ python3 -m unittest discover -s scripts/release_control/internal -p 'release_pre
|
||||
bash -n scripts/release-preflight-worker.sh
|
||||
```
|
||||
|
||||
## Stress-test event window
|
||||
## Bounded API test event windows
|
||||
|
||||
Rehearsal backend Go output is streamed with `-json` and decoded by
|
||||
`release-go-test-events.py`. Every Output string is retained verbatim (including
|
||||
@@ -50,7 +50,8 @@ verbose test logs, skips and package summaries); stderr remains on stderr.
|
||||
Shell pipefail retains a failing Go exit. Invalid event input is drained,
|
||||
retained and fails the reader rather than quietly losing diagnostics.
|
||||
|
||||
Only the exact API `TestMultiTenant_ConcurrentAPIStress` lifecycle receives
|
||||
Only the exact API `TestMultiTenant_ConcurrentAPIStress` and
|
||||
`TestSLO_MetricsHistoryStore` lifecycles receive
|
||||
`RELEASE_GO_TEST_EVENT` records with Go's event Time, a separately labelled
|
||||
receipt timestamp and the same allowlisted resource snapshot. Resource time is
|
||||
collection time, not the Go event time. Sampling and verbose streaming add
|
||||
@@ -67,3 +68,10 @@ terminal events. Cgroup ancestry remains shared context, not per-test CPU usage.
|
||||
```sh
|
||||
python3 -m unittest discover -s scripts/release_control/internal -p 'release_go_test_events_test.py' -v
|
||||
```
|
||||
|
||||
The store-history SLO marker addresses a separate timing gap in failed rehearsal
|
||||
20260908T221214Z: its completed latency assertion was buffered until package
|
||||
output, so launcher resource samples cannot locate the actual test interval.
|
||||
Neither parent throttling over the backend window nor a later passing isolated
|
||||
sample would establish the cause. The marker observes test lifecycle only; it
|
||||
adds no database inspection, replay or threshold change.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render streamed go test -json output and retain bounded stress-test timing.
|
||||
"""Render streamed go test -json output and retain bounded API timing.
|
||||
|
||||
Go's event Time is distinct from receipt time and the resource sample time.
|
||||
Samples share the worker cgroup; they do not establish per-test CPU usage or
|
||||
@@ -18,7 +18,10 @@ spec = importlib.util.spec_from_file_location(
|
||||
resources = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(resources)
|
||||
|
||||
TARGET = "TestMultiTenant_ConcurrentAPIStress"
|
||||
TARGETS = frozenset({
|
||||
"TestMultiTenant_ConcurrentAPIStress",
|
||||
"TestSLO_MetricsHistoryStore",
|
||||
})
|
||||
PACKAGE = "github.com/rcourtman/pulse-go-rewrite/internal/api"
|
||||
ACTIONS = {"run", "pause", "cont", "pass", "fail", "skip"}
|
||||
|
||||
@@ -40,7 +43,7 @@ def render(source, destination):
|
||||
invalid = True
|
||||
continue
|
||||
destination.write(output)
|
||||
if (event.get("Package") == PACKAGE and event.get("Test") == TARGET
|
||||
if (event.get("Package") == PACKAGE and event.get("Test") in TARGETS
|
||||
and event.get("Action") in ACTIONS):
|
||||
evidence = {key: event[key] for key in
|
||||
("Time", "Action", "Package", "Test", "Elapsed") if key in event}
|
||||
|
||||
@@ -19,10 +19,10 @@ spec.loader.exec_module(events)
|
||||
class EventsTest(unittest.TestCase):
|
||||
def test_output_and_bounded_evidence(self):
|
||||
data = [dict(Time='2026-09-08T19:00:00Z', Action=action,
|
||||
Package=events.PACKAGE, Test=events.TARGET, Elapsed=2.0)
|
||||
Package=events.PACKAGE, Test="TestMultiTenant_ConcurrentAPIStress", Elapsed=2.0)
|
||||
for action in ('run', 'pause', 'cont', 'pass', 'fail', 'skip')]
|
||||
data += [dict(Action='output', Output='FAIL\tpackage\t2s\n'),
|
||||
dict(Action='run', Package='other', Test=events.TARGET),
|
||||
dict(Action='run', Package='other', Test="TestMultiTenant_ConcurrentAPIStress"),
|
||||
dict(Action='run', Package=events.PACKAGE, Test='TestOther')]
|
||||
output = io.StringIO()
|
||||
with patch.object(events.resources, 'snapshot', return_value={'unix_time_ns': 456}) as sample:
|
||||
@@ -36,8 +36,29 @@ class EventsTest(unittest.TestCase):
|
||||
self.assertGreater(evidence['received_unix_time_ns'], 456)
|
||||
self.assertEqual(evidence['resources'], {'unix_time_ns': 456})
|
||||
|
||||
def test_slo_exact_target_lifecycle_and_exclusions(self):
|
||||
data = [dict(Action=action, Package=events.PACKAGE,
|
||||
Test='TestSLO_MetricsHistoryStore')
|
||||
for action in ('run', 'pause', 'cont', 'pass', 'fail', 'skip')]
|
||||
data += [dict(Action='run', Package=package, Test=test)
|
||||
for package, test in (
|
||||
('other', 'TestSLO_MetricsHistoryStore'),
|
||||
(events.PACKAGE, 'TestSLO_MetricsHistoryStore/subtest'),
|
||||
(events.PACKAGE, 'TestSLO_MetricsHistoryStoreExtra'),
|
||||
(events.PACKAGE, 'TestSLO_MetricsHistoryMemory'))]
|
||||
output = io.StringIO()
|
||||
with patch.object(events.resources, 'snapshot', return_value={}) as sample:
|
||||
self.assertEqual(events.render(io.StringIO(''.join(
|
||||
json.dumps(x) + '\n' for x in data)), output), 0)
|
||||
self.assertEqual(sample.call_count, 6)
|
||||
records = [json.loads(line.removeprefix('RELEASE_GO_TEST_EVENT '))
|
||||
for line in output.getvalue().splitlines()]
|
||||
self.assertEqual([r['Action'] for r in records],
|
||||
['run', 'pause', 'cont', 'pass', 'fail', 'skip'])
|
||||
self.assertTrue(all(r['Test'] == 'TestSLO_MetricsHistoryStore' for r in records))
|
||||
|
||||
def test_unavailable_snapshot_does_not_change_verdict(self):
|
||||
event = dict(Action='fail', Package=events.PACKAGE, Test=events.TARGET)
|
||||
event = dict(Action='fail', Package=events.PACKAGE, Test="TestMultiTenant_ConcurrentAPIStress")
|
||||
output = io.StringIO()
|
||||
with patch.object(events.resources, 'snapshot', side_effect=OSError):
|
||||
self.assertEqual(events.render(io.StringIO(json.dumps(event)+'\n'), output), 0)
|
||||
@@ -59,6 +80,7 @@ func TestMultiTenant_ConcurrentAPIStress(t *testing.T) {
|
||||
t.Log("synthetic log")
|
||||
if os.Getenv("FIXTURE_FAIL") == "1" { t.Fatal("synthetic failure") }
|
||||
}
|
||||
func TestSLO_MetricsHistoryStore(t *testing.T) { t.Log("synthetic SLO marker") }
|
||||
func TestSkipped(t *testing.T) { t.Skip("synthetic skip") }
|
||||
''')
|
||||
for fail in ('0', '1'):
|
||||
@@ -72,7 +94,10 @@ func TestSkipped(t *testing.T) { t.Skip("synthetic skip") }
|
||||
for line in result.stdout.splitlines()
|
||||
if line.startswith('RELEASE_GO_TEST_EVENT ')]
|
||||
self.assertEqual([r['Action'] for r in records],
|
||||
['run', 'fail' if fail == '1' else 'pass'])
|
||||
['run', 'fail' if fail == '1' else 'pass', 'run', 'pass'])
|
||||
self.assertEqual([r['Test'] for r in records],
|
||||
['TestMultiTenant_ConcurrentAPIStress'] * 2 +
|
||||
['TestSLO_MetricsHistoryStore'] * 2)
|
||||
self.assertTrue(all(r.get('Time') for r in records))
|
||||
self.assertIn('synthetic log', result.stdout)
|
||||
self.assertIn('--- SKIP: TestSkipped', result.stdout)
|
||||
|
||||
Reference in New Issue
Block a user