Go: Распределенные системы

10 вопросов

1 CAP-теорема. CP и AP системы в контексте Go.

При разделении (partition) нельзя одновременно гарантировать Consistency, Availability и Partition tolerance. CP - при партиции жертвуют доступностью (ждать консенсус). AP - жертвуют консистентностью (eventual consistency). В Go при выборе хранилищ и протоколов учитывают: БД чаще CP; кеши, очереди - часто AP. Приложение может маскировать недоступность ретраями и кешем.

Открыть отдельно →
2 Модели консистентности. Strong, eventual, causal.

Strong - все реплики видят одни и те же данные в один момент. Eventual - после прекращения обновлений все придут к одному состоянию. Causal - сохранение причинно-следственных связей. В Go при работе с репликами и кешем учитывают: чтение после записи может требовать чтения с primary; идемпотентность и версии для разрешения конфликтов при eventual.

Открыть отдельно →
3 Консенсус: Raft и Paxos. Применение в Go.

Raft и Paxos - алгоритмы консенсуса для replicated log (лидер, голосование, коммит). В Go: etcd и Consul используют Raft; библиотеки (hashicorp/raft) для встраивания. Используют для координации (лидер-выборы, распределенная конфигурация). Приложение на Go обычно клиент etcd/Consul, не реализует консенсус сам.

// etcd client for distributed config
client, _ := clientv3.New(clientv3.Config{Endpoints: []string{"http://localhost:2379"}})
Открыть отдельно →
4 Распределенные транзакции. 2PC и Saga в Go.

2PC - координатор запрашивает prepare у участников, при всех "готов" шлет commit; при отказе - rollback. Блокировки до решения. Saga - цепочка локальных транзакций с компенсациями при сбое. В Go 2PC редко (сложность, блокировки); чаще Saga с событиями и компенсирующими действиями или паттерн "каждый сервис в своей транзакции + outbox".

Открыть отдельно →
5 Порядок сообщений в распределенной системе. Go и Kafka.

В Kafka порядок гарантируется в пределах партиции; между партициями порядка нет. Для упорядоченной обработки по ключу используют один ключ на логическую сущность (user_id) - все события пользователя в одну партицию. В Go консьюмер обрабатывает сообщения из партиции последовательно (или с ограниченным параллелизмом по ключу). Идемпотентность и версии событий помогают при дублях.

producer.Produce(&kafka.Message{
    Key:   kafka.EncodeKey(userID),
    Value: payload,
})
Открыть отдельно →
6 Идемпотентные ключи при распределенных вызовах в Go.

Клиент генерирует уникальный ключ (idempotency key) и шлет с запросом; сервер кеширует результат по ключу и при повторном запросе с тем же ключом возвращает сохраненный ответ. В Go: middleware извлекает ключ из заголовка, проверяет хранилище (Redis, БД); при совпадении - возврат кеша, иначе выполнение handler и сохранение. Защита от дублей при ретраях и таймаутах.

key := r.Header.Get("Idempotency-Key")
if key == "" { ... }
if cached := store.Get(key); cached != nil { writeCached(w, cached); return }
resp := handle(r)
store.Set(key, resp, ttl)
Открыть отдельно →
7 Распределенные блокировки в Go. Redis, etcd.

Блокировка в распределенной среде: один узел владеет блокировкой (по ключу ресурса). Redis: SET key value NX EX ttl; при успехе - владеем, по окончании - DEL или скрипт Lua. Redlock - несколько инстансов Redis для надежности. etcd: аренда (lease) и сравнение-и-запись. В Go библиотеки: go-redis с SetNX, etcd client с lease. Важно таймаут и продление блокировки при долгой работе.

ok := rdb.SetNX(ctx, "lock:resource", "owner", 10*time.Second).Val()
if !ok { return ErrLocked }
defer rdb.Del(ctx, "lock:resource")
Открыть отдельно →
8 CRDT и бесконфликтные структуры данных.

CRDT (Conflict-free Replicated Data Type) - структуры данных, сходимость реплик без координации (коммутативность и идемпотентность операций). Примеры: G-Counter, LWW-Register. Используют в распределенных редакторах, счетчиках. В Go реализации есть в библиотеках (crdt); приложение чаще использует готовые хранилища с CRDT (например, Redis с CRDT в кластере) или версионные конфликты в БД.

Открыть отдельно →
9 Vector clocks в распределенных системах.

Vector clock - метка события с счетчиками по узлам; позволяет сравнивать порядок событий (произошло ли A до B). Используют для обнаружения конфликтов и причинно-следственного порядка. В Go хранят как map[node]int или срез; при обновлении увеличивают свой счетчик и мержат с полученными. Применение: распределенные хранилища, конфликт-резолюция.

type VectorClock map[string]int64
func (v VectorClock) Merge(other VectorClock) {
    for node, t := range other {
        if t > v[node] { v[node] = t }
    }
}
Открыть отдельно →
10 Service mesh (Istio, Linkerd) и Go-сервисы.

Service mesh - слой управления трафиком между сервисами (sidecar-прокси): retry, timeout, mTLS, метрики, трейсинг. Go-сервис не меняет код; mesh перехватывает исходящие и входящие вызовы. В K8s sidecar (Envoy) инжектируется в Pod. В Go приложение должно поддерживать трейсинг (пропигация заголовков) и корректные таймауты; остальное берет на себя mesh.

Открыть отдельно →
🧠Квиз 🏆Лидеры 🎯Собесед. 📖Вопросы 📚База зн.