Skip to content

Commit 72aeb59

Browse files
committed
bench: add micro-benchmark for was_applied fast path
Construct a minimal ResultSet with a mocked response_future and real cassandra.query statement objects, and time actual accesses to rs.was_applied, instead of re-implementing a simplified stand-in for its fast-path/slow-path branching. This also means the slow path exercises the real ResultSet.batch_regex instead of a different, looser regex, so the reported cost reflects the real regex match. On this machine: ~0.21us/call for the fast path (known-LWT BoundStatement) vs ~0.35us/call for the slow path (SimpleStatement regex match), a ~1.7x speedup -- both call costs are far below a microsecond once measured against the real was_applied property instead of Mock-heavy stand-ins.
1 parent 85f85d9 commit 72aeb59

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

benchmarks/bench_was_applied.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copyright DataStax, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
Micro-benchmark: was_applied fast path for known LWT statements.
17+
18+
Measures the speedup from skipping regex batch detection when the
19+
query already knows it's an LWT statement (is_lwt() returns True).
20+
21+
This benchmarks the real `cassandra.cluster.ResultSet.was_applied` property
22+
(not a simplified stand-in for it): a minimal `ResultSet` is constructed with
23+
a mocked `response_future` -- was_applied only reads
24+
`response_future.row_factory`/`response_future.query` -- and real
25+
`cassandra.query` statement objects, so both the fast-path `is_lwt()` check
26+
and the slow-path `ResultSet.batch_regex` match run exactly as they do in
27+
production.
28+
29+
Run:
30+
python benchmarks/bench_was_applied.py
31+
"""
32+
import timeit
33+
from unittest.mock import Mock
34+
35+
from cassandra.cluster import ResultSet
36+
from cassandra.query import named_tuple_factory, SimpleStatement, PreparedStatement, BoundStatement
37+
38+
39+
def _make_result_set(query, row):
40+
"""Build a minimal ResultSet with a mocked response_future, mirroring what
41+
Session.execute()/ResponseFuture.result() construct in production."""
42+
response_future = Mock(row_factory=named_tuple_factory, query=query,
43+
_col_names=None, _col_types=None)
44+
return ResultSet(response_future, [row])
45+
46+
47+
def bench_was_applied():
48+
"""Benchmark ResultSet.was_applied: fast path vs slow path."""
49+
# Fast path: a BoundStatement bound from a PreparedStatement whose LWT
50+
# status was already resolved from the server's PREPARE response, so
51+
# was_applied can skip batch/regex detection entirely.
52+
prepared = PreparedStatement(
53+
column_metadata=None, query_id=b'\x00', routing_key_indexes=None,
54+
query="UPDATE t SET v=1 WHERE k=1 IF v=0", keyspace=None,
55+
protocol_version=4, result_metadata=None, result_metadata_id=None,
56+
is_lwt=True)
57+
lwt_query = BoundStatement(prepared)
58+
fast_rs = _make_result_set(lwt_query, (True,))
59+
60+
# Slow path: a plain SimpleStatement with unknown LWT status, so
61+
# was_applied must match the query string against the real
62+
# ResultSet.batch_regex to rule out a BEGIN BATCH.
63+
non_lwt_query = SimpleStatement("INSERT INTO t (k, v) VALUES (1, 2) IF NOT EXISTS")
64+
slow_rs = _make_result_set(non_lwt_query, (True,))
65+
66+
def fast_path():
67+
_ = fast_rs.was_applied
68+
69+
def slow_path():
70+
_ = slow_rs.was_applied
71+
72+
n = 500_000
73+
t_fast = timeit.timeit(fast_path, number=n)
74+
t_slow = timeit.timeit(slow_path, number=n)
75+
76+
print(f"Fast path (known LWT, {n} iters): {t_fast:.3f}s ({t_fast / n * 1e6:.2f} us/call)")
77+
print(f"Slow path (regex check, {n} iters): {t_slow:.3f}s ({t_slow / n * 1e6:.2f} us/call)")
78+
print(f"Speedup: {t_slow / t_fast:.1f}x")
79+
80+
81+
def main():
82+
bench_was_applied()
83+
84+
85+
if __name__ == '__main__':
86+
main()

0 commit comments

Comments
 (0)