Как реализовано свойство partition tolerance в MongoDB?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Partition Tolerance в MongoDB и CAP Theorem
Partition Tolerance — это способность системы продолжать работать при разделении сети (network partition), когда узлы не могут общаться друг с другом. Разберём как это реализовано в MongoDB.
1. CAP Theorem и выбор MongoDB
CAP Theorem гласит, что распределённая система может гарантировать только 2 из 3 свойств:
Consistency (Согласованность) — все узлы видят одни и те же данные
Availability (Доступность) — система работает даже если какие-то узлы упали
Partition (Толерантность) — система работает при разделении сети
MongoDB выбирает CP (Consistency + Partition) в режиме по умолчанию:
# MongoDB по умолчанию использует сильную согласованность
# и толерантность к разделениям сети
# но может потерять доступность временно
from pymongo import MongoClient
from pymongo.errors import ServerSelectionTimeoutError
try:
client = MongoClient(
"mongodb://replica-set/db?replicaSet=rs0",
serverSelectionTimeoutMS=5000
)
result = client.test_db.test_collection.find_one()
except ServerSelectionTimeoutError:
# При разделении сети MongoDB может стать недоступным
print("Network partition: cannot reach any replica")
2. Replica Sets — основа Partition Tolerance
Repica Set — это группа MongoDB узлов, которые реплицируют данные друг к другу:
# Стандартная конфигурация Replica Set
replica_set_config = {
"_id": "rs0",
"members": [
{"_id": 0, "host": "primary:27017"},
{"_id": 1, "host": "secondary1:27017"},
{"_id": 2, "host": "secondary2:27017"},
],
"settings": {
"electionTimeoutMillis": 10000, # Время для выборов
}
}
# Нормальное состояние:
# PRIMARY SECONDARY SECONDARY
# ↓ writes ↑ reads ↑ reads
# ↓ replicates ←←←←←←← ←←←←←←←
#
# При разделении PRIMARY ↔ SECONDARIES:
# PRIMARY SECONDARY SECONDARY
# (можно писать) (можно писать?)
3. Election и Quorum
Если PRIMARY упадёт или произойдёт разделение сети, MongoDB проводит Election (выборы) нового PRIMARY:
# Quorum — минимальное количество узлов для принятия решения
# Для 3-узловой replica set: quorum = 2
quorum_needed = (replica_set_size // 2) + 1
# Для 3 узлов: quorum = 2
# Для 5 узлов: quorum = 3
# Для 7 узлов: quorum = 4
# СЦЕНАРИЙ 1: Нормальное разделение
#
# PARTITION 1 PARTITION 2
# Primary (1) Secondary1 (1)
# Secondary2 (1)
# Total: 2 узла Total: 1 узел
# Quorum: 2 ✓ Quorum: 2 ✗
#
# PARTITION 1 может писать (есть quorum)
# PARTITION 2 НЕ может писать (нет quorum)
# PARTITION 2 переходит в режим "читаемый вторичный"
print("PARTITION 1: Can write (has quorum)")
print("PARTITION 2: Cannot write (no quorum)")
print("Result: Consistency maintained!")
4. Write Concern и Replication
Write Concern определяет сколько узлов должны подтвердить запись:
from pymongo import MongoClient
from pymongo.errors import WriteError
client = MongoClient("mongodb://replica-set/db?replicaSet=rs0")
db = client.test_db
collection = db.test_collection
# Write Concern: ACKNOWLEDGED (w=1)
# Только PRIMARY должен подтвердить
try:
result = collection.insert_one(
{"name": "Alice"},
write_concern={"w": 1} # PRIMARY only
)
print(f"Inserted: {result.inserted_id}")
except WriteError as e:
print(f"Write error: {e}")
# Write Concern: MAJORITY (w="majority")
# PRIMARY + большинство SECONDARIES должны подтвердить
result = collection.insert_one(
{"name": "Bob"},
write_concern={"w": "majority", "wtimeoutMS": 5000}
# Ждём подтверждения от большинства
# Если разделение сети, и PRIMARY в меньшинстве, запись будет отклонена
)
# Write Concern: REPLICAS (w=2) для 3-узловой replica set
result = collection.insert_one(
{"name": "Charlie"},
write_concern={"w": 2} # PRIMARY + минимум 1 SECONDARY
)
Как это обеспечивает Partition Tolerance:
# СЦЕНАРИЙ: Разделение сети
# Primary в PARTITION A, Secondaries в PARTITION B
#
# PARTITION A PARTITION B
# Primary (1) Secondary1 (1)
# Secondary2 (1)
# Total: 1 узел Total: 2 узла
# Quorum: 2 Quorum: 2 ✓
#
# Запрос с w="majority" к PRIMARY в PARTITION A:
try:
collection.insert_one(
{"data": "important"},
write_concern={"w": "majority"}
)
except WriteError:
# ❌ Ошибка! PRIMARY не может получить подтверждение от большинства
# Данные НЕ будут записаны
print("Cannot achieve write concern majority - network partition detected")
# PARTITION B может выбрать нового PRIMARY из 2 узлов
# и начать принимать операции самостоятельно
print("PARTITION B: Can elect new PRIMARY and accept writes")
5. Heartbeat и Detection Network Partition
# Узлы в Replica Set периодически проверяют друг друга
# (heartbeat protocol)
heartbeat_config = {
"heartbeatFrequencyMS": 10000, # Каждые 10 сек
"electionTimeoutMillis": 10000, # Если нет пульса 10 сек
"serverMonitoringMode": "auto",
}
# Если PRIMARY не слышит SECONDARIES более electionTimeoutMillis:
# 1. PRIMARY проверяет может ли это быть network partition
# 2. Если PRIMARY в меньшинстве (< quorum), он STEP DOWN
# 3. PRIMARY становится SECONDARY (read-only)
# 4. Большинство узлов могут выбрать нового PRIMARY
print("""
Когда PRIMARY теряет связь > electionTimeoutMillis:
1. PRIMARY проверяет что он в большинстве
2. Если НЕ в большинстве → STEP DOWN
3. PRIMARY → SECONDARY (больше не может писать)
4. Другое большинство выбирает нового PRIMARY
""")
6. Practical Partition Tolerance Implementation
from pymongo import MongoClient
from pymongo.errors import AutoReconnect, ServerSelectionTimeoutError
from typing import Any, Dict
import asyncio
class PartitionTolerantDB:
def __init__(self, replica_set_url: str):
self.client = MongoClient(
replica_set_url,
serverSelectionTimeoutMS=5000,
connectTimeoutMS=10000,
socketTimeoutMS=5000,
retryWrites=True, # Автоматический retry при временных ошибках
w="majority" # Сильная консистентность
)
async def write_with_retry(
self,
db_name: str,
collection_name: str,
document: Dict[str, Any],
max_retries: int = 3
) -> str:
"""Запись с retry при разделении сети"""
for attempt in range(max_retries):
try:
db = self.client[db_name]
collection = db[collection_name]
result = collection.insert_one(
document,
write_concern={"w": "majority", "wtimeoutMS": 5000}
)
return str(result.inserted_id)
except ServerSelectionTimeoutError:
# Не можем достичь PRIMARY
if attempt < max_retries - 1:
print(f"Network partition detected, retry {attempt + 1}...")
await asyncio.sleep(2 ** attempt) # Exponential backoff
else:
raise
except AutoReconnect:
# Временная потеря соединения
print("Temporary connection loss, retrying...")
await asyncio.sleep(1)
async def read_with_preference(
self,
db_name: str,
collection_name: str,
query: Dict[str, Any],
read_preference: str = "primary"
) -> Any:
"""Чтение с выбором стратегии"""
from pymongo.read_preferences import ReadPreference
db = self.client[db_name]
collection = db[collection_name]
if read_preference == "primary":
# Читаем только с PRIMARY (свежие данные, но недоступно при разделении)
return collection.find_one(query)
elif read_preference == "secondary":
# Читаем со SECONDARY (могут быть старые данные, но доступно при разделении)
collection.with_options(
read_preference=ReadPreference.SECONDARY
)
return collection.find_one(query)
elif read_preference == "secondary_preferred":
# Предпочитаем SECONDARY, но можем читать с PRIMARY
collection.with_options(
read_preference=ReadPreference.SECONDARY_PREFERRED
)
return collection.find_one(query)
# Использование
async def main():
db = PartitionTolerantDB("mongodb://rs0/mydb?replicaSet=rs0")
# Запись с гарантией консистентности
doc_id = await db.write_with_retry(
"mydb",
"users",
{"name": "Alice", "email": "alice@example.com"}
)
# Чтение с разных стратегий
fresh_data = await db.read_with_preference(
"mydb", "users",
{"_id": doc_id},
read_preference="primary"
)
# При разделении сети может читать со SECONDARY
possibly_stale = await db.read_with_preference(
"mydb", "users",
{"_id": doc_id},
read_preference="secondary_preferred"
)
7. Выбор между Consistency, Availability и Partition Tolerance
# MongoDB можно конфигурировать под разные требования:
configs = {
"CP (Consistency + Partition)": {
"write_concern": {"w": "majority"},
"read_preference": "primary",
"use_case": "Финансовые операции, критичные данные",
"pros": "Сильная консистентность",
"cons": "При разделении может быть недоступна"
},
"AP (Availability + Partition)": {
"write_concern": {"w": 1},
"read_preference": "secondary_preferred",
"use_case": "Social media, analytics",
"pros": "Высокая доступность даже при разделении",
"cons": "Возможны несогласованные данные временно"
}
}
print("MongoDB по умолчанию: CP (сильная консистентность)")
print("Можно настроить на AP если нужна доступность")
Итоговая схема Partition Tolerance в MongoDB
РЕПЛИКА САТ (3 узла):
PRIMARY → SECONDARY → SECONDARY
↓ ↓ ↓
все writes replication replication
ПРИ РАЗДЕЛЕНИИ СЕТИ:
PARTITION A PARTITION B
PRIMARY (1) SECONDARY (1)
SECONDARY (1)
↓ ↓
1 < quorum(2) 2 = quorum(2)
✗ STEP DOWN ✓ ELECT NEW PRIMARY
PARTITION A: PARTITION B:
❌ cannot write ✓ can write
❌ read only ✓ read + write
РЕЗУЛЬТАТ:
- Данные консистентны
- Одна часть сети может писать
- Другая часть может только читать
- При восстановлении сети синхронизируются
Ключевые механизмы:
- Replica Sets — репликация данных на несколько узлов
- Quorum — большинство узлов для выборов
- Election — выбор нового PRIMARY при сбое
- Heartbeat — обнаружение разделений сети
- Write Concern — гарантии записи на несколько узлов
- Read Preference — выбор от какого узла читать
Это обеспечивает Partition Tolerance при сохранении Consistency!