Understanding the Physical and Digital Divide Between Birmingham and Barcelona
Birmingham's connectivity to the rest of Europe follows a well-worn path: terrestrial fiber Across the UK's backbone, then subsea cables under the Channel into northern France, before transiting down through existing backbones that reach Catalonia. The Birmingham - Barcelona signal doesn't enjoy a dedicated subsea cable; instead, it rides multiple carrier aggregation layers, typically through providers like Lumen, Colt. And the pan-European GÉANT research network. That multi-hop reality introduces not just propagation delay but jitter from intermediate router queues and varying PeeringDB policies.
For a software team, this means you can't simply assume a clean, predictable pipe. When we first lit up a dedicated wavelength between our two Equinix facilities (LD5 in London for Birmingham peering and BA1 in Barcelona), the physical path took an extra 3 ms detour through Paris because of a maintenance event on the normal Bordeaux route. We learned to treat the Birmingham-Barcelona link as a dynamic system, not a static cable. Our solution: build a continuous round-trip time (RTT) measurement pipeline using Cisco IOS-XR streaming telemetry combined with synthetic probing from OpenTelemetry Collector's TCP connectivity receiver, feeding into Grafana Mimir for long-term latencies analysis.
Measuring Real-World Latency: Our RTT Benchmarks and Jitter Profiles
Over a 30-day observation window, the Birmingham - Barcelona link delivered a median RTT of 22. 4 ms, with a p99 of 28. And 1 msJitter, measured via RFC 3550 RTP control protocol timestamps, stayed under 1. 8 ms for 99. And 9% of samplesThese numbers are critical because they define the theoretical lower bound for synchronous replication. With a 22 ms RTT, a single-phase commit in a distributed SQL database blocks for at least 22 ms if you demand strong consistency on every write. That might sound acceptable. But in a write-heavy IoT ingestion pipeline pushing 60,000 events per second, it adds up to 1,320 seconds of cumulative delay per second-physically impossible without massive batching.
We built a home-grown benchmarking tool in Go, using raw sockets with TCP_NODELAY and tracking both kernel-level RTT and user-space transaction latencies. The tests confirmed what the CAP theorem teaches: you can't have low latency, consistency. And partition tolerance simultaneously. The Birmingham-Barcelona link's 22 ms floor meant that any attempt at synchronous cross-site writes would push our p95 transaction latency past 60 ms once database locking and replication overheads were included-well above our SLO of 20 ms for the primary API. This forced a hard architectural decision: embrace asynchrony.
The CAP Theorem in Practice: Consistency Trade-offs Across European Sites
Eric Brewer's CAP theorem, formalized in his 2000 PODC keynote, states that a distributed data store can provide only two of three guarantees: Consistency, Availability. And Partition tolerance. Since network partitions between Birmingham and Barcelona are a reality-we saw five short-lived partitions (>200 ms loss) per week-we had to choose Availability over strong Consistency. That meant the system must remain writable in both cities even when the link goes dark, with eventual consistency as the reconciliation mechanism.
We avoided CRDTs for our core workload because the business logic involved multi-key transactional updates (adjust inventory, create audit log entries, atomically reserve funds). Instead, we adopted a log-replay model: each site writes changes to its own transaction log. And a background merge process replays the Birmingham log in Barcelona and vice versa. Conflict resolution uses a last-writer-wins strategy with application-level fencing tokens, inspired by the DynamoDB design. But implemented over PostgreSQL logical replication slots with custom output plugins. This design let us maintain write availability during a 12-hour fiber cut between Madrid and Perpignan that isolated Barcelona from Birmingham completely.
Choosing the Right Database for a Birmingham-Barcelona Active-Active Topology
We initially evaluated CockroachDB and YugabyteDB, both of which offer geo-partitioning with follower reads. CockroachDB's cost-based optimizer can pin data to specific regions, letting us place UK customer shards in Birmingham and Spanish shards in Barcelona, with cross-region reads incurring latency only when a query spans both. However, our team's operational muscle memory was deep in PostgreSQL. And we didn't want to retool our entire data access layer. The compromise was Spanner-like logical sharding using Citus in a multi-master configuration but after a disastrous split-brain incident during a Citus node failure, we retreated to plain PostgreSQL with bidirectional replication (BDR) from 2ndQuadrant.
BDR gave us the eventual consistency we needed while keeping our familiar PostGIS extensions for geospatial queries (ironically, exactly what we needed to calculate customer proximity to the Birmingham or Barcelona warehouse). A key lesson: never run BDR without a dedicated conflict resolution HTTP endpoint that your application server calls before writing. We built a tiny gRPC service that acquires a distributed advisory lock via an external Redis cluster (sentinel-based, with quorum nodes in both cities) to serialize conflicting customer profile updates. This pattern. While ugly, completely eliminated duplicate key violations that previously crashed our order pipeline.
Asynchronous Event Replication with MirrorMaker 2 and Cross-Region Kafka
Event streaming between our Birmingham and Barcelona sites was non-negotiable: the inventory microservice in Barcelona needed real-time updates from the Birmingham point-of-sale ingestion. We deployed Apache Kafka in stretch cluster mode? That would have required Zookeeper ensembles spanning both sites, a recipe for calamity when a network partition hits. Instead, we ran independent Kafka clusters in each city and connected them using MirrorMaker 2 (MM2). Which leverages the new Kafka Connect framework to replicate topics with offset translation.
MM2's ability to rename remote topics (prefixing with the source cluster alias) allowed us to avoid infinite replication loops. We tuned the replication factor to 3 within each site, so a broker loss wouldn't cause data unavailability. To keep the Birmingham - Barcelona mirroring lag under 500 ms, we had to bump the source cluster's `replication max, and bytesper second` and ensure sufficient NIC throughput-our 10 Gbps links sat at 2. 3 Gbps of sustained replication traffic during peak hours. Observability here was crucial: we instrumented MM2 with the official JMX metrics and created Prometheus alerts for lag > 1,000 messages. Which correlated with SLO breaches downstream.
Kubernetes Cluster Federation: Scheduling Workloads Across Two Countries
Running stateless services across Birmingham and Barcelona demands a mechanism to steer traffic toward the nearest healthy instance. We adopted Kubernetes with Cluster API to manage two separate clusters, then layered Karmada (a CNCF project for multi-cluster workload propagation) on top to federate Deployments and Services. Karmada's override policy let us scale the same microservice differently in each city: 6 replicas in Birmingham to handle higher UK traffic, 4 in Barcelona, all registered in the same global DNS pool via ExternalDNS.
The real complexity wasn't scheduling; it was networking. We needed a flat, encrypted overlay between pods in Birmingham and Barcelona without relying on a VPN appliance as a single point of failure. We implemented Cilium's Cluster Mesh with transparent encryption using WireGuard. Which gave us a single /16 Pod CIDR that spanned both clusters. The eBPF-based routing kept inter-pod latency overhead under 0. 3 ms compared to bare metal, a remarkable feat that let our Kafka brokers and PostgreSQL instances talk directly over the
.Need a Custom App Built?
Let's discuss your project and bring your ideas to life.
Contact Me Today →