Click Below to Get the Code

Browse, clone, and build from real-world templates powered by Harper.
Blog
GitHub Logo

The Distance Problem: Why Centralized Data Paths Cost You Latency, Uptime, and Money

Cross-region latency comes from distance, not slow queries. Why centralized data paths cost you latency, uptime, and money, with a benchmark you can run.
System Design
Blog
System Design

The Distance Problem: Why Centralized Data Paths Cost You Latency, Uptime, and Money

Austin Akers
Head of Developer Relations
at Harper
August 24, 2026
Austin Akers
Head of Developer Relations
at Harper
August 24, 2026
Austin Akers
Head of Developer Relations
at Harper
August 24, 2026
August 24, 2026
Cross-region latency comes from distance, not slow queries. Why centralized data paths cost you latency, uptime, and money, with a benchmark you can run.
Austin Akers
Head of Developer Relations

Inter-region physics, replication topologies, and a benchmark you can run yourself.

A distributed application can look healthy until its users spread out. You can add cores, tune queries, and buy bigger instances, and in-region traffic stays fast. But the moment application logic in Frankfurt reaches back to a primary database in Virginia on every request, you begin paying an ocean crossing on every dependent query, for every user, on every request that follows. No amount of vertical scaling removes that cost, because the cost is distance, and distance is bounded by physics.

Most experienced teams already know the standard responses, and for many workloads they are exactly the right ones. The narrower question is the one worth working through: when latency comes from distance rather than from slow queries, which of those responses actually help, and what does each one cost to run? Here is where the tax comes from, what your options are, and how colocating logic with data changes the arithmetic.

The cost is physical, and it compounds

Before choosing an architecture, it helps to separate the latency you can tune from the latency you cannot. Four costs come straight from distance, and none of them yield to a faster instance. A fifth cost, the business one, sits on top of the rest.

Latency has a floor. Light in fiber travels at roughly two-thirds of vacuum speed, about 5 microseconds per kilometer, so roughly 5 ms one way per 1,000 km (HPBN). New York to London has a fiber floor near 55 ms, and the purpose-built Hibernia Express cable was engineered down to about 58.95 ms RTT and gets no closer. AWS us-east-1 to eu-west-1 measures about 67.9 ms RTT (Economize). This is not a number a cloud provider can engineer away. It is the starting line, not a tuning target.

One round trip becomes ten. A TLS 1.2 handshake is two round trips before any query runs. Real endpoints are chatty: load the user, then permissions, then cart, then pricing, then inventory. If those five queries are dependent, that is five sequential round trips. At 90 ms Frankfurt to Virginia, that is over half a second of pure network wait before the database does a microsecond of work. Fan-out makes the tail worse, because a request inherits the slowest of its calls.

Availability multiplies, it does not average. Serial dependencies compound: ten components at 99.9% each yield roughly 99.0% combined. Make one of them a single-region database and its blast radius is your whole application, everywhere. On October 19 to 20, 2025, a race condition in DynamoDB's automated DNS management wiped the us-east-1 DynamoDB DNS records and cascaded for roughly 15 hours across more than 140 AWS services, taking down Snapchat, Fortnite, Ring, Venmo, and Amazon retail (ThousandEyes). The proximate cause was small. The blast radius was enormous, because so much depended on one regional data service. The lesson is not that DynamoDB is fragile. It is that centralizing a dependency centralizes its failures.

Egress is a recurring line item. Internet egress runs about $0.09/GB on AWS, $0.087/GB on Azure, and $0.085 to $0.12/GB on GCP. The quieter cost is internal: cross-AZ traffic is $0.01/GB each direction on AWS and GCP, so $0.02 per round trip on data that never leaves the cloud (EgressCost). Data transfer commonly runs 6 to 12 percent of a cloud bill. The EU Data Act is worth a caveat here: it bans switching charges from January 12, 2027, but Recital 99 explicitly preserves charges for ongoing parallel and multi-cloud use (Kemp IT Law). Free egress on exit changed the cost of leaving, not the cost of running a chatty path.

The business pays for the milliseconds. Treat the canonical numbers as directional, not gospel. Greg Linden's 2006 Amazon A/B result (100 ms of delay, about 1% of sales) is twenty years old and was never peer reviewed. Bing reported roughly 1.2% revenue loss per 500 ms at Velocity 2009. The defensible modern anchor is Core Web Vitals: Interaction to Next Paint at or under 200 ms at the 75th percentile. A cross-ocean round trip eats that entire budget in one hop.

Replication topologies, honestly

Two corrections first, because the usual shorthand hides the trade-off you actually face. CAP is not "pick two": it says that during a partition you choose consistency or availability. PACELC completes it: else, in normal operation, you choose between latency and consistency. That "else" clause is the one you meet on every request, and each topology below is a different answer to it.

Leader-based (PostgreSQL streaming replication, Aurora Global Database). One leader serializes writes and ships a log. Strong reads on the leader, stale reads on async replicas. Postgres streaming replication is async by default, so a committed transaction may not have reached a replica when the primary dies. Sync replication removes that loss and adds a full round trip to every commit. Aurora Global Database runs roughly 1 second RPO with unplanned failover RTO in minutes, and managed switchover dropped to typically under 30 seconds in May 2025. Reads scale, writes do not.

Multi-leader (DynamoDB Global Tables, Cosmos DB multi-region writes, MySQL Group Replication). Every region takes writes, then they reconcile. DynamoDB's default replicates asynchronously and resolves conflicts by last-writer-wins on item timestamps; the newer multi-Region strong consistency mode replicates synchronously and returns retryable conflict errors instead. Cosmos DB cannot offer strong consistency with multi-region writes enabled, and defaults to last-writer-wins on _ts. Last-writer-wins always converges, and it silently discards the losing write. Fine for a cache entry, dangerous for a balance

Leaderless (Cassandra, ScyllaDB, Riak). Any node accepts writes, and quorums tune consistency. With N replicas, R + W > N guarantees a read overlaps a node that saw the latest write. Supporting machinery from the Dynamo paper: read repair, anti-entropy with Merkle trees, hinted handoff, and version vectors or CRDTs to detect and merge concurrent writes. Reads and writes scale linearly, and the operational burden is real.

Consensus (Spanner, CockroachDB). Data is sharded into ranges, each running Paxos or Raft, with Spanner's TrueTime bounding clock uncertainty for external consistency. Both default to SERIALIZABLE, and CockroachDB targets about 4.5 second RTO with near-zero RPO. The price is written into physics: a write must reach a quorum, and if that quorum spans regions, every write pays a cross-region round trip. There is no way around it if you insist on strong consistency across distance.

What Harper changes

Every option so far treats the network between the application and the database as fixed, then works to hide it behind a replica, a cache, or a quorum. What if the network is the thing to remove?

Harper removes the network from between the application and the data, then lets you choose which data lives where.

One process, no internal hop. Database, cache, application runtime, and messaging (MQTT, WebSockets, SSE) run in a single in-memory Node.js process (GitHub). In a conventional stack, your logic makes a network call to the database even inside the same data center: serialize, cross the socket, wait, deserialize. In Harper, logic runs inside the storage process and calls the data directly.

// schema.graphql
type Product @table {
 id: ID @primaryKey
 name: String @indexed
 price: Float
 inventory: Int
}

// resources.js
export class Product extends tables.Product {
 static async get(target) {
   const record = await super.get(target); // in-process read, no socket
   return record ? { ...record, inStock: record.inventory > 0 } : record;
 }
}

Where it sits in the taxonomy. Harper replication is peer-to-peer over WebSockets with gossip discovery and mTLS, and it is asynchronous with eventual consistency across nodes (docs). Within a node, operations are ACID with serializable isolation. Across nodes, conflict resolution is last-writer-wins by logical timestamp, with CRDT support since 4.3 for counters and independent property merges. In PACELC terms this is AP/EL, the same family as DynamoDB Global Tables by default, not the same family as Spanner. Harper does not give you global serializability, and you should say that out loud in your design review.

Selective replication is the cost and compliance lever. You can restrict replication per database, exclude tables with @table(replicate: false), set one-way flow with per-route sends and receives flags, cap copies with replicateTo, and compute per-record residency with setResidencyById. So a Frankfurt node holds only EU data, an APAC node only APAC data, and an analytics node subscribes to everything without publishing back. That cuts cross-region bandwidth and supports residency requirements, unlike a global table that copies everything everywhere.

The collapsed stack. A conventional global architecture needs a CDN, a cache tier, app servers, a database with replicas, a message broker, and cross-region sync tooling, each deployed and monitored in every region. Harper collapses the middle four into one process with replication built into the runtime. Fewer hops is the latency win. Fewer failure domains and vendors is often the bigger one.

Run the benchmark yourself

The claim above is easy to state and cheap to test. Deploy three Harper nodes in genuinely distant regions (us-east-1, eu-west-1, ap-southeast-1), then measure two read paths from each: the centralized path (all load generators target the node in region A) and the colocated path (each generator targets its local node).

