Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

marketpulse

A low-latency market-data gateway in Go. It fronts an upstream price API with a single-flight, in-memory TTL cache: repeat reads are served from memory in ~92 ns, and a burst of concurrent misses for the same key collapses into exactly one upstream call instead of stampeding it.

go deps license

Zero third-party dependencies — standard library only. Static ~5.6 MB binary, distroless non-root container.


Why

Public market-data APIs are rate-limited and slow (100–800 ms), and naive callers hammer them with identical requests. marketpulse sits in front and gives you:

  • Sub-microsecond repeat reads — cached responses never touch the network.
  • Single-flight — 50 goroutines missing the same key trigger one upstream call; the rest wait for and share its result. No thundering herd.
  • A stable cache key regardless of argument order — ?ids=btc,eth and ?ids=eth,btc hit the same entry.
  • Operational honesty — /stats exposes live hit ratio; every response carries an X-Response-Time header.
flowchart LR
  C[client] -->|/price?ids=| S[marketpulse]
  S -->|hit ~92ns| C
  S -->|miss, single-flight| U[(upstream API)]
  U --> S
  subgraph S[marketpulse]
    K[TTL cache] --- F[single-flight]
  end
Loading

Quickstart

# with Docker
docker compose up --build
# or bare
go run ./cmd/marketpulse

curl 'http://localhost:8080/price?ids=bitcoin,ethereum&vs=usd'
# {"bitcoin":64213.0,"ethereum":3345.7}   (X-Response-Time: 214ms first call)
curl 'http://localhost:8080/price?ids=bitcoin,ethereum&vs=usd'
# same body, X-Response-Time: 41µs  (served from cache)

curl http://localhost:8080/stats
# {"hits":1,"misses":1,"hit_ratio":0.5}

API

Endpoint Description
GET /price?ids=<csv>&vs=<ccy> Cached spot prices. vs defaults to usd.
GET /stats Cumulative cache hits / misses / hit ratio.
GET /healthz Liveness + active provider name.

Configuration

Env var Default Meaning
MARKETPULSE_ADDR :8080 Listen address.
MARKETPULSE_TTL 10s Cache entry lifetime.

Design notes

  • Cache (internal/cache) — RWMutex-guarded map + per-key single-flight. The loader for a key runs once even under concurrent misses; callers block on a sync.WaitGroup and share the result.
  • Provider (internal/provider) — the server depends on a small Provider interface, so the real CoinGecko client and the test mock are interchangeable. Adding a source (DeFiLlama, an exchange WS) is one file.
  • Server (internal/server) — thin HTTP layer; caches the marshalled JSON, not the map, so cache hits skip re-serialization too.
  • main — graceful shutdown on SIGINT/SIGTERM, request logging, timeouts.

Benchmarks

BenchmarkGetOrLoadHit-2    11,633,913    91.98 ns/op    0 B/op    0 allocs/op

go test -race ./... is clean; the single-flight guarantee is asserted by test (25 concurrent requests → 1 upstream call).

Tests

make test    # go test -race ./...
make bench   # cache benchmarks

Part of a small polyglot portfolio — right tool per layer: Go for networking (this), Python for high-level AI, TypeScript for test/QA automation, Rust for the from-scratch numerical core. — Nicholas Martins · github.com/nickmartins-lambda

About

Low-latency market-data gateway in Go — single-flight TTL cache, zero dependencies.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages