max_staleness_selectors.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # Copyright 2016 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 ServerDescriptions based on maxStalenessSeconds.
  15. The Max Staleness Spec says: When there is a known primary P,
  16. a secondary S's staleness is estimated with this formula:
  17. (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate)
  18. + heartbeatFrequencyMS
  19. When there is no known primary, a secondary S's staleness is estimated with:
  20. SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS
  21. where "SMax" is the secondary with the greatest lastWriteDate.
  22. """
  23. from pymongo.errors import ConfigurationError
  24. from pymongo.server_type import SERVER_TYPE
  25. # Constant defined in Max Staleness Spec: An idle primary writes a no-op every
  26. # 10 seconds to refresh secondaries' lastWriteDate values.
  27. IDLE_WRITE_PERIOD = 10
  28. SMALLEST_MAX_STALENESS = 90
  29. def _validate_max_staleness(max_staleness,
  30. heartbeat_frequency):
  31. # We checked for max staleness -1 before this, it must be positive here.
  32. if max_staleness < heartbeat_frequency + IDLE_WRITE_PERIOD:
  33. raise ConfigurationError(
  34. "maxStalenessSeconds must be at least heartbeatFrequencyMS +"
  35. " %d seconds. maxStalenessSeconds is set to %d,"
  36. " heartbeatFrequencyMS is set to %d." % (
  37. IDLE_WRITE_PERIOD, max_staleness, heartbeat_frequency * 1000))
  38. if max_staleness < SMALLEST_MAX_STALENESS:
  39. raise ConfigurationError(
  40. "maxStalenessSeconds must be at least %d. "
  41. "maxStalenessSeconds is set to %d." % (
  42. SMALLEST_MAX_STALENESS, max_staleness))
  43. def _with_primary(max_staleness, selection):
  44. """Apply max_staleness, in seconds, to a Selection with a known primary."""
  45. primary = selection.primary
  46. sds = []
  47. for s in selection.server_descriptions:
  48. if s.server_type == SERVER_TYPE.RSSecondary:
  49. # See max-staleness.rst for explanation of this formula.
  50. staleness = (
  51. (s.last_update_time - s.last_write_date) -
  52. (primary.last_update_time - primary.last_write_date) +
  53. selection.heartbeat_frequency)
  54. if staleness <= max_staleness:
  55. sds.append(s)
  56. else:
  57. sds.append(s)
  58. return selection.with_server_descriptions(sds)
  59. def _no_primary(max_staleness, selection):
  60. """Apply max_staleness, in seconds, to a Selection with no known primary."""
  61. # Secondary that's replicated the most recent writes.
  62. smax = selection.secondary_with_max_last_write_date()
  63. if not smax:
  64. # No secondaries and no primary, short-circuit out of here.
  65. return selection.with_server_descriptions([])
  66. sds = []
  67. for s in selection.server_descriptions:
  68. if s.server_type == SERVER_TYPE.RSSecondary:
  69. # See max-staleness.rst for explanation of this formula.
  70. staleness = (smax.last_write_date -
  71. s.last_write_date +
  72. selection.heartbeat_frequency)
  73. if staleness <= max_staleness:
  74. sds.append(s)
  75. else:
  76. sds.append(s)
  77. return selection.with_server_descriptions(sds)
  78. def select(max_staleness, selection):
  79. """Apply max_staleness, in seconds, to a Selection."""
  80. if max_staleness == -1:
  81. return selection
  82. # Server Selection Spec: If the TopologyType is ReplicaSetWithPrimary or
  83. # ReplicaSetNoPrimary, a client MUST raise an error if maxStaleness <
  84. # heartbeatFrequency + IDLE_WRITE_PERIOD, or if maxStaleness < 90.
  85. _validate_max_staleness(max_staleness, selection.heartbeat_frequency)
  86. if selection.primary:
  87. return _with_primary(max_staleness, selection)
  88. else:
  89. return _no_primary(max_staleness, selection)