Methodology that matters: discard 2,000 warm-up requests; use HTTP keep-alive and measure the TLS handshake separately with curl -w "tls=%{time_appconnect}s" rather than smearing it across samples; report p50, p95, and p99, never averages; and drive load open-loop. That last one is the subtle one. A closed-loop client stops sending when the server stalls, so it never records how bad the stall was. The fix is to schedule sends on a fixed cadence and measure latency from the intended send time.

// bench.mjs (core loop). node bench.mjs --url http://HOST:9926/Product/sku-1 --rate 500 --seconds 120
const interval = 1000 / rate;
const start = performance.now();
const inflight = [];
for (let i = 0; i < rate * seconds; i++) {
 const intended = start + i * interval;
 const now = performance.now();
 if (intended > now) await new Promise((r) => setTimeout(r, intended - now));
 // Do not await: fire on schedule regardless of server state.
 inflight.push(request().then(() => {
   samples[count++] = (performance.now() - intended) * 1000; // vs intended, not actual
 }));
}
await Promise.all(inflight);

Expected magnitudes below are derived from published RTT figures, not measured Harper results. Replace them with your own numbers.

The flatness of the right column, not any single number in it, is the argument. Harper is not making the network faster. On the colocated path, the network is no longer in the read. The tradeoff you accept in return is that a local replica may briefly lag a write made in another region.

When to choose it, and when not to

The honest way to read the options is side by side, with the costs visible next to the wins.

Choose Harper's colocated model when global read latency dominates, your workload tolerates asynchronous convergence, and you want to cut both hops and operational surface area with per-table control over what lives where.

Do not choose it when you need strict global serializability (money movement, distributed locks, globally unique inventory decrements), when the same keys see heavy concurrent writes from multiple regions and every update must survive, or for heavy OLAP. Use Harper as the serving layer and feed a warehouse from it.

The distance between your users and your data is fixed, and no architecture on this page changes that. What stays a choice is how many times each request has to cross it, and how much of your operational surface you are willing to keep in every region to avoid the crossing.

The architecture

Run the benchmark yourself

The claim above is easy to state and cheap to test. Deploy three Harper nodes in genuinely distant regions (us-east-1, eu-west-1, ap-southeast-1), then measure two read paths from each: the centralized path (all load generators target the node in region A) and the colocated path (each generator targets its local node).

Methodology that matters: discard 2,000 warm-up requests; use HTTP keep-alive and measure the TLS handshake separately with curl -w "tls=%{time_appconnect}s" rather than smearing it across samples; report p50, p95, and p99, never averages; and drive load open-loop. That last one is the subtle one. A closed-loop client stops sending when the server stalls, so it never records how bad the stall was. The fix is to schedule sends on a fixed cadence and measure latency from the intended send time.

// bench.mjs (core loop). node bench.mjs --url http://HOST:9926/Product/sku-1 --rate 500 --seconds 120
const interval = 1000 / rate;
const start = performance.now();
const inflight = [];
for (let i = 0; i < rate * seconds; i++) {
 const intended = start + i * interval;
 const now = performance.now();
 if (intended > now) await new Promise((r) => setTimeout(r, intended - now));
 // Do not await: fire on schedule regardless of server state.
 inflight.push(request().then(() => {
   samples[count++] = (performance.now() - intended) * 1000; // vs intended, not actual
 }));
}
await Promise.all(inflight);

Expected magnitudes below are derived from published RTT figures, not measured Harper results. Replace them with your own numbers.

Load origin Centralized p50 Colocated p50
Virginia 1 to 3 ms 1 to 3 ms
Ireland 70 to 80 ms 1 to 3 ms
Singapore 230 to 260 ms 1 to 3 ms

The flatness of the right column, not any single number in it, is the argument. Harper is not making the network faster. On the colocated path, the network is no longer in the read. The tradeoff you accept in return is that a local replica may briefly lag a write made in another region

When to choose it, and when not to

The honest way to read the options is side by side, with the costs visible next to the wins.

