Skip to content

Commit b6182d0

Browse files
author
Luke Lovett
committed
PYTHON-976 - Publish a CommandFailedEvent for arbitrary failures.
1 parent 519f068 commit b6182d0

File tree

9 files changed

+80
-25
lines changed

9 files changed

+80
-25
lines changed

pymongo/collection.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -398,19 +398,22 @@ def _legacy_write(
398398
try:
399399
result = sock_info.legacy_write(
400400
rqst_id, msg, max_size, acknowledged)
401-
except OperationFailure as exc:
401+
except Exception as exc:
402402
if publish:
403403
dur = (datetime.datetime.now() - start) + duration
404-
details = exc.details
405-
# Succeed if GLE was successful and this is a write error.
406-
if details.get("ok") and "n" in details:
407-
reply = message._convert_write_result(
408-
name, cmd, details)
409-
monitoring.publish_command_success(
410-
dur, reply, name, rqst_id, sock_info.address, op_id)
404+
if isinstance(exc, OperationFailure):
405+
details = exc.details
406+
# Succeed if GLE was successful and this is a write error.
407+
if details.get("ok") and "n" in details:
408+
reply = message._convert_write_result(
409+
name, cmd, details)
410+
monitoring.publish_command_success(
411+
dur, reply, name, rqst_id, sock_info.address, op_id)
412+
raise
411413
else:
412-
monitoring.publish_command_failure(
413-
dur, details, name, rqst_id, sock_info.address, op_id)
414+
details = message._convert_exception(exc)
415+
monitoring.publish_command_failure(
416+
dur, details, name, rqst_id, sock_info.address, op_id)
414417
raise
415418
if publish:
416419
if result is not None:

pymongo/command_cursor.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from bson.py3compat import integer_types
2222
from pymongo import helpers, monitoring
2323
from pymongo.errors import AutoReconnect, NotMasterError, OperationFailure
24-
from pymongo.message import _CursorAddress, _GetMore
24+
from pymongo.message import _CursorAddress, _GetMore, _convert_exception
2525

2626

2727
class CommandCursor(object):
@@ -132,6 +132,13 @@ def __send_message(self, operation):
132132

133133
client._reset_server_and_request_check(self.address)
134134
raise
135+
except Exception as exc:
136+
if publish:
137+
duration = (datetime.datetime.now() - start) + cmd_duration
138+
monitoring.publish_command_failure(
139+
duration, _convert_exception(exc), "getMore", rqst_id,
140+
self.__address)
141+
raise
135142

136143
if publish:
137144
duration = (datetime.datetime.now() - start) + cmd_duration

pymongo/cursor.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
InvalidOperation,
3333
NotMasterError,
3434
OperationFailure)
35-
from pymongo.message import _CursorAddress, _GetMore, _Query
35+
from pymongo.message import _CursorAddress, _GetMore, _Query, _convert_exception
3636
from pymongo.read_preferences import ReadPreference
3737

3838
_QUERY_OPTIONS = {
@@ -850,8 +850,14 @@ def __send_message(self, operation):
850850
start = datetime.datetime.now()
851851
try:
852852
data = self.__exhaust_mgr.sock.receive_message(1, None)
853-
except ConnectionFailure:
854-
self.__die()
853+
except Exception as exc:
854+
if publish:
855+
duration = datetime.datetime.now() - start
856+
monitoring.publish_command_failure(
857+
duration, _convert_exception(exc), cmd_name, rqst_id,
858+
self.__address)
859+
if isinstance(exc, ConnectionFailure):
860+
self.__die()
855861
raise
856862
if publish:
857863
cmd_duration = datetime.datetime.now() - start
@@ -895,6 +901,13 @@ def __send_message(self, operation):
895901

896902
client._reset_server_and_request_check(self.__address)
897903
raise
904+
except Exception as exc:
905+
if publish:
906+
duration = (datetime.datetime.now() - start) + cmd_duration
907+
monitoring.publish_command_failure(
908+
duration, _convert_exception(exc), cmd_name, rqst_id,
909+
self.__address)
910+
raise
898911

899912
if publish:
900913
duration = (datetime.datetime.now() - start) + cmd_duration

pymongo/helpers.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
WriteError,
3232
WriteConcernError,
3333
WTimeoutError)
34-
from pymongo.message import _Query
34+
from pymongo.message import _Query, _convert_exception
3535

3636

