Skip to content

Roshan Karki / Performance Lab

I investigate why systems become slow, expensive, and unreliable.

Backend performance experiments, database internals, Node.js runtime behaviour, distributed systems, queues, networking, and infrastructure — measured from first principles.

Consulting$50 / hourBook a call ↗

Lab active — currently investigating postgresql buffer cache behaviour under mixed workloads.

Request pathSynthetic example
  1. ingresstls + keep-alive
  2. node.js apiloop lag 0.4ms
  3. pg poolacquire wait
  4. postgresqlshared hit 94%
  5. storageiops 4.2k
p50
18ms
p95
72ms
p99
190ms
cpu
37%

Illustrative only. No number on this panel is a measurement.

What this is

This is a laboratory, not a portfolio. Every entry starts with a question and a hypothesis written down before the run, states the environment it was measured in, and separates what was measured from what was expected and what is interpretation.

The blog explains

Concepts, mechanisms, and how a piece of a system is designed to behave.

blog.roshankarki1.com.np ↗

The lab measures

Whether that behaviour holds under load, at scale, and at the boundaries where it stops holding.

lab.roshankarki1.com.np
Measured — recorded from a run Expected — stated before the run Interpretation — the mechanism behind it

Experiments

10 registered · 0 published

The register

Each experiment is registered with its question, hypothesis, and method before it runs. Results are published against that record — including the ones where the hypothesis was wrong.

experiment 001Runtime · foundational

Why 5% CPU can still mean a slow API

PlannedReproducibleNode.jsLatencyI/O

01 — Question

Why does a Node.js service report low CPU utilisation while p95 latency keeps climbing?

02 — Hypothesis

Latency is dominated by off-CPU time — pool wait, socket read, event loop queueing — so CPU headroom is not the constraint. Doubling vCPU should move p95 by less than 5%.

Written before the run. Not a result.

03 — Setup

cpu4 vCPU
memory8 GB
osLinux
node24.x
postgresql17.x

Reference rig. Exact versions, dataset size, concurrency, cache state and run count are pinned and published with each result.

04 — Method

Hold offered load fixed. Vary vCPU from 2 to 8. Attribute wall time separately to on-CPU execution, pool acquire wait, database service time, and socket wait, rather than reading a single utilisation gauge.

05 — Results

Data pending

This experiment has not been run. Nothing is reported until it has.

Will report

  • p50 / p95 / p99
  • event loop lag
  • pool acquire wait
  • off-CPU time
  • CPU %

What actually happened

The mechanism behind the numbers, written in plain terms — published with the results. Numbers without a mechanism are trivia.

Benchmarks

9 tracked · 0 with data

Benchmark board

Every row declares what will be measured and in what unit before it has a number. An empty track means the run hasn’t happened — not that the result was unremarkable.

BenchmarkAreaReported inResult
PostgreSQLp95No data
PostgreSQLexecution timeNo data
Node.jsns / opNo data
Node.jslag p99No data
Rediswall timeNo data
Connection reuse vs fresh handshakeNetworkingRTTNo data
Queuesduplicates / 10kNo data
Concurrencyacquire wait p95No data
Cachingp95No data

Reporting standard

Every published benchmark states hardware, software versions, dataset size, concurrency, cache state, number of runs, measurement method, and known sources of error. If a benchmark can’t be reproduced from what’s written down, it isn’t finished.

Topics

Six areas

What I investigate

Six areas where latency, cost, and reliability problems usually originate — and where the abstraction and the machine disagree most often.

Database performance

Query plans, index design and ordering, buffer behaviour, MVCC and vacuum, locks, transaction boundaries, I/O.

plannerindexesMVCCWALbuffer cachelocksvacuum

Runtime performance

What the Node.js process is actually doing while a request waits — event loop phases, libuv, V8, memory, worker threads.

event looplibuvV8GCworker threads

Distributed systems

Queues, retries, idempotency, delivery semantics, backpressure, replication, and the failure modes between services.

queuesretriesidempotencyconsistencyreplicationbackpressure

Networking

HTTP and TCP behaviour, connection reuse, proxy layers, kernel accept queues, and where latency accumulates on the wire.

HTTPTCPkeep-aliveL4 / L7 proxyaccept queue

Infrastructure

AWS, containers, resource limits, and the trade between cost and performance when a machine is the wrong size.

AWScontainerslimitsload balancingcost / perf

Observability

Metrics, structured logs, traces, and profiling — the signals that make an incident boring to diagnose.

metricstracesprofilinghistogramsincident review

Case studies

Method fixed · none published yet

How an investigation runs

Production work follows the same sequence every time. Published case studies are anonymised, and none go up without permission and a measurement on both sides of the fix.

  1. 01ProblemWhat the team can observe from outside the system.
  2. 02SymptomsWhich signals move together, and which stay flat.
  3. 03InvestigationNarrow the search space by measurement, not by guessing.
  4. 04Root causeThe mechanism, stated precisely enough to predict behaviour.
  5. 05FixThe smallest change that addresses the mechanism.
  6. 06MeasurementThe same benchmark, before and after, same conditions.
  7. 07LessonsWhat generalises, and what was specific to this system.