Architecture Global read latency Write latency Consistency Conflict burden RTO / RPO Moving parts Egress Residency control
Single-region centralized Poor for distant users Good in-region Strong None App down on region loss Fewest, no HA High Simple
Leader + read replicas Good near replica, stale reads Cross-region to leader Strong on leader, eventual on replicas Low Minutes / about 1s Moderate Moderate Moderate
Multi-leader Good Good Eventual by default High (LWW) Low / low Managed, but conflicts are yours Moderate to high Weak by default
Leaderless P2P Good Tunable Tunable via R + W > N High Degrades gracefully Many Moderate to high Moderate
Consensus Good reads Poor across regions Serializable, global None Seconds / about 0 Moderate High Good
Harper colocated Excellent, in-process Local write fast, async converge Per-node ACID, cross-node eventual Moderate (LWW plus CRDTs) Multi-active Fewest Low (selective) Strong

Choose Harper's colocated model when global read latency dominates, your workload tolerates asynchronous convergence, and you want to cut both hops and operational surface area with per-table control over what lives where.

Do not choose it when you need strict global serializability (money movement, distributed locks, globally unique inventory decrements), when the same keys see heavy concurrent writes from multiple regions and every update must survive, or for heavy OLAP. Use Harper as the serving layer and feed a warehouse from it.

The distance between your users and your data is fixed, and no architecture on this page changes that. What stays a choice is how many times each request has to cross it, and how much of your operational surface you are willing to keep in every region to avoid the crossing.

Inter-region physics, replication topologies, and a benchmark you can run yourself.

A distributed application can look healthy until its users spread out. You can add cores, tune queries, and buy bigger instances, and in-region traffic stays fast. But the moment application logic in Frankfurt reaches back to a primary database in Virginia on every request, you begin paying an ocean crossing on every dependent query, for every user, on every request that follows. No amount of vertical scaling removes that cost, because the cost is distance, and distance is bounded by physics.

Most experienced teams already know the standard responses, and for many workloads they are exactly the right ones. The narrower question is the one worth working through: when latency comes from distance rather than from slow queries, which of those responses actually help, and what does each one cost to run? Here is where the tax comes from, what your options are, and how colocating logic with data changes the arithmetic.

The cost is physical, and it compounds

Before choosing an architecture, it helps to separate the latency you can tune from the latency you cannot. Four costs come straight from distance, and none of them yield to a faster instance. A fifth cost, the business one, sits on top of the rest.

Latency has a floor. Light in fiber travels at roughly two-thirds of vacuum speed, about 5 microseconds per kilometer, so roughly 5 ms one way per 1,000 km (HPBN). New York to London has a fiber floor near 55 ms, and the purpose-built Hibernia Express cable was engineered down to about 58.95 ms RTT and gets no closer. AWS us-east-1 to eu-west-1 measures about 67.9 ms RTT (Economize). This is not a number a cloud provider can engineer away. It is the starting line, not a tuning target.

One round trip becomes ten. A TLS 1.2 handshake is two round trips before any query runs. Real endpoints are chatty: load the user, then permissions, then cart, then pricing, then inventory. If those five queries are dependent, that is five sequential round trips. At 90 ms Frankfurt to Virginia, that is over half a second of pure network wait before the database does a microsecond of work. Fan-out makes the tail worse, because a request inherits the slowest of its calls.

Availability multiplies, it does not average. Serial dependencies compound: ten components at 99.9% each yield roughly 99.0% combined. Make one of them a single-region database and its blast radius is your whole application, everywhere. On October 19 to 20, 2025, a race condition in DynamoDB's automated DNS management wiped the us-east-1 DynamoDB DNS records and cascaded for roughly 15 hours across more than 140 AWS services, taking down Snapchat, Fortnite, Ring, Venmo, and Amazon retail (ThousandEyes). The proximate cause was small. The blast radius was enormous, because so much depended on one regional data service. The lesson is not that DynamoDB is fragile. It is that centralizing a dependency centralizes its failures.

Egress is a recurring line item. Internet egress runs about $0.09/GB on AWS, $0.087/GB on Azure, and $0.085 to $0.12/GB on GCP. The quieter cost is internal: cross-AZ traffic is $0.01/GB each direction on AWS and GCP, so $0.02 per round trip on data that never leaves the cloud (EgressCost). Data transfer commonly runs 6 to 12 percent of a cloud bill. The EU Data Act is worth a caveat here: it bans switching charges from January 12, 2027, but Recital 99 explicitly preserves charges for ongoing parallel and multi-cloud use (Kemp IT Law). Free egress on exit changed the cost of leaving, not the cost of running a chatty path.

The business pays for the milliseconds. Treat the canonical numbers as directional, not gospel. Greg Linden's 2006 Amazon A/B result (100 ms of delay, about 1% of sales) is twenty years old and was never peer reviewed. Bing reported roughly 1.2% revenue loss per 500 ms at Velocity 2009. The defensible modern anchor is Core Web Vitals: Interaction to Next Paint at or under 200 ms at the 75th percentile. A cross-ocean round trip eats that entire budget in one hop.

