Interview-Fragen zu Verteilten Netzwerken: CAP, Konsens und Systemdesign (2026)

Verteilte Netzwerke sind fundamental fuer moderne Systeme - von Microservices ueber Blockchain bis hin zu globalen CDNs. Das Verstaendnis dieser Konzepte ist entscheidend fuer Senior-Engineering-Positionen. Dieser Leitfaden behandelt die haeufigsten Interviewfragen mit ausfuehrlichen Antworten und praktischen Beispielen.

Grundlagen

1. Was ist ein verteiltes System?

Antwort: Ein verteiltes System ist eine Sammlung unabhaengiger Computer, die den Benutzern als ein einziges kohaerentes System erscheinen. Wichtige Merkmale:

  • Nebenlaeufigkeit: Komponenten werden gleichzeitig ausgefuehrt
  • Keine globale Uhr: Knoten haben unabhaengige Uhren
  • Unabhaengige Ausfaelle: Komponenten koennen ausfallen, ohne andere zu beeintraechtigen
  • Nachrichtenuebermittlung: Kommunikation ueber Netzwerknachrichten

Beispiele: Google-Suche, Netflix-Streaming, Bitcoin-Netzwerk.

2. Erklaeren Sie das CAP-Theorem

Antwort: Das CAP-Theorem besagt, dass ein verteiltes System nur zwei von drei Eigenschaften garantieren kann:

  • Konsistenz: Alle Knoten sehen zur gleichen Zeit die gleichen Daten
  • Verfuegbarkeit: Jede Anfrage erhaelt eine Antwort
  • Partitionstoleranz: Das System funktioniert trotz Netzwerkausfaellen weiter

In der Praxis ist Partitionstoleranz erforderlich (Netzwerke fallen aus), daher waehlt man zwischen CP (Konsistenz) oder AP (Verfuegbarkeit):

CP Systems: MongoDB, HBase, Redis Cluster
- Sacrifice availability during partitions
- Strong consistency guarantees

AP Systems: Cassandra, DynamoDB, CouchDB
- Remain available during partitions
- Eventually consistent

3. Was ist Eventual Consistency?

Antwort: Eventual Consistency garantiert, dass alle Replikate schliesslich zum gleichen Wert konvergieren, wenn keine neuen Aktualisierungen vorgenommen werden. Es ist eine schwaechere Garantie als starke Konsistenz, ermoeglicht aber eine hoehere Verfuegbarkeit.

// Example: DNS propagation
// Update takes time to propagate globally
// Different users may see different values temporarily
// Eventually, all DNS servers have the same record

// Conflict resolution strategies:
// 1. Last-write-wins (LWW) - timestamp-based
// 2. Vector clocks - track causality
// 3. CRDTs - mathematically guaranteed convergence

4. Erklaeren Sie den Unterschied zwischen horizontaler und vertikaler Skalierung

Antwort:

AspektVertikale SkalierungHorizontale Skalierung
MethodeRessourcen zu einer Maschine hinzufuegenMehr Maschinen hinzufuegen
KostenTeuer bei SkalierungStandard-Hardware
GrenzeHardware-ObergrenzeTheoretisch unbegrenzt
KomplexitaetEinfachErfordert Verteilungslogik
AusfallzeitNormalerweise erforderlichNull Ausfallzeit moeglich

Konsens und Koordination

5. Was ist der Raft-Konsensalgorithmus?

Antwort: Raft ist ein Konsensalgorithmus zur Verwaltung eines replizierten Logs. Er wurde so konzipiert, dass er verstaendlich ist (im Gegensatz zu Paxos). Hauptkomponenten:

  • Leader-Wahl: Ein Knoten wird zum Leader gewaehlt und bearbeitet alle Client-Anfragen
  • Log-Replikation: Der Leader repliziert Eintraege an die Follower
  • Sicherheit: Nur Knoten mit aktuellen Logs koennen Leader werden
