@@ -778,6 +778,9 @@ def close(self):
778778 If this instance is used again it will be automatically re-opened and
779779 the threads restarted.
780780 """
781+ # Run _process_periodic_tasks to send pending killCursor requests
782+ # before closing the topology.
783+ self ._process_periodic_tasks ()
781784 self ._topology .close ()
782785
783786 def set_cursor_manager (self , manager_class ):
@@ -1024,6 +1027,21 @@ def close_cursor(self, cursor_id, address=None):
10241027 else :
10251028 self .__kill_cursors_queue .append ((address , [cursor_id ]))
10261029
1030+ def _close_cursor_now (self , cursor_id , address = None ):
1031+ """Send a kill cursors message with the given id.
1032+
1033+ What closing the cursor actually means depends on this client's
1034+ cursor manager. If there is none, the cursor is closed synchronously
1035+ on the current thread.
1036+ """
1037+ if not isinstance (cursor_id , integer_types ):
1038+ raise TypeError ("cursor_id must be an instance of (int, long)" )
1039+
1040+ if self .__cursor_manager is not None :
1041+ self .__cursor_manager .close (cursor_id , address )
1042+ else :
1043+ self ._kill_cursors ([cursor_id ], address , self ._get_topology ())
1044+
10271045 def kill_cursors (self , cursor_ids , address = None ):
10281046 """DEPRECATED - Send a kill cursors message soon with the given ids.
10291047
@@ -1055,6 +1073,62 @@ def kill_cursors(self, cursor_ids, address=None):
10551073 # "Atomic", needs no lock.
10561074 self .__kill_cursors_queue .append ((address , cursor_ids ))
10571075
1076+ def _kill_cursors (self , cursor_ids , address , topology ):
1077+ """Send a kill cursors message with the given ids."""
1078+ listeners = self ._event_listeners
1079+ publish = listeners .enabled_for_commands
1080+ if address :
1081+ # address could be a tuple or _CursorAddress, but
1082+ # select_server_by_address needs (host, port).
1083+ server = topology .select_server_by_address (tuple (address ))
1084+ else :
1085+ # Application called close_cursor() with no address.
1086+ server = topology .select_server (writable_server_selector )
1087+
1088+ try :
1089+ namespace = address .namespace
1090+ db , coll = namespace .split ('.' , 1 )
1091+ except AttributeError :
1092+ namespace = None
1093+ db = coll = "OP_KILL_CURSORS"
1094+
1095+ spec = SON ([('killCursors' , coll ), ('cursors' , cursor_ids )])
1096+ with server .get_socket (self .__all_credentials ) as sock_info :
1097+ if sock_info .max_wire_version >= 4 and namespace is not None :
1098+ sock_info .command (db , spec )
1099+ else :
1100+ if publish :
1101+ start = datetime .datetime .now ()
1102+ request_id , msg = message .kill_cursors (cursor_ids )
1103+ if publish :
1104+ duration = datetime .datetime .now () - start
1105+ # Here and below, address could be a tuple or
1106+ # _CursorAddress. We always want to publish a
1107+ # tuple to match the rest of the monitoring
1108+ # API.
1109+ listeners .publish_command_start (
1110+ spec , db , request_id , tuple (address ))
1111+ start = datetime .datetime .now ()
1112+
1113+ try :
1114+ sock_info .send_message (msg , 0 )
1115+ except Exception as exc :
1116+ if publish :
1117+ dur = ((datetime .datetime .now () - start ) + duration )
1118+ listeners .publish_command_failure (
1119+ dur , message ._convert_exception (exc ),
1120+ 'killCursors' , request_id ,
1121+ tuple (address ))
1122+ raise
1123+
1124+ if publish :
1125+ duration = ((datetime .datetime .now () - start ) + duration )
1126+ # OP_KILL_CURSORS returns no reply, fake one.
1127+ reply = {'cursorsUnknown' : cursor_ids , 'ok' : 1 }
1128+ listeners .publish_command_success (
1129+ duration , reply , 'killCursors' , request_id ,
1130+ tuple (address ))
1131+
10581132 # This method is run periodically by a background thread.
10591133 def _process_periodic_tasks (self ):
10601134 """Process any pending kill cursors requests and
@@ -1072,68 +1146,10 @@ def _process_periodic_tasks(self):
10721146
10731147 # Don't re-open topology if it's closed and there's no pending cursors.
10741148 if address_to_cursor_ids :
1075- listeners = self ._event_listeners
1076- publish = listeners .enabled_for_commands
10771149 topology = self ._get_topology ()
10781150 for address , cursor_ids in address_to_cursor_ids .items ():
10791151 try :
1080- if address :
1081- # address could be a tuple or _CursorAddress, but
1082- # select_server_by_address needs (host, port).
1083- server = topology .select_server_by_address (
1084- tuple (address ))
1085- else :
1086- # Application called close_cursor() with no address.
1087- server = topology .select_server (
1088- writable_server_selector )
1089-
1090- try :
1091- namespace = address .namespace
1092- db , coll = namespace .split ('.' , 1 )
1093- except AttributeError :
1094- namespace = None
1095- db = coll = "OP_KILL_CURSORS"
1096-
1097- spec = SON ([('killCursors' , coll ),
1098- ('cursors' , cursor_ids )])
1099- with server .get_socket (self .__all_credentials ) as sock_info :
1100- if (sock_info .max_wire_version >= 4 and
1101- namespace is not None ):
1102- sock_info .command (db , spec )
1103- else :
1104- if publish :
1105- start = datetime .datetime .now ()
1106- request_id , msg = message .kill_cursors (cursor_ids )
1107- if publish :
1108- duration = datetime .datetime .now () - start
1109- # Here and below, address could be a tuple or
1110- # _CursorAddress. We always want to publish a
1111- # tuple to match the rest of the monitoring
1112- # API.
1113- listeners .publish_command_start (
1114- spec , db , request_id , tuple (address ))
1115- start = datetime .datetime .now ()
1116-
1117- try :
1118- sock_info .send_message (msg , 0 )
1119- except Exception as exc :
1120- if publish :
1121- dur = ((datetime .datetime .now () - start )
1122- + duration )
1123- listeners .publish_command_failure (
1124- dur , message ._convert_exception (exc ),
1125- 'killCursors' , request_id , tuple (address ))
1126- raise
1127-
1128- if publish :
1129- duration = ((datetime .datetime .now () - start )
1130- + duration )
1131- # OP_KILL_CURSORS returns no reply, fake one.
1132- reply = {'cursorsUnknown' : cursor_ids , 'ok' : 1 }
1133- listeners .publish_command_success (
1134- duration , reply , 'killCursors' , request_id ,
1135- tuple (address ))
1136-
1152+ self ._kill_cursors (cursor_ids , address , topology )
11371153 except Exception :
11381154 helpers ._handle_exception ()
11391155 try :
0 commit comments