diff --git a/connection.py b/connection.py index 424b3874e..acb8c18e8 100644 --- a/connection.py +++ b/connection.py @@ -7,6 +7,7 @@ import traceback from errors import ConnectionFailure, InvalidName from database import Database +from cursor_manager import CursorManager class Connection(object): """A connection to Mongo. @@ -30,9 +31,24 @@ class Connection(object): self.__host = host self.__port = port self.__id = 1 + self.__cursor_manager = CursorManager(self) self.__connect() + def set_cursor_manager(self, manager_class): + """Set this connections cursor manager. + + Raises TypeError if manager_class is not a subclass of CursorManager. A + cursor manager handles closing cursors. Different managers can implement + different policies in terms of when to actually kill a cursor that has + been closed. + """ + manager = manager_class(self) + if not isinstance(manager, CursorManager): + raise TypeError("manager_class must be a subclass of CursorManager") + + self.__cursor_manager = manager + def host(self): """Get the connection host. """ @@ -136,6 +152,37 @@ class Connection(object): """ return self.__getattr__(name) + def close_cursor(self, cursor_id): + """Close a single database cursor. + + Raises TypeError if cursor_id is not an instance of (int, long). What + closing the cursor actually means depends on this connection's cursor + manager. + + Arguments: + - `cursor_id`: cursor id to close + """ + if not isinstance(cursor_id, (types.IntType, types.LongType)): + raise TypeError("cursor_id must be an instance of (int, long)") + + self.__cursor_manager.close(cursor_id) + + def kill_cursors(self, cursor_ids): + """Kill database cursors with the given ids. + + Raises TypeError if cursor_ids is not an instance of list. + + Arguments: + - `cursor_ids`: list of cursor ids to kill + """ + if not isinstance(cursor_ids, types.ListType): + raise TypeError("cursor_ids must be a list") + message = "\x00\x00\x00\x00" + message += struct.pack(" self.__max_dying_cursors: + print "killing cursors" + self.__connection.kill_cursors(self.__dying_cursors) + self.__dying_cursors = [] diff --git a/mongo.py b/mongo.py index aa7d87aa0..7ee14c1ee 100644 --- a/mongo.py +++ b/mongo.py @@ -12,8 +12,7 @@ from connection import Connection from son import SON from objectid import ObjectId from dbref import DBRef - -_MAX_DYING_CURSORS = 20 +from cursor_manager import BatchCursorManager ASCENDING = 1 DESCENDING = -1 @@ -45,25 +44,13 @@ class Mongo(Database): if not isinstance(settings, types.DictType): raise TypeError("settings must be an instance of dict") - self.__dying_cursors = [] self.__auto_dereference = settings.get("auto_dereference", False) self.__auto_reference = settings.get("auto_reference", False) - Database.__init__(self, Connection(host, port), name) + connection = Connection(host, port) + connection.set_cursor_manager(BatchCursorManager) - def _kill_cursors(self): - message = "\x00\x00\x00\x00" - message += struct.pack(" _MAX_DYING_CURSORS: - self._kill_cursors() + Database.__init__(self, connection, name) def __repr__(self): return "Mongo(%r, %r, %r)" % (self.name(), self.connection().host(), self.connection().port()) diff --git a/test/test_mongo.py b/test/test_mongo.py index 930bf4d36..49918fcb5 100644 --- a/test/test_mongo.py +++ b/test/test_mongo.py @@ -10,7 +10,7 @@ from dbref import DBRef from son import SON from errors import InvalidOperation, ConnectionFailure from collection import SYSTEM_INDEX_COLLECTION -from mongo import Mongo, ASCENDING, DESCENDING, _MAX_DYING_CURSORS +from mongo import Mongo, ASCENDING, DESCENDING class TestMongo(unittest.TestCase): def setUp(self): @@ -124,7 +124,7 @@ class TestMongo(unittest.TestCase): self.assertEqual(1000, count) # test that kill cursors doesn't assert or anything - for _ in xrange(3 * _MAX_DYING_CURSORS + 2): + for _ in xrange(62): for _ in db.test.find(): break