10 вопросов
При разделении (partition) нельзя одновременно гарантировать Consistency, Availability и Partition tolerance. CP - при партиции жертвуют доступностью (ждать консенсус). AP - жертвуют консистентностью (eventual consistency). В Go при выборе хранилищ и протоколов учитывают: БД чаще CP; кеши, очереди - часто AP. Приложение может маскировать недоступность ретраями и кешем.
Strong - все реплики видят одни и те же данные в один момент. Eventual - после прекращения обновлений все придут к одному состоянию. Causal - сохранение причинно-следственных связей. В Go при работе с репликами и кешем учитывают: чтение после записи может требовать чтения с primary; идемпотентность и версии для разрешения конфликтов при eventual.
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"}})2PC - координатор запрашивает prepare у участников, при всех "готов" шлет commit; при отказе - rollback. Блокировки до решения. Saga - цепочка локальных транзакций с компенсациями при сбое. В Go 2PC редко (сложность, блокировки); чаще Saga с событиями и компенсирующими действиями или паттерн "каждый сервис в своей транзакции + outbox".
В Kafka порядок гарантируется в пределах партиции; между партициями порядка нет. Для упорядоченной обработки по ключу используют один ключ на логическую сущность (user_id) - все события пользователя в одну партицию. В Go консьюмер обрабатывает сообщения из партиции последовательно (или с ограниченным параллелизмом по ключу). Идемпотентность и версии событий помогают при дублях.
producer.Produce(&kafka.Message{
Key: kafka.EncodeKey(userID),
Value: payload,
})Клиент генерирует уникальный ключ (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)Блокировка в распределенной среде: один узел владеет блокировкой (по ключу ресурса). 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")CRDT (Conflict-free Replicated Data Type) - структуры данных, сходимость реплик без координации (коммутативность и идемпотентность операций). Примеры: G-Counter, LWW-Register. Используют в распределенных редакторах, счетчиках. В Go реализации есть в библиотеках (crdt); приложение чаще использует готовые хранилища с CRDT (например, Redis с CRDT в кластере) или версионные конфликты в БД.
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 }
}
}Service mesh - слой управления трафиком между сервисами (sidecar-прокси): retry, timeout, mTLS, метрики, трейсинг. Go-сервис не меняет код; mesh перехватывает исходящие и входящие вызовы. В K8s sidecar (Envoy) инжектируется в Pod. В Go приложение должно поддерживать трейсинг (пропигация заголовков) и корректные таймауты; остальное берет на себя mesh.