Skip to content

Commit b0d9bcb

Browse files
antiguruclaude
andcommitted
benchmarks: guard the interactive runtime's read paths
Feature-benchmark scenarios for the four read shapes the interactive runtime changes, each pinning the flag on and provisioning its own cluster, which the benchmark's fresh instance per scenario makes safe: a peek dataflow joining two indexes, a fast-path point lookup, `CREATE INDEX` plus the first read that uses it, and a per-replica introspection read. They skip on versions before the flag exists, following the `can_run` precedent. Parallel-benchmark scenarios for what the second runtime is meant to buy: the temporary-dataflow floor on a quiet replica, introspection latency under hydration, and how far expensive peek walks hold back a written index's frontier, measured as the latency of a strict serializable read after a write. The two existing isolation scenarios now report regressions on their measured loops, with looser thresholds for contended tails. A sqllogictest pins the flag on a two-worker replica and covers same-key duplicate indexes across drops, error results on the fast path and through a peek dataflow, strict serializable reads over shared arrangements, and cluster re-provisioning. The clusterd-test-driver two-runtime workflow also runs with two workers, so the registry's worker pairing is exercised. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDm7opomJLxbNUEP3r9BLk
1 parent 872ae4f commit b0d9bcb

6 files changed

Lines changed: 624 additions & 13 deletions

