Skip to content

Commit 5207686

Browse files
committed
Add example request init listener
PYTHON-284
1 parent 686ccf1 commit 5207686

3 files changed

Lines changed: 118 additions & 0 deletions

File tree

cassandra/cluster.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2140,6 +2140,9 @@ def add_request_init_listener(self, fn, *args, **kwargs):
21402140
synchronization if you have multiple threads. Any callbacks added to the response future will be executed
21412141
on the event loop thread, so the normal advice about minimizing cycles and avoiding blocking apply (see Note in
21422142
:meth:`.ResponseFuture.add_callbacks`.
2143+
2144+
See `this example <https://github.com/datastax/python-driver/blob/master/examples/request_init_listener.py>`_ in the
2145+
source tree for an example.
21432146
"""
21442147
self._request_init_callbacks.append((fn, args, kwargs))
21452148

examples/README.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Driver Examples
2+
===============
3+
This directory will contain a set of scripts demonstrating driver APIs or integration techniques. It will not be exhaustive, but will contain examples where they are too involved, or
4+
open-ended to include inline in the docstrings. In that case, they should be referenced from the docstrings
5+
6+
Features
7+
--------
8+
* `request_init_listener.py <request_init_listener.py>`_ A script demonstrating how to register a session request listener and use it to track alternative metrics about requests (size, for example).

examples/request_init_listener.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env python
2+
# Copyright 2013-2016 DataStax, Inc.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# This script shows an example "request init listener" which can be registered to track certain request metrics
17+
# for a session. In this case we're just accumulating total request and error counts, as well as some statistics
18+
# about the encoded request size. Note that the counts would be available using the internal 'metrics' tracking --
19+
# this is just demonstrating a way to track a few custom attributes.
20+
21+
from __future__ import print_function
22+
from cassandra.cluster import Cluster
23+
from greplin import scales
24+
25+
import pprint
26+
pp = pprint.PrettyPrinter(indent=2)
27+
28+
29+
class RequestAnalyzer(object):
30+
"""
31+
Class used to track request and error counts for a Session.
32+
33+
Also computes statistics on encoded request size.
34+
"""
35+
36+
requests = scales.PmfStat('request size')
37+
errors = scales.IntStat('errors')
38+
39+
def __init__(self, session):
40+
scales.init(self, '/cassandra')
41+
# each instance will be registered with a session, and receive a callback for each request generated
42+
session.add_request_init_listener(self.on_request)
43+
44+
def on_request(self, rf):
45+
# This callback is invoked each time a request is created, on the thread creating the request.
46+
# We can use this to count events, or add callbacks
47+
rf.add_callbacks(self.on_success, self.on_error, callback_args=(rf,), errback_args=(rf,))
48+
49+
def on_success(self, _, response_future):
50+
# future callback on a successful request; just record the size
51+
self.requests.addValue(response_future.request_encoded_size)
52+
53+
def on_error(self, _, response_future):
54+
# future callback for failed; record size and increment errors
55+
self.requests.addValue(response_future.request_encoded_size)
56+
self.errors += 1
57+
58+
def __str__(self):
59+
# just extracting request count from the size stats (which are recorded on all requests)
60+
request_sizes = dict(self.requests)
61+
count = request_sizes.pop('count')
62+
return "%d requests (%d errors)\nRequest size statistics:\n%s" % (count, self.errors, pp.pformat(request_sizes))
63+
64+
65+
# connect a session
66+
session = Cluster().connect()
67+
68+
# attach a listener to this session
69+
ra = RequestAnalyzer(session)
70+
71+
session.execute("SELECT release_version FROM system.local")
72+
session.execute("SELECT release_version FROM system.local")
73+
74+
print(ra)
75+
# 2 requests (0 errors)
76+
# Request size statistics:
77+
# { '75percentile': 74,
78+
# '95percentile': 74,
79+
# '98percentile': 74,
80+
# '999percentile': 74,
81+
# '99percentile': 74,
82+
# 'max': 74,
83+
# 'mean': 74.0,
84+
# 'median': 74.0,
85+
# 'min': 74,
86+
# 'stddev': 0.0}
87+
88+
try:
89+
# intentional error to show that count increase
90+
session.execute("syntax err")
91+
except Exception as e:
92+
pass
93+
94+
print()
95+
print(ra) # note: the counts are updated, but the stats are not because scales only updates every 20s
96+
# 3 requests (1 errors)
97+
# Request size statistics:
98+
# { '75percentile': 74,
99+
# '95percentile': 74,
100+
# '98percentile': 74,
101+
# '999percentile': 74,
102+
# '99percentile': 74,
103+
# 'max': 74,
104+
# 'mean': 74.0,
105+
# 'median': 74.0,
106+
# 'min': 74,
107+
# 'stddev': 0.0}

0 commit comments

Comments
 (0)