|
35 | 35 |
|
36 | 36 | import datetime
|
37 | 37 | import os
|
| 38 | +import select |
38 | 39 | import socket
|
39 | 40 | import struct
|
40 | 41 | import threading
|
| 42 | +import time |
41 | 43 | import warnings
|
42 | 44 |
|
43 | 45 | from pymongo import (database,
|
@@ -113,6 +115,16 @@ def _parse_uri(uri, default_port):
|
113 | 115 | return (host_list, database, username, password)
|
114 | 116 |
|
115 | 117 |
|
| 118 | +def _closed(sock): |
| 119 | + """Return True if we know socket has been closed, False otherwise. |
| 120 | + """ |
| 121 | + rd, _, _ = select.select([sock], [], [], 0) |
| 122 | + try: |
| 123 | + return len(rd) and sock.recv() == "" |
| 124 | + except: |
| 125 | + return True |
| 126 | + |
| 127 | + |
116 | 128 | class _Pool(threading.local):
|
117 | 129 | """A simple connection pool.
|
118 | 130 |
|
@@ -280,6 +292,7 @@ def __init__(self, host=None, port=None, pool_size=None,
|
280 | 292 | self.__cursor_manager = CursorManager(self)
|
281 | 293 |
|
282 | 294 | self.__pool = _Pool(self.__connect)
|
| 295 | + self.__last_checkout = time.time() |
283 | 296 |
|
284 | 297 | self.__network_timeout = network_timeout
|
285 | 298 | self.__document_class = document_class
|
@@ -515,6 +528,26 @@ def __connect(self):
|
515 | 528 | self.disconnect()
|
516 | 529 | raise AutoReconnect("could not connect to %r" % list(self.__nodes))
|
517 | 530 |
|
| 531 | + def __socket(self): |
| 532 | + """Get a socket from the pool. |
| 533 | +
|
| 534 | + If it's been > 1 second since the last time we checked out a |
| 535 | + socket, we also check to see if the socket has been closed - |
| 536 | + this let's us avoid seeing *some* |
| 537 | + :class:`~pymongo.errors.AutoReconnect` exceptions on server |
| 538 | + hiccups, etc. We only do this if it's been > 1 second since |
| 539 | + the last socket checkout, to keep performance reasonable - we |
| 540 | + can't avoid those completely anyway. |
| 541 | + """ |
| 542 | + sock = self.__pool.socket() |
| 543 | + t = time.time() |
| 544 | + if t - self.__last_checkout > 1: |
| 545 | + if _closed(sock): |
| 546 | + self.disconnect() |
| 547 | + sock = self.__pool.socket() |
| 548 | + self.__last_checkout = t |
| 549 | + return sock |
| 550 | + |
518 | 551 | def disconnect(self):
|
519 | 552 | """Disconnect from MongoDB.
|
520 | 553 |
|
@@ -595,7 +628,7 @@ def _send_message(self, message, with_last_error=False):
|
595 | 628 | - `with_last_error`: check getLastError status after sending the
|
596 | 629 | message
|
597 | 630 | """
|
598 |
| - sock = self.__pool.socket() |
| 631 | + sock = self.__socket() |
599 | 632 | try:
|
600 | 633 | (request_id, data) = message
|
601 | 634 | sock.sendall(data)
|
@@ -658,7 +691,7 @@ def _send_message_with_response(self, message,
|
658 | 691 | :Parameters:
|
659 | 692 | - `message`: (request_id, data) pair making up the message to send
|
660 | 693 | """
|
661 |
| - sock = self.__pool.socket() |
| 694 | + sock = self.__socket() |
662 | 695 |
|
663 | 696 | try:
|
664 | 697 | try:
|
|
0 commit comments