File tree

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# Copyright Materialize, Inc. and contributors. All rights reserved.
2+
#
3+
# Use of this software is governed by the Business Source License
4+
# included in the LICENSE file at the root of this repository.
5+
#
6+
# As of the Change Date specified in that file, in accordance with
7+
# the Business Source License, use of this software will be governed
8+
# by the Apache License, Version 2.0.
9+
10+
"""Reads on a replica with the interactive compute runtime.
11+
12+
Every scenario here provisions its cluster with `enable_compute_interactive_runtime` on, so peeks
13+
and peek dataflows run on the second runtime and read the maintenance runtime's published
14+
arrangements. The flag is read when a replica is provisioned, and the benchmark boots a fresh
15+
instance per scenario, so `init` sets it before creating the cluster and nothing else observes the
16+
setting.
17+
"""
18+
19+
from materialize.feature_benchmark.action import Action, TdAction
20+
from materialize.feature_benchmark.measurement_source import MeasurementSource, Td
21+
from materialize.feature_benchmark.scenario import Scenario
22+
from materialize.mz_version import MzVersion
23+
24+
25+
class InteractiveRuntime(Scenario):
26+
"""Reads whose placement the interactive runtime changes. Group parent."""
27+
28+
@classmethod
29+
def can_run(cls, version: MzVersion) -> bool:
30+
# Requires `enable_compute_interactive_runtime`, which lands in 26.41.0. Skips on both
31+
# sides while we are still on `26.41.0-dev.0`, because `MzVersion` strips the commit
32+
# and cannot distinguish dev builds with the flag from dev builds without it.
33+
return version > MzVersion.create(26, 41, 0)
34+
35+
def cluster(self) -> TdAction:
36+
"""Provisions cluster `ir` with the interactive runtime on."""
37+
return TdAction(f"""
38+
$ postgres-execute connection=mz_system
39+
ALTER SYSTEM SET enable_compute_interactive_runtime = true;
40+
41+
> CREATE CLUSTER ir SIZE 'scale={self._default_size},workers=1', REPLICATION FACTOR 1
42+
""")
43+
44+
45+
class PeekDataflowJoin(InteractiveRuntime):
46+
"""A join over two indexed views, repeated. Neither input can take the fast path, so each
47+
query builds a peek dataflow that imports both indexes, which is the cost of a temporary
48+
dataflow on the runtime that serves it."""
49+
50+
SCALE = 5
51+
REPEAT = 10
52+
53+
def init(self) -> list[Action]:
54+
return [
55+
self.cluster(),
56+
self.table_ten(),
57+
TdAction(f"""
58+
> SET cluster = ir
59+
60+
> CREATE MATERIALIZED VIEW v1 AS SELECT {self.unique_values()} AS f1 FROM {self.join()}
61+
62+
> CREATE MATERIALIZED VIEW v2 AS SELECT {self.unique_values()} AS f1 FROM {self.join()}
63+
64+
> CREATE DEFAULT INDEX ON v1
65+
66+
> CREATE DEFAULT INDEX ON v2
67+
68+
> SELECT count(*) FROM v1 JOIN v2 USING (f1)
69+
{self.n()}
70+
"""),
71+
]
72+
73+
def benchmark(self) -> MeasurementSource:
74+
joins = "\n".join(
75+
f"> SELECT count(*) FROM v1 JOIN v2 USING (f1)\n{self.n()}\n"
76+
for _ in range(self.REPEAT)
77+
)
78+
return Td(f"""
79+
> SET cluster = ir
80+
81+
> SELECT 1
82+
/* A */
83+
1
84+
85+
{joins}
86+
87+
> SELECT 1
88+
/* B */
89+
1
90+
""")
91+
92+
93+
class PointLookup(InteractiveRuntime):
94+
"""A literal lookup on an indexed view, repeated. On the interactive runtime the walk reads
95+
the arrangement the maintenance runtime published rather than a local trace."""
96+
97+
REPEAT = 1000
98+
99+
def init(self) -> list[Action]:
100+
return [
101+
self.cluster(),
102+
self.table_ten(),
103+
TdAction(f"""
104+
> SET cluster = ir
105+
106+
> CREATE MATERIALIZED VIEW v1 AS SELECT {self.unique_values()} AS f1 FROM {self.join()}
107+
108+
> CREATE DEFAULT INDEX ON v1
109+
110+
> SELECT count(*) = {self.n()} FROM v1
111+
true
112+
"""),
113+
]
114+
115+
def benchmark(self) -> MeasurementSource:
116+
lookups = "\n".join(
117+
"> SELECT * FROM v1 WHERE f1 = 1\n1\n" for _ in range(self.REPEAT)
118+
)
119+
return Td(f"""
120+
> SET cluster = ir
121+
122+
> SET auto_route_introspection_queries TO false
123+
124+
> BEGIN
125+
126+
> SELECT 1
127+
/* A */
128+
1
129+
130+
{lookups}
131+
132+
> SELECT 1
133+
/* B */
134+
1
135+
""")
136+
137+
138+
class CreateIndexPublish(InteractiveRuntime):
139+
"""CREATE INDEX plus the first read that uses it. A publishing runtime installs a publisher
140+
per arrangement, and the first read on the interactive runtime waits for its publication.
141+
"""
142+
143+
def init(self) -> list[Action]:
144+
return [
145+
self.cluster(),
146+
self.table_ten(),
147+
TdAction(f"""
148+
> SET cluster = ir
149+
150+
> CREATE TABLE t1 (f1 INTEGER, f2 INTEGER)
151+
152+
> INSERT INTO t1 (f1) SELECT {self.unique_values()} FROM {self.join()}
153+
154+
> SELECT 1 FROM t1 WHERE f1 = 0
155+
1
156+
"""),
157+
]
158+
159+
def benchmark(self) -> MeasurementSource:
160+
return Td("""
161+
> SET cluster = ir
162+
163+
> DROP INDEX IF EXISTS i1
164+
/* A */
165+
166+
> CREATE INDEX i1 ON t1(f1)
167+
168+
> SELECT count(*) FROM t1 AS a1, t1 AS a2 WHERE a1.f1 = a2.f1 AND a1.f1 = 0 AND a2.f1 = 0
169+
/* B */
170+
1
171+
""")
172+
173+
174+
class IntrospectionRead(InteractiveRuntime):
175+
"""A read of a per-replica introspection relation, repeated. The interactive runtime serves it
176+
from the maintenance runtime's published logging index."""
177+
178+
REPEAT = 100
179+
180+
def init(self) -> list[Action]:
181+
return [
182+
self.cluster(),
183+
self.table_ten(),
184+
TdAction(f"""
185+
> SET cluster = ir
186+
187+
> CREATE MATERIALIZED VIEW v1 AS SELECT {self.unique_values()} AS f1 FROM {self.join()}
188+
189+
> CREATE DEFAULT INDEX ON v1
190+
191+
> SELECT count(*) = {self.n()} FROM v1
192+
true
193+
"""),
194+
]
195+
196+
def benchmark(self) -> MeasurementSource:
197+
reads = "\n".join(
198+
"> SELECT count(*) > 0 FROM mz_introspection.mz_dataflow_arrangement_sizes\ntrue\n"
199+
for _ in range(self.REPEAT)
200+
)
201+
return Td(f"""
202+
> SET cluster = ir
203+
204+
> SELECT 1
205+
/* A */
206+
1
207+
208+
{reads}
209+
210+
> SELECT 1
211+
/* B */
212+
1
213+
""")

0 commit comments

Comments
 (0)