Raft states:
1. Follower - Default state, responds to leader
2. Candidate - Requesting votes for leadership
3. Leader - Handles all client requests

Election process:
1. Follower timeout expires
2. Becomes candidate, increments term
3. Requests votes from peers
4. Majority votes = new leader
5. Sends heartbeats to maintain leadership

6. Was ist ein verteiltes Lock?

Antwort: Ein verteiltes Lock stellt sicher, dass nur ein Prozess ueber mehrere Knoten hinweg auf eine Ressource zugreifen kann. Implementierungsherausforderungen:

// Redis distributed lock (Redlock algorithm)
const Redis = require('ioredis');

async function acquireLock(redis, key, ttl) {
  const token = crypto.randomUUID();
  const result = await redis.set(key, token, 'NX', 'PX', ttl);
  return result === 'OK' ? token : null;
}

async function releaseLock(redis, key, token) {
  // Lua script for atomic check-and-delete
  const script = `
    if redis.call("get", KEYS[1]) == ARGV[1] then
      return redis.call("del", KEYS[1])
    else
      return 0
    end
  `;
  return redis.eval(script, 1, key, token);
}

// Usage
const token = await acquireLock(redis, 'my-resource', 30000);
if (token) {
  try {
    // Critical section
  } finally {
    await releaseLock(redis, 'my-resource', token);
  }
}

7. Erklaeren Sie Vektoruhren

Antwort: Vektoruhren verfolgen die Kausalitaet zwischen Ereignissen in verteilten Systemen. Jeder Knoten verwaltet einen Vektor von logischen Zeitstempeln:

// Vector clock example with 3 nodes
// Initial: [0, 0, 0]

// Node A sends message: [1, 0, 0]
// Node B receives and sends: [1, 1, 0]
// Node C receives: [1, 1, 1]

// Comparison rules:
// V1 < V2 if all V1[i] <= V2[i] and at least one V1[i] < V2[i]
// V1 || V2 (concurrent) if neither V1 < V2 nor V2 < V1

class VectorClock {
  constructor(nodeId, numNodes) {
    this.nodeId = nodeId;
    this.clock = new Array(numNodes).fill(0);
  }

  increment() {
    this.clock[this.nodeId]++;
    return [...this.clock];
  }

  update(received) {
    for (let i = 0; i < this.clock.length; i++) {
      this.clock[i] = Math.max(this.clock[i], received[i]);
    }
    this.clock[this.nodeId]++;
  }

  compare(other) {
    let less = false, greater = false;
    for (let i = 0; i < this.clock.length; i++) {
      if (this.clock[i] < other[i]) less = true;
      if (this.clock[i] > other[i]) greater = true;
    }
    if (less && !greater) return -1; // this happened before
    if (greater && !less) return 1;  // other happened before
    return 0; // concurrent
  }
}

Netzwerkprotokolle

8. Vergleichen Sie TCP und UDP fuer verteilte Systeme

Antwort:

MerkmalTCPUDP
VerbindungVerbindungsorientiertVerbindungslos
ZuverlaessigkeitGarantierte ZustellungBest Effort
ReihenfolgeGeordnetKeine Reihenfolge
GeschwindigkeitLangsamer (Handshake)Schneller
AnwendungsfaelleHTTP, DatenbankenDNS, Streaming, Gaming
// When to use each:
// TCP: When you need reliability
// - Database connections
// - File transfers
// - API calls

// UDP: When speed matters more than reliability
// - Real-time gaming
// - Video streaming
// - DNS queries
// - Health checks

9. Was ist gRPC und wann wuerde man es verwenden?

Antwort: gRPC ist ein hochperformantes RPC-Framework, das Protocol Buffers und HTTP/2 verwendet:

  • Binaerprotokoll: Kleinere Payloads als JSON
  • Streaming: Unterstuetzung fuer bidirektionales Streaming
  • Code-Generierung: Typsichere Clients/Server
  • HTTP/2: Multiplexing, Header-Komprimierung