Replication topologies, honestly

Two corrections first, because the usual shorthand hides the trade-off you actually face. CAP is not "pick two": it says that during a partition you choose consistency or availability. PACELC completes it: else, in normal operation, you choose between latency and consistency. That "else" clause is the one you meet on every request, and each topology below is a different answer to it.

Leader-based (PostgreSQL streaming replication, Aurora Global Database). One leader serializes writes and ships a log. Strong reads on the leader, stale reads on async replicas. Postgres streaming replication is async by default, so a committed transaction may not have reached a replica when the primary dies. Sync replication removes that loss and adds a full round trip to every commit. Aurora Global Database runs roughly 1 second RPO with unplanned failover RTO in minutes, and managed switchover dropped to typically under 30 seconds in May 2025. Reads scale, writes do not.

Multi-leader (DynamoDB Global Tables, Cosmos DB multi-region writes, MySQL Group Replication). Every region takes writes, then they reconcile. DynamoDB's default replicates asynchronously and resolves conflicts by last-writer-wins on item timestamps; the newer multi-Region strong consistency mode replicates synchronously and returns retryable conflict errors instead. Cosmos DB cannot offer strong consistency with multi-region writes enabled, and defaults to last-writer-wins on _ts. Last-writer-wins always converges, and it silently discards the losing write. Fine for a cache entry, dangerous for a balance

Leaderless (Cassandra, ScyllaDB, Riak). Any node accepts writes, and quorums tune consistency. With N replicas, R + W &gt; N guarantees a read overlaps a node that saw the latest write. Supporting machinery from the Dynamo paper: read repair, anti-entropy with Merkle trees, hinted handoff, and version vectors or CRDTs to detect and merge concurrent writes. Reads and writes scale linearly, and the operational burden is real.

Consensus (Spanner, CockroachDB). Data is sharded into ranges, each running Paxos or Raft, with Spanner's TrueTime bounding clock uncertainty for external consistency. Both default to SERIALIZABLE, and CockroachDB targets about 4.5 second RTO with near-zero RPO. The price is written into physics: a write must reach a quorum, and if that quorum spans regions, every write pays a cross-region round trip. There is no way around it if you insist on strong consistency across distance.

What Harper changes

Every option so far treats the network between the application and the database as fixed, then works to hide it behind a replica, a cache, or a quorum. What if the network is the thing to remove?

Harper removes the network from between the application and the data, then lets you choose which data lives where.

One process, no internal hop. Database, cache, application runtime, and messaging (MQTT, WebSockets, SSE) run in a single in-memory Node.js process (GitHub). In a conventional stack, your logic makes a network call to the database even inside the same data center: serialize, cross the socket, wait, deserialize. In Harper, logic runs inside the storage process and calls the data directly.

// schema.graphql
type Product @table {
 id: ID @primaryKey
 name: String @indexed
 price: Float
 inventory: Int
}

// resources.js
export class Product extends tables.Product {
 static async get(target) {
   const record = await super.get(target); // in-process read, no socket
   return record ? { ...record, inStock: record.inventory &gt; 0 } : record;
 }
}

Where it sits in the taxonomy. Harper replication is peer-to-peer over WebSockets with gossip discovery and mTLS, and it is asynchronous with eventual consistency across nodes (docs). Within a node, operations are ACID with serializable isolation. Across nodes, conflict resolution is last-writer-wins by logical timestamp, with CRDT support since 4.3 for counters and independent property merges. In PACELC terms this is AP/EL, the same family as DynamoDB Global Tables by default, not the same family as Spanner. Harper does not give you global serializability, and you should say that out loud in your design review.

Selective replication is the cost and compliance lever. You can restrict replication per database, exclude tables with @table(replicate: false), set one-way flow with per-route sends and receives flags, cap copies with replicateTo, and compute per-record residency with setResidencyById. So a Frankfurt node holds only EU data, an APAC node only APAC data, and an analytics node subscribes to everything without publishing back. That cuts cross-region bandwidth and supports residency requirements, unlike a global table that copies everything everywhere.

The collapsed stack. A conventional global architecture needs a CDN, a cache tier, app servers, a database with replicas, a message broker, and cross-region sync tooling, each deployed and monitored in every region. Harper collapses the middle four into one process with replication built into the runtime. Fewer hops is the latency win. Fewer failure domains and vendors is often the bigger one.