3737
_UUNDER = u("_")
@@ -259,11 +259,15 @@ def _first_batch(sock_info, namespace, query,
259259
response = sock_info.receive_message(1, request_id)
260260
try:
261261
result = _unpack_response(response, None, codec_options)
262-
except (NotMasterError, OperationFailure) as exc:
262+
except Exception as exc:
263263
if publish:
264264
duration = (datetime.datetime.now() - start) + encoding_duration
265+
if isinstance(exc, (NotMasterError, OperationFailure)):
266+
failure = exc.details
267+
else:
268+
failure = _convert_exception(exc)
265269
monitoring.publish_command_failure(
266-
duration, exc.details, name, request_id, sock_info.address)
270+
duration, failure, name, request_id, sock_info.address)
267271
raise
268272
if publish:
269273
duration = (datetime.datetime.now() - start) + encoding_duration

pymongo/message.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ def _maybe_add_read_preference(spec, read_preference):
8282
return spec
8383

8484

85+
def _convert_exception(exception):
86+
"""Convert an Exception into a failure document for publishing."""
87+
return {'errmsg': exception.message,
88+
'errtype': exception.__class__.__name__}
89+
90+
8591
def _convert_write_result(operation, command, result):
8692
"""Convert a legacy write result to write commmand format."""
8793

pymongo/mongo_client.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,15 @@ def _process_kill_cursors_queue(self):
938938
monitoring.publish_command_start(
939939
command, dbname, data[0], address)
940940
start = datetime.datetime.now()
941-
server.send_message(data, self.__all_credentials)
941+
try:
942+
server.send_message(data, self.__all_credentials)
943+
except Exception as exc:
944+
if publish:
945+
dur = (datetime.datetime.now() - start) + duration
946+
monitoring.publish_command_failure(
947+
dur, message._convert_exception(exc),
948+
'killCursors', data[0], address)
949+
raise
942950
if publish:
943951
duration = (datetime.datetime.now() - start) + duration
944952
# OP_KILL_CURSORS returns no reply, fake one.

pymongo/monitoring.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ def publish_command_failure(
198198
199199
:Parameters:
200200
- `duration`: The command duration as a datetime.timedelta.
201-
- `failure`: The server reply document.
201+
- `failure`: The server reply document or failure description document.
202202
- `command_name`: The command name.
203203
- `request_id`: The request id for this operation.
204204
- `connection_id`: The address (host, port) of the server this command

pymongo/network.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ def command(sock, dbname, spec, slave_ok, is_mongos,
7575
monitoring.publish_command_start(orig, dbname, request_id, address)
7676
start = datetime.datetime.now()
7777

78-
sock.sendall(msg)
79-
response = receive_message(sock, 1, request_id)
8078
try:
79+
sock.sendall(msg)
80+
response = receive_message(sock, 1, request_id)
8181
unpacked = helpers._unpack_response(
8282
response, codec_options=codec_options)
8383

@@ -86,11 +86,15 @@ def command(sock, dbname, spec, slave_ok, is_mongos,
8686
msg = "command %s on namespace %s failed: %%s" % (
8787
repr(spec).replace("%", "%%"), ns)
8888
helpers._check_command_response(response_doc, msg, allowable_errors)
89-
except (NotMasterError, OperationFailure) as exc:
89+
except Exception as exc:
9090
if publish:
9191
duration = (datetime.datetime.now() - start) + encoding_duration
92+
if isinstance(exc, (NotMasterError, OperationFailure)):
93+
failure = exc.details
94+
else:
95+
failure = message._convert_exception(exc)
9296
monitoring.publish_command_failure(
93-
duration, exc.details, name, request_id, address)
97+
duration, failure, name, request_id, address)
9498
raise
9599
if publish:
96100
duration = (datetime.datetime.now() - start) + encoding_duration

pymongo/server.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from datetime import datetime
2020

2121
from pymongo import monitoring
22+
from pymongo.message import _convert_exception
2223
from pymongo.response import Response, ExhaustResponse
2324
from pymongo.server_type import SERVER_TYPE
2425

@@ -101,8 +102,17 @@ def send_message_with_response(
101102
cmd, dbn, request_id, sock_info.address)
102103
start = datetime.now()
103104

104-
sock_info.send_message(data, max_doc_size)
105-
response_data = sock_info.receive_message(1, request_id)
105+
try:
106+
sock_info.send_message(data, max_doc_size)
107+
response_data = sock_info.receive_message(1, request_id)
108+
except Exception as exc:
109+
if publish:
110+
duration = (datetime.now() - start) + encoding_duration
111+
failure = _convert_exception(exc)
112+
monitoring.publish_command_failure(
113+
duration, failure, next(iter(cmd)), request_id,
114+
sock_info.address)
115+
raise
106116

107117
if publish:
108118
duration = (datetime.now() - start) + encoding_duration

0 commit comments

Comments
 (0)