Command Server

Michal Gornisiewicz michal-bzr at mxg.id.au
Mon Nov 27 08:39:20 GMT 2006


Hi all,


I created a quick hack of a "command server" plugin which speeds up bzr
for really basic operations - making things /feel/ more responsive.  The
idea is similar to the shell plugin, but without having to use a
different shell.

Instead you start a server process which listens on a UNIX socket and
instead of bzr, run a script ("fbzr") which connects to the server and
just passes the command line arguments.

This should work with most commands, but I've only tested with a few
simple ones (diff, log, status).

I thought I'd post it here in case someone finds this useful.


Some example timings:
bzr rocks  0.88s user 0.09s system 100% cpu 0.970 total
fbzr.py rocks  0.04s user 0.02s system 100% cpu 0.060 total

bzr status  1.24s user 0.17s system 100% cpu 1.408 total
fbzr.py status  0.05s user 0.01s system 15% cpu 0.386 total


Caveats:
1. You need to use bzr (not fbzr) to control the command server.
2. Only stdout (not stderr) is passed from the server.
	eg. --version won't show anything
3. The environment used is that of the server, not client (except for
	the working dir).
4. No bells or whistles whatsoever.


Attached:
__init__.py	- the plugin
fbzr.py		- run instead of bzr



Michal

PS - I'm NOT subscribed to the list.
-------------- next part --------------
# Copyright (C) 2006 Michal Gornisiewicz <michal-bzr at mxg.id.au>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License ONLY.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

"""
A command server, for speeding up bzr operations.
"""
import os
import cPickle as pickle
import socket
import sys

import bzrlib.commands
from bzrlib.commands import Command, register_command, display_command, Option

SOCKET_FN = "%s/.bazaar/cmdsrv" %os.getenv("HOME")

class cmd_command_server(Command):
	"""Controls the command server.
	"""
	aliases = ["cmdsrv"]
	takes_args = ['subcommand']

	def __init__(self):
		self._sc_funs = {
			"start": self.start_server,
			"stop": self.stop_server,
			"status": self.status_server,
		}

	def help(self):
		text = ["%s\nSubcommands:" %self.__doc__]
		subcoms = [
			("start", "Start the command server."),
			("stop", "Stop the command server."),
			("status", "Check if the command server is running.")
		]
		indent = 2*" "
		for n, d in subcoms:
			text.append("%s%s" %(indent, n))
			text.append("%s%s" %(2*indent, d))
			text.append("")

		return "\n".join(text)


	def run(self, subcommand):
		fun = self._sc_funs.get(subcommand)
		if not fun:
			raise CommandError("Unknown command-server command"\
					" '%s'." %subcommand)
		fun()


	def start_server(self):
		# TODO: check if socket already exists
		pid = os.fork()
		if 0 < pid:
			return
		os.chdir("/")
		os.setsid()
		os.umask(0)
		pid = os.fork()
		if 0 < pid:
			return

		s = socket.socket(socket.AF_UNIX)
		try:
			s.bind(SOCKET_FN)
			s.listen(1)
			while self._process_command(s):
				pass
		finally:
			s.close()
			os.unlink(SOCKET_FN)


	def _process_command(self, s):
		result = True
		conn = None
		try:
			real_stdout = sys.stdout
			conn, addr = s.accept()

			client = conn.makefile()
			dir = pickle.load(client)
			if dir == "STOP":
				result = False
			elif dir == "STATUS":
				client.write("Running.")
				client.flush()
			else:
				argv = pickle.load(client)
				os.chdir(dir)
				sys.stdout = client
				bzrlib.commands.main(argv)
		finally:
			sys.stdout = real_stdout
			os.chdir("/")
			if conn:
				conn.shutdown(2)
		return result


	def stop_server(self):
		try:
			s = socket.socket(socket.AF_UNIX)
			s.connect(SOCKET_FN)
			s.send(pickle.dumps("STOP"))
		except socket.error:
			print "Can't connect to command server.  Is it running?"


	def status_server(self):
		try:
			s = socket.socket(socket.AF_UNIX)
			s.connect(SOCKET_FN)
			s.send(pickle.dumps("STATUS"))
			data = s.recv(4096)
			while data:
				sys.stdout.write("%s\n" %data)
				data = s.recv(4096)
		except socket.error:
			print "Not running."


register_command(cmd_command_server)

if __name__ == '__main__':
	print "This is a Bazaar plugin. Copy this directory to"\
			" ~/.bazaar/plugins to use it.\n"
else:
	sys.path.append(os.path.dirname(__file__))
-------------- next part --------------
#!/usr/bin/python

# Copyright (C) 2006 Michal Gornisiewicz <michal-bzr at mxg.id.au>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License ONLY.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import os
import cPickle as pickle
import socket
import sys

def main(argv):
	s = socket.socket(socket.AF_UNIX)
	fn = "%s/.bazaar/cmdsrv" %os.getenv("HOME")
	s.connect(fn)

	server = s.makefile()
	dir = os.getcwd()
	s.send(pickle.dumps(dir))
	s.send(pickle.dumps(sys.argv))
	data = s.recv(4096)
	while data:
		sys.stdout.write(data)
		data = s.recv(4096)


if "__main__" == __name__:
	main(sys.argv)


More information about the bazaar mailing list