Run the benchmark yourself

The claim above is easy to state and cheap to test. Deploy three Harper nodes in genuinely distant regions (us-east-1, eu-west-1, ap-southeast-1), then measure two read paths from each: the centralized path (all load generators target the node in region A) and the colocated path (each generator targets its local node).

Methodology that matters: discard 2,000 warm-up requests; use HTTP keep-alive and measure the TLS handshake separately with curl -w "tls=%{time_appconnect}s" rather than smearing it across samples; report p50, p95, and p99, never averages; and drive load open-loop. That last one is the subtle one. A closed-loop client stops sending when the server stalls, so it never records how bad the stall was. The fix is to schedule sends on a fixed cadence and measure latency from the intended send time.

// bench.mjs (core loop). node bench.mjs --url http://HOST:9926/Product/sku-1 --rate 500 --seconds 120
const interval = 1000 / rate;
const start = performance.now();
const inflight = [];
for (let i = 0; i &lt; rate * seconds; i++) {
 const intended = start + i * interval;
 const now = performance.now();
 if (intended &gt; now) await new Promise((r) =&gt; setTimeout(r, intended - now));
 // Do not await: fire on schedule regardless of server state.
 inflight.push(request().then(() =&gt; {
   samples[count++] = (performance.now() - intended) * 1000; // vs intended, not actual
 }));
}
await Promise.all(inflight);

Expected magnitudes below are derived from published RTT figures, not measured Harper results. Replace them with your own numbers.

The flatness of the right column, not any single number in it, is the argument. Harper is not making the network faster. On the colocated path, the network is no longer in the read. The tradeoff you accept in return is that a local replica may briefly lag a write made in another region.

When to choose it, and when not to

The honest way to read the options is side by side, with the costs visible next to the wins.

Choose Harper's colocated model when global read latency dominates, your workload tolerates asynchronous convergence, and you want to cut both hops and operational surface area with per-table control over what lives where.

Do not choose it when you need strict global serializability (money movement, distributed locks, globally unique inventory decrements), when the same keys see heavy concurrent writes from multiple regions and every update must survive, or for heavy OLAP. Use Harper as the serving layer and feed a warehouse from it.

The distance between your users and your data is fixed, and no architecture on this page changes that. What stays a choice is how many times each request has to cross it, and how much of your operational surface you are willing to keep in every region to avoid the crossing.

The architecture

Run the benchmark yourself

The claim above is easy to state and cheap to test. Deploy three Harper nodes in genuinely distant regions (us-east-1, eu-west-1, ap-southeast-1), then measure two read paths from each: the centralized path (all load generators target the node in region A) and the colocated path (each generator targets its local node).

Methodology that matters: discard 2,000 warm-up requests; use HTTP keep-alive and measure the TLS handshake separately with curl -w "tls=%{time_appconnect}s" rather than smearing it across samples; report p50, p95, and p99, never averages; and drive load open-loop. That last one is the subtle one. A closed-loop client stops sending when the server stalls, so it never records how bad the stall was. The fix is to schedule sends on a fixed cadence and measure latency from the intended send time.

// bench.mjs (core loop). node bench.mjs --url http://HOST:9926/Product/sku-1 --rate 500 --seconds 120
const interval = 1000 / rate;
const start = performance.now();
const inflight = [];
for (let i = 0; i < rate * seconds; i++) {
 const intended = start + i * interval;
 const now = performance.now();
 if (intended > now) await new Promise((r) => setTimeout(r, intended - now));
 // Do not await: fire on schedule regardless of server state.
 inflight.push(request().then(() => {
   samples[count++] = (performance.now() - intended) * 1000; // vs intended, not actual
 }));
}
await Promise.all(inflight);

Expected magnitudes below are derived from published RTT figures, not measured Harper results. Replace them with your own numbers.

Load origin Centralized p50 Colocated p50
Virginia 1 to 3 ms 1 to 3 ms
Ireland 70 to 80 ms 1 to 3 ms
Singapore 230 to 260 ms 1 to 3 ms

The flatness of the right column, not any single number in it, is the argument. Harper is not making the network faster. On the colocated path, the network is no longer in the read. The tradeoff you accept in return is that a local replica may briefly lag a write made in another region

When to choose it, and when not to

The honest way to read the options is side by side, with the costs visible next to the wins.

