r23923 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r23922‎ | r23923 | r23924 >
Date:21:39, 9 July 2007
Author:tstarling
Status:old
Tags:
Comment:
Added MySQL metrics
Modified paths:
  • /trunk/ganglia_metrics/GangliaMetrics.py (modified) (history)
  • /trunk/ganglia_metrics/MySQLStats.py (added) (history)
  • /trunk/ganglia_metrics/debian/changelog (modified) (history)
  • /trunk/ganglia_metrics/gmetricd.py (modified) (history)

Diff [purge]

Index: trunk/ganglia_metrics/GangliaMetrics.py
@@ -15,6 +15,8 @@
1616 self.tmax = 60
1717 self.dmax = 0
1818 self.interval = 10
 19+
 20+ self.value = 0
1921
2022 def isReady(self):
2123 return time.time() - self.lastSendTime >= self.interval
@@ -40,8 +42,11 @@
4143 self.lastSendTime = time.time()
4244
4345 def getValue(self):
44 - raise NotImplementedError
 46+ return self.value
4547
 48+ def set(self, value):
 49+ self.value = value
 50+
4651 """
4752 A metric which works by querying a system counter. The counter typically
4853 increases monotonically, but may occasionally overflow. The difference
@@ -83,7 +88,37 @@
8489 def getCounterValue(self):
8590 raise NotImplementedError
8691
 92+"""
 93+A rolling average metric
 94+"""
 95+class RollingMetric(Metric):
 96+ def __init__(self, name, avPeriod = 60, units = '', type = 'double'):
 97+ Metric.__init__(self, name, units, type)
 98+ self.queue = []
 99+ self.sum = 0
 100+ self.targetSize = avPeriod / self.interval
 101+ self.head = 0
87102
 103+ def getValue(self):
 104+ if len(self.queue) == 0:
 105+ return None
 106+ else:
 107+ return float(self.sum) / len(self.queue)
 108+
 109+ def set(self, value):
 110+ if value == None:
 111+ self.queue = []
 112+ return
 113+
 114+ self.sum += value
 115+ if len(self.queue) == self.targetSize:
 116+ self.head = (self.head + 1) % self.targetSize
 117+ self.sum -= self.queue[self.head]
 118+ self.queue[self.head] = value
 119+ else:
 120+ self.queue.append(value)
 121+
 122+
88123 """
89124 A metric which averages pushed values over the polling period
90125 If no value is pushed during a given polling interval, the previous average is returned
Index: trunk/ganglia_metrics/debian/changelog
@@ -1,3 +1,9 @@
 2+ganglia-metrics (1.1-1) edgy; urgency=low
 3+
 4+ * Added MySQL metrics
 5+
 6+ -- Tim Starling <tstarling@wikimedia.org> Mon, 9 Jul 2007 19:30:00 +0000
 7+
28 ganglia-metrics (1.0-2) edgy; urgency=medium
39
410 * Add dependency on gmond so install doesn't fail
Index: trunk/ganglia_metrics/gmetricd.py
@@ -1,7 +1,7 @@
22 #! /usr/bin/env python
33
44 from xdrlib import Packer
5 -import sys, socket, re, GangliaMetrics, time, os, signal, pwd, logging
 5+import sys, socket, re, GangliaMetrics, MySQLStats, time, os, signal, pwd, logging, commands
66 from SelectServer import *
77
88 # Configuration
@@ -11,7 +11,9 @@
1212 'sock': '/tmp/gmetric.sock',
1313 'log': '/var/log/gmetricd/gmetricd.log',
1414 'pid': '/var/run/gmetricd.pid',
15 - 'user': 'gmetric'
 15+ 'user': 'gmetric',
 16+ 'dbuser': 'wikiadmin',
 17+ 'dbpassword': commands.getoutput('/home/wikipedia/bin/wikiadmin_pass')
1618 }
1719
1820 unixSocket = None
@@ -161,8 +163,10 @@
162164 # Create the metrics
163165 diskStats = GangliaMetrics.DiskStats()
164166 pushMetrics = GangliaMetrics.MetricCollection()
165 -allMetrics = (diskStats, pushMetrics)
166167
 168+mysqlStats = MySQLStats.MySQLStats( conf['dbuser'], conf['dbpassword'] )
 169+allMetrics = (diskStats, pushMetrics, mysqlStats)
 170+