// user.proto
syntax = "proto3";

service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc ListUsers(ListUsersRequest) returns (stream User);
  rpc CreateUser(User) returns (User);
}

message User {
  string id = 1;
  string name = 2;
  string email = 3;
}

message GetUserRequest {
  string id = 1;
}

10. Erklaeren Sie die Service-Mesh-Architektur

Antwort: Ein Service Mesh ist eine Infrastrukturschicht fuer die Service-zu-Service-Kommunikation. Komponenten:

  • Datenebene: Sidecar-Proxys (Envoy) verarbeiten den Datenverkehr
  • Steuerungsebene: Verwaltet die Proxy-Konfiguration
# Istio example - traffic splitting
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-service
spec:
  hosts:
  - my-service
  http:
  - route:
    - destination:
        host: my-service
        subset: v1
      weight: 90
    - destination:
        host: my-service
        subset: v2
      weight: 10

Vorteile: mTLS, Observability, Traffic-Management, Wiederholungsversuche ohne Code-Aenderungen.

Fehlertoleranz

11. Was ist das Circuit-Breaker-Pattern?

Antwort: Der Circuit Breaker verhindert kaskadierende Ausfaelle, indem er schnell fehlschlaegt, wenn ein Dienst nicht gesund ist:

class CircuitBreaker {
  constructor(options) {
    this.failureThreshold = options.failureThreshold || 5;
    this.resetTimeout = options.resetTimeout || 30000;
    this.state = 'CLOSED';
    this.failures = 0;
    this.lastFailure = null;
  }

  async call(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailure > this.resetTimeout) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  onSuccess() {
    this.failures = 0;
    this.state = 'CLOSED';
  }

  onFailure() {
    this.failures++;
    this.lastFailure = Date.now();
    if (this.failures >= this.failureThreshold) {
      this.state = 'OPEN';
    }
  }
}

12. Erklaeren Sie das Bulkhead-Pattern

Antwort: Bulkhead isoliert Komponenten, damit Ausfaelle nicht kaskadieren. Wie Schiffskompartimente:

// Thread pool bulkhead
const criticalPool = new ThreadPool({ size: 10 });
const nonCriticalPool = new ThreadPool({ size: 5 });

// If non-critical service exhausts its pool,
// critical operations still have dedicated resources

// Semaphore bulkhead
class Bulkhead {
  constructor(maxConcurrent) {
    this.maxConcurrent = maxConcurrent;
    this.current = 0;
    this.queue = [];
  }

  async execute(fn) {
    if (this.current >= this.maxConcurrent) {
      throw new Error('Bulkhead full');
    }

    this.current++;
    try {
      return await fn();
    } finally {
      this.current--;
    }
  }
}

13. Was ist Byzantine Fault Tolerance?

Antwort: BFT behandelt Knoten, die sich beliebig verhalten (boeswillig oder aufgrund von Fehlern). Das Problem der Byzantinischen Generaele:

  • Erfordert 3f+1 Knoten, um f byzantinische Fehler zu tolerieren
  • Wird in Blockchain verwendet (PBFT, Tendermint)
  • Aufwendiger als Crash-Fehlertoleranz
PBFT phases:
1. Pre-prepare: Leader proposes value
2. Prepare: Nodes broadcast prepared messages
3. Commit: Nodes commit after 2f+1 prepares
4. Reply: Send result to client

Requires 2f+1 matching messages at each phase

Lastverteilung und Routing

14. Vergleichen Sie Lastverteilungsalgorithmen

Antwort:

// Round Robin - Simple rotation
class RoundRobin {
  constructor(servers) {
    this.servers = servers;
    this.current = 0;
  }

  next() {
    const server = this.servers[this.current];
    this.current = (this.current + 1) % this.servers.length;
    return server;
  }
}

