Commit d5082536 authored by Jim Fulton's avatar Jim Fulton

Added a new ``ruok`` client protocol for getting server status on

the ZEO port without creating a full-blown client connection and
without logging in the server log.
parent dd55476f
...@@ -6,6 +6,10 @@ Changelog ...@@ -6,6 +6,10 @@ Changelog
- Add support for Python 3.4. - Add support for Python 3.4.
- Added a new ``ruok`` client protocol for getting server status on
the ZEO port without creating a full-blown client connection and
without logging in the server log.
- Log errors on server side even if using multi threaded delay. - Log errors on server side even if using multi threaded delay.
......
...@@ -753,7 +753,7 @@ class ZEOStorage: ...@@ -753,7 +753,7 @@ class ZEOStorage:
self._iterators.pop(iid, None) self._iterators.pop(iid, None)
def server_status(self): def server_status(self):
return self.server.server_status(self) return self.server.server_status(self.storage_id)
def set_client_label(self, label): def set_client_label(self, label):
self.log_label = str(label)+' '+_addr_label(self.connection.addr) self.log_label = str(label)+' '+_addr_label(self.connection.addr)
...@@ -992,7 +992,7 @@ class StorageServer: ...@@ -992,7 +992,7 @@ class StorageServer:
zstorage = self.ZEOStorageClass(self, self.read_only) zstorage = self.ZEOStorageClass(self, self.read_only)
c = self.ManagedServerConnectionClass(sock, addr, zstorage, self) c = self.ManagedServerConnectionClass(sock, addr, zstorage, self)
log("new connection %s: %s" % (addr, repr(c))) log("new connection %s: %s" % (addr, repr(c)), logging.DEBUG)
return c return c
def register_connection(self, storage_id, conn): def register_connection(self, storage_id, conn):
...@@ -1303,14 +1303,19 @@ class StorageServer: ...@@ -1303,14 +1303,19 @@ class StorageServer:
with self._lock: with self._lock:
return bool([i for i in waiting if i[0] is zeostore]) return bool([i for i in waiting if i[0] is zeostore])
def server_status(self, zeostore): def server_status(self, storage_id):
storage_id = zeostore.storage_id
status = self.stats[storage_id].__dict__.copy() status = self.stats[storage_id].__dict__.copy()
status['connections'] = len(status['connections']) status['connections'] = len(status['connections'])
status['waiting'] = len(self._waiting[storage_id]) status['waiting'] = len(self._waiting[storage_id])
status['timeout-thread-is-alive'] = self.timeouts[storage_id].isAlive() status['timeout-thread-is-alive'] = self.timeouts[storage_id].isAlive()
status['last-transaction'] = (
self.storages[storage_id].lastTransaction().encode('hex'))
return status return status
def ruok(self):
return dict((storage_id, self.server_status(storage_id))
for storage_id in self.storages)
def _level_for_waiting(waiting): def _level_for_waiting(waiting):
if len(waiting) > 9: if len(waiting) > 9:
return logging.CRITICAL return logging.CRITICAL
......
...@@ -35,6 +35,7 @@ import doctest ...@@ -35,6 +35,7 @@ import doctest
import logging import logging
import os import os
import persistent import persistent
import pprint
import re import re
import shutil import shutil
import signal import signal
...@@ -1299,7 +1300,6 @@ def test_server_status(): ...@@ -1299,7 +1300,6 @@ def test_server_status():
>>> addr, _ = start_server(zeo_conf=dict(transaction_timeout=1)) >>> addr, _ = start_server(zeo_conf=dict(transaction_timeout=1))
>>> db = ZEO.DB(addr) >>> db = ZEO.DB(addr)
>>> import pprint
>>> pprint.pprint(db.storage.server_status(), width=40) >>> pprint.pprint(db.storage.server_status(), width=40)
{'aborts': 0, {'aborts': 0,
'active_txns': 0, 'active_txns': 0,
...@@ -1307,6 +1307,7 @@ def test_server_status(): ...@@ -1307,6 +1307,7 @@ def test_server_status():
'conflicts': 0, 'conflicts': 0,
'conflicts_resolved': 0, 'conflicts_resolved': 0,
'connections': 1, 'connections': 1,
'last-transaction': '03ac11b771fa1c00',
'loads': 1, 'loads': 1,
'lock_time': None, 'lock_time': None,
'start': 'Tue May 4 10:55:20 2010', 'start': 'Tue May 4 10:55:20 2010',
...@@ -1318,6 +1319,35 @@ def test_server_status(): ...@@ -1318,6 +1319,35 @@ def test_server_status():
>>> db.close() >>> db.close()
""" """
def test_ruok():
"""
You can also get server status using the ruok protocol.
>>> addr, _ = start_server(zeo_conf=dict(transaction_timeout=1))
>>> db = ZEO.DB(addr) # force a transaction :)
>>> import json, socket, struct
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.connect(addr)
>>> _ = s.send(struct.pack(">I", 4)+"ruok")
>>> proto = s.recv(struct.unpack(">I", s.recv(4))[0])
>>> pprint.pprint(json.loads(s.recv(struct.unpack(">I", s.recv(4))[0])))
{u'1': {u'aborts': 0,
u'active_txns': 0,
u'commits': 1,
u'conflicts': 0,
u'conflicts_resolved': 0,
u'connections': 1,
u'last-transaction': u'03ac11cd11372499',
u'loads': 1,
u'lock_time': None,
u'start': u'Sun Jan 4 09:37:03 2015',
u'stores': 1,
u'timeout-thread-is-alive': True,
u'verifying_clients': 0,
u'waiting': 0}}
>>> db.close(); s.close()
"""
def client_labels(): def client_labels():
""" """
When looking at server logs, for servers with lots of clients coming When looking at server logs, for servers with lots of clients coming
...@@ -1754,7 +1784,9 @@ def test_suite(): ...@@ -1754,7 +1784,9 @@ def test_suite():
zeo = unittest.TestSuite() zeo = unittest.TestSuite()
zeo.addTest(unittest.makeSuite(ZODB.tests.util.AAAA_Test_Runner_Hack)) zeo.addTest(unittest.makeSuite(ZODB.tests.util.AAAA_Test_Runner_Hack))
patterns = [ patterns = [
(re.compile(r"'start': '[^\n]+'"), 'start'), (re.compile(r"u?'start': u?'[^\n]+'"), 'start'),
(re.compile(r"u?'last-transaction': u?'[0-9a-f]+'"),
'last-transaction'),
(re.compile("ZODB.POSException.ConflictError"), "ConflictError"), (re.compile("ZODB.POSException.ConflictError"), "ConflictError"),
(re.compile("ZODB.POSException.POSKeyError"), "POSKeyError"), (re.compile("ZODB.POSException.POSKeyError"), "POSKeyError"),
(re.compile("ZEO.Exceptions.ClientStorageError"), "ClientStorageError"), (re.compile("ZEO.Exceptions.ClientStorageError"), "ClientStorageError"),
......
...@@ -351,6 +351,7 @@ statistics using the server_status method: ...@@ -351,6 +351,7 @@ statistics using the server_status method:
'conflicts': 0, 'conflicts': 0,
'conflicts_resolved': 0, 'conflicts_resolved': 0,
'connections': 11, 'connections': 11,
'last-transaction': '0000000000000000',
'loads': 0, 'loads': 0,
'lock_time': 1272653598.693882, 'lock_time': 1272653598.693882,
'start': 'Fri Apr 30 14:53:18 2010', 'start': 'Fri Apr 30 14:53:18 2010',
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
############################################################################## ##############################################################################
import asyncore import asyncore
import errno import errno
import json
import sys import sys
import threading import threading
import logging import logging
...@@ -631,8 +632,13 @@ class ManagedServerConnection(Connection): ...@@ -631,8 +632,13 @@ class ManagedServerConnection(Connection):
self.message_output(self.current_protocol) self.message_output(self.current_protocol)
def recv_handshake(self, proto): def recv_handshake(self, proto):
Connection.recv_handshake(self, proto) if proto == 'ruok':
self.obj.notifyConnected(self) self.message_output(json.dumps(self.mgr.ruok()))
self.poll()
Connection.close(self)
else:
Connection.recv_handshake(self, proto)
self.obj.notifyConnected(self)
def close(self): def close(self):
self.obj.notifyDisconnected() self.obj.notifyDisconnected()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment