[PEAK] PROPOSAL: Performance tracking service for PEAK
Phillip J. Eby
pje at telecommunity.com
Thu Dec 18 20:11:53 EST 2003
Sometimes it's important to be able to measure performance of a system,
perhaps one that's in "active duty" like a web application. I'd like to
add a 'running.IPerformanceService' interface (and implementation) to PEAK
for this.
The basic idea is that the service will offer the ability to create
timers. A timer has a dotted name (key), just like a logger or a
property. The timer has the ability to add arbitrary key/value pairs to
help identify what is being measured (such as a hit number, user ID, SQL
snippet, etc.), and a 'stop()' method to record the stop time.
So, the interfaces might be something like:
class IPerformanceService(Interface):
def getTimer(key):
"""Return timer named by 'key'"""
def addListener(key,listener):
"""Add a listener for measurements of 'key' (may be wildcard)"""
def addStartListener(key,listener):
"""Add a listener for *start* events on 'key' (may be wildcard)"""
class IPerformanceTimer(Interface):
def start(**info):
"""Start timing after setting info (like 'reset(); resume()')"""
def resume(**info):
"""Resume timing (i.e. don't reset) after adding info"""
def reset():
"""Reset the timer to zero clear info""""
def stop():
"""Stop timing"""
def addInfo(**info):
"""Update info"""
def addListener(listener):
"""Add a listener for measurements of this timer"""
def addStartListener(listener):
"""Add a listener for *start* events on this timer"""
class IPerformanceListener(Interface):
def timerStopped(service,key,elapsed,processor,info):
"""Timer 'key' of 'service' stopped with specified performance+info"""
class IPerformanceStartListener(Interface):
def timerStarting(service,key,elapsed,processor,info):
"""Timer 'key' of 'service' about to start"""
The idea here is that you'll define listeners to do things with the
performance counts. If your program doesn't need instrumentation, there
will be no listeners, and the timer methods will be no-ops. However, by
changing configuration and restarting, you'll be able to have listeners
receive performance data and "do something" about it.
For our current web application monitoring tool, our apps write out a
special file on each web hit that indicates the status of that application,
logs SQL and other performance-related parameters. The mechanism is
awkward, and hardwired into both the monitoring tool and the
applications. The interfaces above are intended to let us decouple
this. What we should be able to do is write listeners that write out the
existing file format upon the starting or completion of various designated
timer keys. Then, we could create additional listeners to do other
things. For example, we could create listeners that would dump performance
data to a database, so we could report on and analyze the
information. Such listeners could wait until the process is between web
requests to dump out the data, so that a request in progress is only slowed
down during the time it takes to accumulate the data in memory.
There are a number of open issues here still. For example, what happens if
a timer isn't stopped, due to an error? I don't think we want to force the
use of try-finally blocks, but the alternative requires that we simply
ignore timing in progress whenever we resume or reset an already-running
timer. The majority of listeners will listen to stop events, not start
events, so this is no big deal. We'll simply have to say that receiving a
start event doesn't guarantee that the timer will also produce a stop event.
Note that timers should use whether there are any *stop* listeners to
decide whether they're a no-op. Start listeners don't count, for the
simple reason that stops are what do the "measuring". So, if a timer has
no stop listeners, its start listeners will receive no messages either, and
in fact the start() and resume() methods will be complete no-ops in that
case. But the stop() method will always check the current time values
immediately, so as to minimize measurement error in the event that it is
actually going to do something with the measurements. (Although I suppose
there are ways to deal with that, too, like shunting the methods to
non-empty versions as soon as a stop listener is added to the timer. Or,
the timer class could be written in Pyrex for the absolute minimum overhead.)
The only other significant issue remaining I think is the issue of how to
configure the listeners. Ty and I have some ideas about that, too, but I'm
going to save them for another e-mail, as it's getting quite
late. (Similar issues exist for configuring other event-like mechanisms,
like logging, so hopefully we'll be able to resolve the issue across a
range of such kinds of services.)
In the meantime, your feedback on the proposal so far would be appreciated.
More information about the PEAK
mailing list