// Weighted Round Robin - Based on capacity
class WeightedRoundRobin {
  constructor(servers) {
    // servers: [{ host: 'a', weight: 3 }, { host: 'b', weight: 1 }]
    this.servers = [];
    for (const s of servers) {
      for (let i = 0; i < s.weight; i++) {
        this.servers.push(s.host);
      }
    }
    this.current = 0;
  }
}

// Least Connections - Route to least busy
class LeastConnections {
  constructor(servers) {
    this.connections = new Map(servers.map(s => [s, 0]));
  }

  next() {
    let min = Infinity, selected;
    for (const [server, count] of this.connections) {
      if (count < min) {
        min = count;
        selected = server;
      }
    }
    return selected;
  }
}

// Consistent Hashing - For caches/sharding
// Minimizes redistribution when nodes change

15. Erklaeren Sie Consistent Hashing

Antwort: Consistent Hashing verteilt Daten ueber Knoten, waehrend die Umverteilung beim Hinzufuegen/Entfernen von Knoten minimiert wird:

const crypto = require('crypto');

class ConsistentHash {
  constructor(replicas = 100) {
    this.replicas = replicas;
    this.ring = new Map();
    this.sortedKeys = [];
  }

  hash(key) {
    return crypto.createHash('md5')
      .update(key)
      .digest('hex')
      .substring(0, 8);
  }

  addNode(node) {
    for (let i = 0; i < this.replicas; i++) {
      const hash = this.hash(`${node}:${i}`);
      this.ring.set(hash, node);
      this.sortedKeys.push(hash);
    }
    this.sortedKeys.sort();
  }

  removeNode(node) {
    for (let i = 0; i < this.replicas; i++) {
      const hash = this.hash(`${node}:${i}`);
      this.ring.delete(hash);
      this.sortedKeys = this.sortedKeys.filter(k => k !== hash);
    }
  }

  getNode(key) {
    const hash = this.hash(key);
    for (const nodeHash of this.sortedKeys) {
      if (hash <= nodeHash) {
        return this.ring.get(nodeHash);
      }
    }
    return this.ring.get(this.sortedKeys[0]);
  }
}

Messaging und Warteschlangen

16. Vergleichen Sie Message-Queue-Patterns

Antwort:

  • Point-to-Point: Ein Producer, ein Consumer (Task-Warteschlangen)
  • Pub/Sub: Ein Producer, mehrere Consumer (Event-Broadcasting)
  • Request/Reply: Synchrones Messaging-Pattern
Delivery guarantees:
- At most once: May lose messages (fastest)
- At least once: May duplicate (requires idempotency)
- Exactly once: Most complex, often needs transactions

Technologies:
- RabbitMQ: Traditional message broker, AMQP
- Kafka: Distributed log, high throughput
- Redis Streams: Simple, built into Redis
- AWS SQS: Managed, scalable

17. Was ist das Outbox-Pattern?

Antwort: Das Outbox-Pattern stellt zuverlaessiges Message-Publishing mit Datenbanktransaktionen sicher:

// Instead of:
await db.transaction(async (tx) => {
  await tx.insert('orders', order);
  await messageQueue.publish('order.created', order); // Can fail!
});

// Use outbox pattern:
await db.transaction(async (tx) => {
  await tx.insert('orders', order);
  await tx.insert('outbox', {
    event_type: 'order.created',
    payload: JSON.stringify(order),
    created_at: new Date()
  });
});

// Separate process polls outbox and publishes
async function processOutbox() {
  const events = await db.query(
    'SELECT * FROM outbox WHERE processed = false LIMIT 100'
  );

  for (const event of events) {
    await messageQueue.publish(event.event_type, event.payload);
    await db.update('outbox', { id: event.id }, { processed: true });
  }
}

Observability

18. Was ist Distributed Tracing?

Antwort: Distributed Tracing verfolgt Anfragen ueber Service-Grenzen hinweg:

// OpenTelemetry example
const { trace } = require('@opentelemetry/api');

const tracer = trace.getTracer('my-service');