Awaiting publication

No case studies are published yet. The method above is what gets applied, and write-ups appear here once client permission and before/after measurements are both in hand.

Discuss an investigation

What we do

Three tiers · one report

We measure where the problem actually is.

Three tiers, and the only thing that changes between them is where we put the capture. Start at whichever one matches the symptom — or run all three and get the whole path.

Service-to-service traffic sits inside the backend hop — we capture that too.

Tier 1Client ↔ edge

Single client trace

One user. One session. Every request it makes.

We capture at the client and follow one interaction the whole way through — every call it fires, what came back, and how long each hop really took. This is where “the app feels slow” becomes a specific request with a number next to it.

What it finds

  • Which call owns the latency
  • Duplicate and retried requests
  • Payload and header waste
  • Connection reuse and TLS cost
Tier 2Edge ↔ backend

Backend edge breakdown

Every client at once, measured just above your backend.

We capture where all traffic arrives — at or just in front of the load balancer. Tier 1 tells you what happened to one user. This tells you what the system does under real, concurrent load.

What it finds

  • Traffic shape and hot endpoints
  • Queueing and connection limits
  • Tail latency against the average
  • Errors, timeouts, retry storms
Tier 3Backend ↔ database

Backend and database

What your service actually asks the database for.

We capture the traffic between your services and the database — the queries as they are really issued, in the order and volume they are really issued. Often the plan is fine and the pattern is not.

What it finds

  • N+1 and repeated queries
  • Plans, indexes, and scan choice
  • Pool saturation and acquire wait
  • Transaction and lock time

Also

Service to service

When the slowness lives between your own services rather than at any single edge, we trace that too — the calls services make to each other, what they wait on, and how a failure in one becomes a timeout in another.

Call graphs and fan-outTimeout and retry budgetsSerialization and payload costCascading failure paths

Tools

WiresharktcpdumpmitmproxyEXPLAIN ANALYZEFlame graphsLoad generatorsss / netstatstrace

Packet capture where the wire tells the truth, proxies where the payload does, profilers and query plans where the code does.

Every tier ends the same way — a written report your engineering team can act on: what we measured, how we measured it, what it means, and what to change first.

How engagements work →

Work with me

Audits · reviews · investigations

Something in your backend is slower than it should be.

I help engineering teams find performance bottlenecks across application code, databases, queues, caches, and infrastructure — and prove the fix with the same measurement that found the problem.

Backend performance audit

API latency, Node.js runtime, database queries, indexes, connection pools, Redis, queues, infrastructure, observability.

Deliverable

  • Performance report
  • Root causes, ranked
  • Prioritised fixes
  • Benchmark recommendations

Database performance review

PostgreSQL query plans, index and schema design, locks, transaction boundaries, connection pools, I/O behaviour.

Deliverable

  • Plan-level findings
  • Index and schema changes
  • Before / after measurements

Architecture review

Service boundaries, synchronous vs asynchronous communication, queues, caching, consistency, failure modes, scalability.

Deliverable

  • Boundary map
  • Failure mode analysis
  • Scaling constraints

Performance investigation

For teams who already know something is slow but not where. A specific production problem, traced to a mechanism.

Deliverable

  • Reproduction
  • Root cause
  • Fix and verification

Have a performance problem?

Tell me what’s slow.

Useful first message: what the symptom is, when it started, what you’ve already measured, and what the system looks like. Rough numbers are fine — the point is to work out what to measure next.

First step
A short scoping call
Billing
Hourly, or fixed scope
Format
Remote, async-friendly
Time zone
NPT · UTC+05:45
Deliverable
Findings with measurements

Rate

$50/ hour

Billed hourly for audits, reviews, and investigations. Once the problem is scoped I can quote fixed scope instead.

Book a callcal.com/book-roshan-karkiEmail instead

About

Updated August 2026

I like understanding what happens underneath the abstraction.

I’m Roshan Karki, a backend engineer with 3+ years shipping production systems for concurrent users. I’m interested in the parts of software that usually stay hidden — database pages, query planners, event loops, network boundaries, queues, caches, memory, and the failure modes between services.

Day to day that means TypeScript-first Node.js with whichever framework fits the job, PostgreSQL, and AWS, with an emphasis on module boundaries and making the happy path and the failure modes equally explicit in code and in dashboards. I’ve worked across dating and social, directory platforms, LMS, ads and marketing, and hiking and outdoor products — each with a different tolerance for latency and a different definition of correct.

This lab is where that curiosity turns into experiments instead of opinions.

Read my engineering writing ↗

Engineering principles

Measure before optimising.

If the bottleneck can’t be measured, the fix is a guess with extra steps.

Understand the mechanism.

Framework behaviour is easier to reason about once you know what runs underneath it.

Optimise the bottleneck.

Making a fast component faster changes nothing while another component dominates latency.

Failure is part of the design.

A system isn’t production-ready until its failure modes are understood.

Simple systems scale surprisingly far.

Complexity should arrive because the problem requires it, not because the diagram looks impressive.