server_selectors.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. # Copyright 2014-2015 MongoDB, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you
  4. # may not use this file except in compliance with the License. You
  5. # 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
  12. # implied. See the License for the specific language governing
  13. # permissions and limitations under the License.
  14. """Criteria to select some ServerDescriptions out of a list."""
  15. from pymongo.server_type import SERVER_TYPE
  16. def any_server_selector(server_descriptions):
  17. return server_descriptions
  18. def readable_server_selector(server_descriptions):
  19. return [s for s in server_descriptions if s.is_readable]
  20. def writable_server_selector(server_descriptions):
  21. return [s for s in server_descriptions if s.is_writable]
  22. def secondary_server_selector(server_descriptions):
  23. return [s for s in server_descriptions
  24. if s.server_type == SERVER_TYPE.RSSecondary]
  25. def arbiter_server_selector(server_descriptions):
  26. return [s for s in server_descriptions
  27. if s.server_type == SERVER_TYPE.RSArbiter]
  28. def writable_preferred_server_selector(server_descriptions):
  29. """Like PrimaryPreferred but doesn't use tags or latency."""
  30. return (
  31. writable_server_selector(server_descriptions) or
  32. secondary_server_selector(server_descriptions))
  33. def single_tag_set_server_selector(tag_set, server_descriptions):
  34. """All servers matching one tag set.
  35. A tag set is a dict. A server matches if its tags are a superset:
  36. A server tagged {'a': '1', 'b': '2'} matches the tag set {'a': '1'}.
  37. The empty tag set {} matches any server.
  38. The `server_descriptions` passed to this function should have
  39. non-readable servers (e.g. RSGhost, RSArbiter, Unknown) filtered
  40. out (e.g. by readable_server_selector or secondary_server_selector)
  41. first.
  42. """
  43. def tags_match(server_tags):
  44. for key, value in tag_set.items():
  45. if key not in server_tags or server_tags[key] != value:
  46. return False
  47. return True
  48. return [s for s in server_descriptions if tags_match(s.tags)]
  49. def tag_sets_server_selector(tag_sets, server_descriptions):
  50. """All servers match a list of tag sets.
  51. tag_sets is a list of dicts. The empty tag set {} matches any server,
  52. and may be provided at the end of the list as a fallback. So
  53. [{'a': 'value'}, {}] expresses a preference for servers tagged
  54. {'a': 'value'}, but accepts any server if none matches the first
  55. preference.
  56. The `server_descriptions` passed to this function should have
  57. non-readable servers (e.g. RSGhost, RSArbiter, Unknown) filtered
  58. out (e.g. by readable_server_selector or secondary_server_selector)
  59. first.
  60. """
  61. for tag_set in tag_sets:
  62. selected = single_tag_set_server_selector(tag_set, server_descriptions)
  63. if selected:
  64. return selected
  65. return []
  66. def apply_local_threshold(latency_ms, server_descriptions):
  67. """All servers with round trip times within latency_ms of the fastest one.
  68. No ServerDescription's round_trip_time can be None.
  69. The `server_descriptions` passed to this function should have
  70. non-readable servers (e.g. RSGhost, RSArbiter, Unknown) filtered
  71. out (e.g. by readable_server_selector or secondary_server_selector)
  72. first.
  73. """
  74. if not server_descriptions:
  75. # Avoid ValueError from min() with empty sequence.
  76. return []
  77. # round_trip_time is in seconds.
  78. if any(s for s in server_descriptions if s.round_trip_time is None):
  79. raise ValueError("Not all servers' round trip times are known")
  80. fastest = min(s.round_trip_time for s in server_descriptions)
  81. return [
  82. s for s in server_descriptions
  83. if (s.round_trip_time - fastest) <= latency_ms / 1000.]
  84. def secondary_with_tags_server_selector(tag_sets, server_descriptions):
  85. """All near-enough secondaries matching the tag sets."""
  86. return tag_sets_server_selector(
  87. tag_sets, secondary_server_selector(server_descriptions))
  88. def member_with_tags_server_selector(tag_sets, server_descriptions):
  89. """All near-enough members matching the tag sets."""
  90. return tag_sets_server_selector(
  91. tag_sets, readable_server_selector(server_descriptions))