Architecture Global read latency Write latency Consistency Conflict burden RTO / RPO Moving parts Egress Residency control
Single-region centralized Poor for distant users Good in-region Strong None App down on region loss Fewest, no HA High Simple
Leader + read replicas Good near replica, stale reads Cross-region to leader Strong on leader, eventual on replicas Low Minutes / about 1s Moderate Moderate Moderate
Multi-leader Good Good Eventual by default High (LWW) Low / low Managed, but conflicts are yours Moderate to high Weak by default
Leaderless P2P Good Tunable Tunable via R + W > N High Degrades gracefully Many Moderate to high Moderate
Consensus Good reads Poor across regions Serializable, global None Seconds / about 0 Moderate High Good
Harper colocated Excellent, in-process Local write fast, async converge Per-node ACID, cross-node eventual Moderate (LWW plus CRDTs) Multi-active Fewest Low (selective) Strong

Choose Harper's colocated model when global read latency dominates, your workload tolerates asynchronous convergence, and you want to cut both hops and operational surface area with per-table control over what lives where.

Do not choose it when you need strict global serializability (money movement, distributed locks, globally unique inventory decrements), when the same keys see heavy concurrent writes from multiple regions and every update must survive, or for heavy OLAP. Use Harper as the serving layer and feed a warehouse from it.

The distance between your users and your data is fixed, and no architecture on this page changes that. What stays a choice is how many times each request has to cross it, and how much of your operational surface you are willing to keep in every region to avoid the crossing.

Cross-region latency comes from distance, not slow queries. Why centralized data paths cost you latency, uptime, and money, with a benchmark you can run.

Download

White arrow pointing right
Cross-region latency comes from distance, not slow queries. Why centralized data paths cost you latency, uptime, and money, with a benchmark you can run.

Download

White arrow pointing right
Cross-region latency comes from distance, not slow queries. Why centralized data paths cost you latency, uptime, and money, with a benchmark you can run.

Download

White arrow pointing right

Explore Recent Resources

Media Coverage
GitHub Logo

Harper Argues Against the Multi-System Stack and Releases 5.2

InfoQ examines benchmark results comparing co-located and serverless architectures, highlighting faster personalized-data paths, serverless advantages under heavy fan-out, and the performance implications of eliminating network hops between services at scale.
Media Coverage
InfoQ examines benchmark results comparing co-located and serverless architectures, highlighting faster personalized-data paths, serverless advantages under heavy fan-out, and the performance implications of eliminating network hops between services at scale.
Renato Losio, Staff Editor at InfoQ
Renato Losio
InfoQ Staff Editor
Media Coverage

Harper Argues Against the Multi-System Stack and Releases 5.2

InfoQ examines benchmark results comparing co-located and serverless architectures, highlighting faster personalized-data paths, serverless advantages under heavy fan-out, and the performance implications of eliminating network hops between services at scale.
Renato Losio
Aug 2026
Media Coverage

Harper Argues Against the Multi-System Stack and Releases 5.2

InfoQ examines benchmark results comparing co-located and serverless architectures, highlighting faster personalized-data paths, serverless advantages under heavy fan-out, and the performance implications of eliminating network hops between services at scale.
Renato Losio
Media Coverage

Harper Argues Against the Multi-System Stack and Releases 5.2

InfoQ examines benchmark results comparing co-located and serverless architectures, highlighting faster personalized-data paths, serverless advantages under heavy fan-out, and the performance implications of eliminating network hops between services at scale.
Renato Losio
Blog
GitHub Logo

Faster by Doing Less: How Harper 5.2 Engineers Database Performance

Harper 5.2 attacks database performance on three fronts: a per-worker record cache validated through lock-free atomic version slots, isolated commit scheduling to keep database writes off the application thread, and query planner improvements that route through the shortest available data path.
Cache
Blog
Harper 5.2 attacks database performance on three fronts: a per-worker record cache validated through lock-free atomic version slots, isolated commit scheduling to keep database writes off the application thread, and query planner improvements that route through the shortest available data path.
Person with very short blonde hair wearing a light gray button‑up shirt, standing with arms crossed and smiling outdoors with foliage behind.
Kris Zyp
SVP of Engineering
Blog

Faster by Doing Less: How Harper 5.2 Engineers Database Performance

Harper 5.2 attacks database performance on three fronts: a per-worker record cache validated through lock-free atomic version slots, isolated commit scheduling to keep database writes off the application thread, and query planner improvements that route through the shortest available data path.
Kris Zyp
Aug 2026
Blog

Faster by Doing Less: How Harper 5.2 Engineers Database Performance

