Skip to content

Commit

Permalink
Added a new ruok client protocol for getting server status on
Browse files Browse the repository at this point in the history
the ZEO port without creating a full-blown client connection and
without logging in the server log.
  • Loading branch information
Jim Fulton committed Jan 4, 2015
1 parent dd55476 commit d508253
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ Changelog

- 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.


Expand Down
13 changes: 9 additions & 4 deletions src/ZEO/StorageServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ def iterator_gc(self, iids):
self._iterators.pop(iid, None)

def server_status(self):
return self.server.server_status(self)
return self.server.server_status(self.storage_id)

def set_client_label(self, label):
self.log_label = str(label)+' '+_addr_label(self.connection.addr)
Expand Down Expand Up @@ -992,7 +992,7 @@ def new_connection(self, sock, addr):
zstorage = self.ZEOStorageClass(self, self.read_only)

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

def register_connection(self, storage_id, conn):
Expand Down Expand Up @@ -1303,14 +1303,19 @@ def already_waiting(self, zeostore):
with self._lock:
return bool([i for i in waiting if i[0] is zeostore])

def server_status(self, zeostore):
storage_id = zeostore.storage_id
def server_status(self, storage_id):
status = self.stats[storage_id].__dict__.copy()
status['connections'] = len(status['connections'])
status['waiting'] = len(self._waiting[storage_id])
status['timeout-thread-is-alive'] = self.timeouts[storage_id].isAlive()
status['last-transaction'] = (
self.storages[storage_id].lastTransaction().encode('hex'))
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):
if len(waiting) > 9:
return logging.CRITICAL
Expand Down
36 changes: 34 additions & 2 deletions src/ZEO/tests/testZEO.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import logging
import os
import persistent
import pprint
import re
import shutil
import signal
Expand Down Expand Up @@ -1299,14 +1300,14 @@ def test_server_status():
>>> addr, _ = start_server(zeo_conf=dict(transaction_timeout=1))
>>> db = ZEO.DB(addr)
>>> import pprint
>>> pprint.pprint(db.storage.server_status(), width=40)
{'aborts': 0,
'active_txns': 0,
'commits': 1,
'conflicts': 0,
'conflicts_resolved': 0,
'connections': 1,
'last-transaction': '03ac11b771fa1c00',
'loads': 1,
'lock_time': None,
'start': 'Tue May 4 10:55:20 2010',
Expand All @@ -1318,6 +1319,35 @@ def test_server_status():
>>> 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():
"""
When looking at server logs, for servers with lots of clients coming
Expand Down Expand Up @@ -1754,7 +1784,9 @@ def test_suite():
zeo = unittest.TestSuite()
zeo.addTest(unittest.makeSuite(ZODB.tests.util.AAAA_Test_Runner_Hack))
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.POSKeyError"), "POSKeyError"),
(re.compile("ZEO.Exceptions.ClientStorageError"), "ClientStorageError"),
Expand Down
1 change: 1 addition & 0 deletions src/ZEO/tests/testZEO2.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ def some_basic_locking_tests():
'conflicts': 0,
'conflicts_resolved': 0,
'connections': 11,
'last-transaction': '0000000000000000',
'loads': 0,
'lock_time': 1272653598.693882,
'start': 'Fri Apr 30 14:53:18 2010',
Expand Down
10 changes: 8 additions & 2 deletions src/ZEO/zrpc/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
##############################################################################
import asyncore
import errno
import json
import sys
import threading
import logging
Expand Down Expand Up @@ -631,8 +632,13 @@ def handshake(self):
self.message_output(self.current_protocol)

def recv_handshake(self, proto):
Connection.recv_handshake(self, proto)
self.obj.notifyConnected(self)
if proto == 'ruok':
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):
self.obj.notifyDisconnected()
Expand Down

0 comments on commit d508253

Please sign in to comment.