@@ -778,6 +778,9 @@ def close(self):
778
778
If this instance is used again it will be automatically re-opened and
779
779
the threads restarted.
780
780
"""
781
+ # Run _process_periodic_tasks to send pending killCursor requests
782
+ # before closing the topology.
783
+ self ._process_periodic_tasks ()
781
784
self ._topology .close ()
782
785
783
786
def set_cursor_manager (self , manager_class ):
@@ -1024,6 +1027,21 @@ def close_cursor(self, cursor_id, address=None):
1024
1027
else :
1025
1028
self .__kill_cursors_queue .append ((address , [cursor_id ]))
1026
1029
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
+
1027
1045
def kill_cursors (self , cursor_ids , address = None ):
1028
1046
"""DEPRECATED - Send a kill cursors message soon with the given ids.
1029
1047
@@ -1055,6 +1073,62 @@ def kill_cursors(self, cursor_ids, address=None):
1055
1073
# "Atomic", needs no lock.
1056
1074
self .__kill_cursors_queue .append ((address , cursor_ids ))
1057
1075
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
+
1058
1132
# This method is run periodically by a background thread.
1059
1133
def _process_periodic_tasks (self ):
1060
1134
"""Process any pending kill cursors requests and
@@ -1072,68 +1146,10 @@ def _process_periodic_tasks(self):
1072
1146
1073
1147
# Don't re-open topology if it's closed and there's no pending cursors.
1074
1148
if address_to_cursor_ids :
1075
- listeners = self ._event_listeners
1076
- publish = listeners .enabled_for_commands
1077
1149
topology = self ._get_topology ()
1078
1150
for address , cursor_ids in address_to_cursor_ids .items ():
1079
1151
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 )
1137
1153
except Exception :
1138
1154
helpers ._handle_exception ()
1139
1155
try :
0 commit comments