Harper 5.2 attacks database performance on three fronts: a per-worker record cache validated through lock-free atomic version slots, isolated commit scheduling to keep database writes off the application thread, and query planner improvements that route through the shortest available data path.
Kris Zyp
Blog

Faster by Doing Less: How Harper 5.2 Engineers Database Performance

Harper 5.2 attacks database performance on three fronts: a per-worker record cache validated through lock-free atomic version slots, isolated commit scheduling to keep database writes off the application thread, and query planner improvements that route through the shortest available data path.
Kris Zyp
News
GitHub Logo

Harper Recognized as an Honorable Mention in the 2026 Gartner® Magic Quadrant™ for Cloud-Native Application Platforms

Harper’s inclusion highlights a broader shift toward integrated cloud-native platforms that unify data, caching, messaging, and application logic. The article explains how Harper’s distributed runtime and managed infrastructure reduce operational complexity, improve performance, and support modern applications and AI agents that require fast, reliable access to state, events, and tools.
Announcement
News
Harper’s inclusion highlights a broader shift toward integrated cloud-native platforms that unify data, caching, messaging, and application logic. The article explains how Harper’s distributed runtime and managed infrastructure reduce operational complexity, improve performance, and support modern applications and AI agents that require fast, reliable access to state, events, and tools.
Person with short dark hair and moustache, wearing a colorful plaid shirt, smiling outdoors in a forested mountain landscape.
Aleks Haugom
Senior Manager of GTM
News

Harper Recognized as an Honorable Mention in the 2026 Gartner® Magic Quadrant™ for Cloud-Native Application Platforms

Harper’s inclusion highlights a broader shift toward integrated cloud-native platforms that unify data, caching, messaging, and application logic. The article explains how Harper’s distributed runtime and managed infrastructure reduce operational complexity, improve performance, and support modern applications and AI agents that require fast, reliable access to state, events, and tools.
Aleks Haugom
Aug 2026
News

Harper Recognized as an Honorable Mention in the 2026 Gartner® Magic Quadrant™ for Cloud-Native Application Platforms

Harper’s inclusion highlights a broader shift toward integrated cloud-native platforms that unify data, caching, messaging, and application logic. The article explains how Harper’s distributed runtime and managed infrastructure reduce operational complexity, improve performance, and support modern applications and AI agents that require fast, reliable access to state, events, and tools.
Aleks Haugom
News

Harper Recognized as an Honorable Mention in the 2026 Gartner® Magic Quadrant™ for Cloud-Native Application Platforms

Harper’s inclusion highlights a broader shift toward integrated cloud-native platforms that unify data, caching, messaging, and application logic. The article explains how Harper’s distributed runtime and managed infrastructure reduce operational complexity, improve performance, and support modern applications and AI agents that require fast, reliable access to state, events, and tools.
Aleks Haugom
News
GitHub Logo

Harper 5.2: More Throughput per Node, Fewer Systems Around It

Harper 5.2 helps architecture and platform leaders improve performance, control infrastructure costs, secure production workloads, and reduce operational complexity by bringing faster data access, agentic capabilities, scheduling, backup, routing, and protection into one unified runtime.
Product Update
News
Harper 5.2 helps architecture and platform leaders improve performance, control infrastructure costs, secure production workloads, and reduce operational complexity by bringing faster data access, agentic capabilities, scheduling, backup, routing, and protection into one unified runtime.
Person with short dark hair and moustache, wearing a colorful plaid shirt, smiling outdoors in a forested mountain landscape.
Aleks Haugom
Senior Manager of GTM
News

Harper 5.2: More Throughput per Node, Fewer Systems Around It

Harper 5.2 helps architecture and platform leaders improve performance, control infrastructure costs, secure production workloads, and reduce operational complexity by bringing faster data access, agentic capabilities, scheduling, backup, routing, and protection into one unified runtime.
Aleks Haugom
Aug 2026
News

Harper 5.2: More Throughput per Node, Fewer Systems Around It

Harper 5.2 helps architecture and platform leaders improve performance, control infrastructure costs, secure production workloads, and reduce operational complexity by bringing faster data access, agentic capabilities, scheduling, backup, routing, and protection into one unified runtime.
Aleks Haugom
News

Harper 5.2: More Throughput per Node, Fewer Systems Around It

Harper 5.2 helps architecture and platform leaders improve performance, control infrastructure costs, secure production workloads, and reduce operational complexity by bringing faster data access, agentic capabilities, scheduling, backup, routing, and protection into one unified runtime.
Aleks Haugom