async function handleRequest(req) {
  const span = tracer.startSpan('handleRequest');

  try {
    span.setAttribute('user.id', req.userId);

    // Child span for database call
    const dbSpan = tracer.startSpan('database.query', {
      parent: span
    });
    const result = await db.query('...');
    dbSpan.end();

    return result;
  } catch (error) {
    span.recordException(error);
    span.setStatus({ code: SpanStatusCode.ERROR });
    throw error;
  } finally {
    span.end();
  }
}

// Trace context propagation
// W3C Trace Context header: traceparent
// Format: version-trace_id-span_id-flags
// Example: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01

19. Erklaeren Sie die drei Saeulen der Observability

Antwort:

  • Logs: Diskrete Ereignisse mit Kontext
  • Metriken: Numerische Messungen ueber Zeit
  • Traces: Anfragefluss ueber Services hinweg
Logs: What happened?
- Structured JSON logs
- Correlation IDs
- Log levels (debug, info, warn, error)

Metrics: How is the system performing?
- Counters: request_count
- Gauges: active_connections
- Histograms: request_duration

Traces: Where did the request go?
- Spans with timing
- Parent-child relationships
- Cross-service context

Sicherheit

20. Wie sichert man die Service-zu-Service-Kommunikation?

Antwort:

  • mTLS: Gegenseitige TLS-Authentifizierung
  • Service Mesh: Automatisches mTLS (Istio, Linkerd)
  • API-Schluessel: Einfach, aber weniger sicher
  • JWT: Zustandslose Authentifizierung
# Istio PeerAuthentication for mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: my-namespace
spec:
  mtls:
    mode: STRICT

System-Design-Fragen

21. Entwerfen Sie einen verteilten Rate Limiter

Antwort:

// Token bucket with Redis
class DistributedRateLimiter {
  constructor(redis, options) {
    this.redis = redis;
    this.capacity = options.capacity;
    this.refillRate = options.refillRate; // tokens per second
  }

  async isAllowed(key) {
    const now = Date.now();
    const script = `
      local key = KEYS[1]
      local capacity = tonumber(ARGV[1])
      local refillRate = tonumber(ARGV[2])
      local now = tonumber(ARGV[3])

      local bucket = redis.call('HMGET', key, 'tokens', 'lastRefill')
      local tokens = tonumber(bucket[1]) or capacity
      local lastRefill = tonumber(bucket[2]) or now

      -- Refill tokens
      local elapsed = (now - lastRefill) / 1000
      tokens = math.min(capacity, tokens + elapsed * refillRate)

      if tokens >= 1 then
        tokens = tokens - 1
        redis.call('HMSET', key, 'tokens', tokens, 'lastRefill', now)
        redis.call('EXPIRE', key, 60)
        return 1
      else
        return 0
      end
    `;

    return await this.redis.eval(script, 1, key,
      this.capacity, this.refillRate, now);
  }
}

22. Wie wuerden Sie einen verteilten Cache entwerfen?

Antwort: Wichtige Ueberlegungen:

  • Partitionierung: Consistent Hashing ueber Knoten
  • Replikation: Primaer-Replikat fuer Fehlertoleranz
  • Eviction: LRU-, LFU- oder TTL-basiert
  • Konsistenz: Write-Through, Write-Behind oder Cache-Aside
Cache-aside pattern:
1. Check cache
2. If miss, read from database
3. Update cache
4. Return data

Write-through pattern:
1. Write to cache
2. Cache writes to database
3. Ensures consistency but adds latency

Write-behind pattern:
1. Write to cache
2. Cache async writes to database
3. Better performance, eventual consistency

Fazit

Verteilte Systeme sind komplex, aber das Verstaendnis dieser Kernkonzepte - Konsens, Fehlertoleranz, Netzwerke und Observability - wird Sie auf Senior-Engineering-Interviews vorbereiten. Konzentrieren Sie sich auf Kompromisse: Es gibt selten eine perfekte Loesung, nur Kompromisse, die fuer bestimmte Anforderungen angemessen sind.