server_description.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. # Copyright 2014-present MongoDB, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Represent one server the driver is connected to."""
  15. from bson import EPOCH_NAIVE
  16. from pymongo.ismaster import IsMaster
  17. from pymongo.server_type import SERVER_TYPE
  18. from pymongo.monotonic import time as _time
  19. class ServerDescription(object):
  20. """Immutable representation of one server.
  21. :Parameters:
  22. - `address`: A (host, port) pair
  23. - `ismaster`: Optional Hello instance
  24. - `round_trip_time`: Optional float
  25. - `error`: Optional, the last error attempting to connect to the server
  26. .. warning:: The `ismaster` parameter will be renamed to `hello` in PyMongo
  27. 4.0.
  28. """
  29. __slots__ = (
  30. '_address', '_server_type', '_all_hosts', '_tags', '_replica_set_name',
  31. '_primary', '_max_bson_size', '_max_message_size',
  32. '_max_write_batch_size', '_min_wire_version', '_max_wire_version',
  33. '_round_trip_time', '_me', '_is_writable', '_is_readable',
  34. '_ls_timeout_minutes', '_error', '_set_version', '_election_id',
  35. '_cluster_time', '_last_write_date', '_last_update_time',
  36. '_topology_version')
  37. def __init__(
  38. self,
  39. address,
  40. ismaster=None,
  41. round_trip_time=None,
  42. error=None):
  43. self._address = address
  44. hello = ismaster or IsMaster({})
  45. self._server_type = hello.server_type
  46. self._all_hosts = hello.all_hosts
  47. self._tags = hello.tags
  48. self._replica_set_name = hello.replica_set_name
  49. self._primary = hello.primary
  50. self._max_bson_size = hello.max_bson_size
  51. self._max_message_size = hello.max_message_size
  52. self._max_write_batch_size = hello.max_write_batch_size
  53. self._min_wire_version = hello.min_wire_version
  54. self._max_wire_version = hello.max_wire_version
  55. self._set_version = hello.set_version
  56. self._election_id = hello.election_id
  57. self._cluster_time = hello.cluster_time
  58. self._is_writable = hello.is_writable
  59. self._is_readable = hello.is_readable
  60. self._ls_timeout_minutes = hello.logical_session_timeout_minutes
  61. self._round_trip_time = round_trip_time
  62. self._me = hello.me
  63. self._last_update_time = _time()
  64. self._error = error
  65. self._topology_version = hello.topology_version
  66. if error:
  67. if hasattr(error, 'details') and isinstance(error.details, dict):
  68. self._topology_version = error.details.get('topologyVersion')
  69. if hello.last_write_date:
  70. # Convert from datetime to seconds.
  71. delta = hello.last_write_date - EPOCH_NAIVE
  72. self._last_write_date = delta.total_seconds()
  73. else:
  74. self._last_write_date = None
  75. @property
  76. def address(self):
  77. """The address (host, port) of this server."""
  78. return self._address
  79. @property
  80. def server_type(self):
  81. """The type of this server."""
  82. return self._server_type
  83. @property
  84. def server_type_name(self):
  85. """The server type as a human readable string.
  86. .. versionadded:: 3.4
  87. """
  88. return SERVER_TYPE._fields[self._server_type]
  89. @property
  90. def all_hosts(self):
  91. """List of hosts, passives, and arbiters known to this server."""
  92. return self._all_hosts
  93. @property
  94. def tags(self):
  95. return self._tags
  96. @property
  97. def replica_set_name(self):
  98. """Replica set name or None."""
  99. return self._replica_set_name
  100. @property
  101. def primary(self):
  102. """This server's opinion about who the primary is, or None."""
  103. return self._primary
  104. @property
  105. def max_bson_size(self):
  106. return self._max_bson_size
  107. @property
  108. def max_message_size(self):
  109. return self._max_message_size
  110. @property
  111. def max_write_batch_size(self):
  112. return self._max_write_batch_size
  113. @property
  114. def min_wire_version(self):
  115. return self._min_wire_version
  116. @property
  117. def max_wire_version(self):
  118. return self._max_wire_version
  119. @property
  120. def set_version(self):
  121. return self._set_version
  122. @property
  123. def election_id(self):
  124. return self._election_id
  125. @property
  126. def cluster_time(self):
  127. return self._cluster_time
  128. @property
  129. def election_tuple(self):
  130. return self._set_version, self._election_id
  131. @property
  132. def me(self):
  133. return self._me
  134. @property
  135. def logical_session_timeout_minutes(self):
  136. return self._ls_timeout_minutes
  137. @property
  138. def last_write_date(self):
  139. return self._last_write_date
  140. @property
  141. def last_update_time(self):
  142. return self._last_update_time
  143. @property
  144. def round_trip_time(self):
  145. """The current average latency or None."""
  146. # This override is for unittesting only!
  147. if self._address in self._host_to_round_trip_time:
  148. return self._host_to_round_trip_time[self._address]
  149. return self._round_trip_time
  150. @property
  151. def error(self):
  152. """The last error attempting to connect to the server, or None."""
  153. return self._error
  154. @property
  155. def is_writable(self):
  156. return self._is_writable
  157. @property
  158. def is_readable(self):
  159. return self._is_readable
  160. @property
  161. def mongos(self):
  162. return self._server_type == SERVER_TYPE.Mongos
  163. @property
  164. def is_server_type_known(self):
  165. return self.server_type != SERVER_TYPE.Unknown
  166. @property
  167. def retryable_writes_supported(self):
  168. """Checks if this server supports retryable writes."""
  169. return ((
  170. self._ls_timeout_minutes is not None and
  171. self._server_type in (SERVER_TYPE.Mongos, SERVER_TYPE.RSPrimary))
  172. or self._server_type == SERVER_TYPE.LoadBalancer)
  173. @property
  174. def retryable_reads_supported(self):
  175. """Checks if this server supports retryable writes."""
  176. return self._max_wire_version >= 6
  177. @property
  178. def topology_version(self):
  179. return self._topology_version
  180. def to_unknown(self, error=None):
  181. unknown = ServerDescription(self.address, error=error)
  182. unknown._topology_version = self.topology_version
  183. return unknown
  184. def __eq__(self, other):
  185. if isinstance(other, ServerDescription):
  186. return ((self._address == other.address) and
  187. (self._server_type == other.server_type) and
  188. (self._min_wire_version == other.min_wire_version) and
  189. (self._max_wire_version == other.max_wire_version) and
  190. (self._me == other.me) and
  191. (self._all_hosts == other.all_hosts) and
  192. (self._tags == other.tags) and
  193. (self._replica_set_name == other.replica_set_name) and
  194. (self._set_version == other.set_version) and
  195. (self._election_id == other.election_id) and
  196. (self._primary == other.primary) and
  197. (self._ls_timeout_minutes ==
  198. other.logical_session_timeout_minutes) and
  199. (self._error == other.error))
  200. return NotImplemented
  201. def __ne__(self, other):
  202. return not self == other
  203. def __repr__(self):
  204. errmsg = ''
  205. if self.error:
  206. errmsg = ', error=%r' % (self.error,)
  207. return "<%s %s server_type: %s, rtt: %s%s>" % (
  208. self.__class__.__name__, self.address, self.server_type_name,
  209. self.round_trip_time, errmsg)
  210. # For unittesting only. Use under no circumstances!
  211. _host_to_round_trip_time = {}