123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- # Copyright 2016 MongoDB, Inc.
- #
- # Licensed under the Apache License, Version 2.0 (the "License"); you
- # may not use this file except in compliance with the License. You
- # may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- # implied. See the License for the specific language governing
- # permissions and limitations under the License.
- """Criteria to select ServerDescriptions based on maxStalenessSeconds.
- The Max Staleness Spec says: When there is a known primary P,
- a secondary S's staleness is estimated with this formula:
- (S.lastUpdateTime - S.lastWriteDate) - (P.lastUpdateTime - P.lastWriteDate)
- + heartbeatFrequencyMS
- When there is no known primary, a secondary S's staleness is estimated with:
- SMax.lastWriteDate - S.lastWriteDate + heartbeatFrequencyMS
- where "SMax" is the secondary with the greatest lastWriteDate.
- """
- from pymongo.errors import ConfigurationError
- from pymongo.server_type import SERVER_TYPE
- # Constant defined in Max Staleness Spec: An idle primary writes a no-op every
- # 10 seconds to refresh secondaries' lastWriteDate values.
- IDLE_WRITE_PERIOD = 10
- SMALLEST_MAX_STALENESS = 90
- def _validate_max_staleness(max_staleness,
- heartbeat_frequency):
- # We checked for max staleness -1 before this, it must be positive here.
- if max_staleness < heartbeat_frequency + IDLE_WRITE_PERIOD:
- raise ConfigurationError(
- "maxStalenessSeconds must be at least heartbeatFrequencyMS +"
- " %d seconds. maxStalenessSeconds is set to %d,"
- " heartbeatFrequencyMS is set to %d." % (
- IDLE_WRITE_PERIOD, max_staleness, heartbeat_frequency * 1000))
- if max_staleness < SMALLEST_MAX_STALENESS:
- raise ConfigurationError(
- "maxStalenessSeconds must be at least %d. "
- "maxStalenessSeconds is set to %d." % (
- SMALLEST_MAX_STALENESS, max_staleness))
- def _with_primary(max_staleness, selection):
- """Apply max_staleness, in seconds, to a Selection with a known primary."""
- primary = selection.primary
- sds = []
- for s in selection.server_descriptions:
- if s.server_type == SERVER_TYPE.RSSecondary:
- # See max-staleness.rst for explanation of this formula.
- staleness = (
- (s.last_update_time - s.last_write_date) -
- (primary.last_update_time - primary.last_write_date) +
- selection.heartbeat_frequency)
- if staleness <= max_staleness:
- sds.append(s)
- else:
- sds.append(s)
- return selection.with_server_descriptions(sds)
- def _no_primary(max_staleness, selection):
- """Apply max_staleness, in seconds, to a Selection with no known primary."""
- # Secondary that's replicated the most recent writes.
- smax = selection.secondary_with_max_last_write_date()
- if not smax:
- # No secondaries and no primary, short-circuit out of here.
- return selection.with_server_descriptions([])
- sds = []
- for s in selection.server_descriptions:
- if s.server_type == SERVER_TYPE.RSSecondary:
- # See max-staleness.rst for explanation of this formula.
- staleness = (smax.last_write_date -
- s.last_write_date +
- selection.heartbeat_frequency)
- if staleness <= max_staleness:
- sds.append(s)
- else:
- sds.append(s)
- return selection.with_server_descriptions(sds)
- def select(max_staleness, selection):
- """Apply max_staleness, in seconds, to a Selection."""
- if max_staleness == -1:
- return selection
- # Server Selection Spec: If the TopologyType is ReplicaSetWithPrimary or
- # ReplicaSetNoPrimary, a client MUST raise an error if maxStaleness <
- # heartbeatFrequency + IDLE_WRITE_PERIOD, or if maxStaleness < 90.
- _validate_max_staleness(max_staleness, selection.heartbeat_frequency)
- if selection.primary:
- return _with_primary(max_staleness, selection)
- else:
- return _no_primary(max_staleness, selection)
|