167171 # Daemonize
168172 pid = os.fork()
169173 if pid != 0:
Index: trunk/ganglia_metrics/MySQLStats.py
@@ -0,0 +1,98 @@
 2+import logging, commands, time
 3+from GangliaMetrics import *
 4+from xml.dom.minidom import parseString
 5+
 6+"""
 7+A collection of metrics from MySQL, using SHOW STATUS and SHOW PROCESSLIST
 8+"""
 9+class MySQLStats(MetricCollection):
 10+ def __init__(self, user, password):
 11+ self.metrics = {
 12+ 'mysql_questions': DeltaMetricItem('mysql_questions', 'q/s'),
 13+ 'mysql_threads_connected': RollingMetric('mysql_threads_connected', 60),
 14+ 'mysql_threads_running': RollingMetric('mysql_threads_running', 60),
 15+ 'mysql_slave_lag': Metric('mysql_slave_lag', 's')
 16+ }
 17+ self.user = user
 18+ self.password = password
 19+ self.pipes = None
 20+
 21+ if self.query('select 1') == None:
 22+ self.disabled = True
 23+ else:
 24+ self.disabled = False
 25+ logger = logging.getLogger('GangliaMetrics')
 26+ logger.warning('Unable to run query, disabling MySQL statistics')
 27+
 28+ def update(self):
 29+ if disabled:
 30+ return False
 31+
 32+ refTime = time.time()
 33+ status = self.showStatus()
 34+ if not status:
 35+ self.markDown()
 36+ return False
 37+
 38+ lag = self.getLag()
 39+
 40+ self.metrics['mysql_questions'].set(int(status['Questions']), refTime)
 41+ self.metrics['mysql_threads_connected'].set(int(status['Threads_connected']))
 42+ self.metrics['mysql_threads_running'].set(int(status['Threads_running']))
 43+ self.metrics['mysql_slave_lag'].set(float(lag)) # float = wishful thinking
 44+ return True
 45+
 46+ def escapeshellarg(self, s):
 47+ return s.replace( "\\", "\\\\").replace( "'", "'\\''")
 48+
 49+ def query(self, sql):
 50+ out = commands.getoutput("mysql -XB -u '%s' -p'%s' -e '%s'" % (
 51+ self.escapeshellarg(self.user),
 52+ self.escapeshellarg(self.password),
 53+ self.escapeshellarg(sql)
 54+ ))
 55+ try:
 56+ dom = parseString(out)
 57+ except:
 58+ logger = logging.getLogger('GangliaMetrics')
 59+ logger.warning("SQL error: Unable to parse XML result\n")
 60+ return None
 61+ return dom
 62+
 63+ def markDown(self):
 64+ self.metrics['mysql_questions'].set(None)
 65+ self.metrics['mysql_threads_connected'].set(None)
 66+ self.metrics['mysql_threads_running'].set(None)
 67+ self.metrics['mysql_slave_lag'].set(None)
 68+ self.conn = None
 69+
 70+ def showStatus(self):
 71+ result = self.query("SHOW STATUS")
 72+ if not result:
 73+ return None
 74+
 75+ resultHash = {}
 76+ for row in result.documentElement.getElementsByTagName('row'):
 77+ name = row.getElementsByTagName('Variable_name')[0].childNodes[0].data
 78+ value = row.getElementsByTagName('Value')[0].childNodes[0].data
 79+ resultHash[name] = value
 80+ return resultHash
 81+
 82+ def getLag(self):
 83+ result = self.query("SHOW PROCESSLIST")
 84+ if not result:
 85+ return None
 86+
 87+ for row in result.documentElement.getElementsByTagName('row'):
 88+ user = row.getElementsByTagName('User')[0].childNodes[0].data
 89+ time = row.getElementsByTagName('Time')[0].childNodes[0].data
 90+ state = row.getElementsByTagName('State')[0].childNodes[0].data
 91+ if user == 'system user' and \
 92+ state != 'Waiting for master to send event' and \
 93+ state != 'Connecting to master' and \
 94+ state != 'Queueing master event to the relay log' and \
 95+ state != 'Waiting for master update' and \
 96+ state != 'Requesting binlog dump':
 97+ return time
 98+ return None
 99+
Property changes on: trunk/ganglia_metrics/MySQLStats.py
___________________________________________________________________
Name: svn:eol-style
1100 + native

Status & tagging log