← Back to Blogs

LightShard: Designing a Sharded In-Memory Key-Value Store

A short architectural write-up on building a sharded, in-memory key-value store with clean APIs and clear evolution paths.

Why LightShard?

LightShard started from a simple question:

What does it really take to go from a single in-memory map to a scalable key-value service?

While systems like Redis abstract away most internal complexity, LightShard was built to understand and design those internals explicitly — sharding, concurrency, APIs, and extensibility — without jumping straight into distributed consensus or persistence.

The goal was not to build a production replacement, but to design a system that mirrors real backend trade-offs.

The Core Problem

A single in-memory map has clear limits:

  • bounded by a single CPU core
  • bounded by process memory
  • hard to scale reads/writes cleanly

LightShard addresses this by introducing explicit sharding, while remaining simple, predictable, and easy to reason about.

High-Level Architecture

At a high level, LightShard is a sharded, in-memory key-value store exposed over a gRPC API.

LightShard architecture

Figure 1: LightShard Architecture.

Each shard is an independent in-memory store responsible for a subset of keys.

Sharding Model

LightShard uses a simple hash-based sharding:

shard_id = hash(key) % N

Where:

  • N = number of shards
  • each shard owns its own data and lock

Why this matters:

  • isolates contention to a shard
  • enables parallel reads and writes
  • keeps the implementation simple

This mirrors early-stage designs in many real systems before introducing consistent hashing or rebalancing.

Shard Internals

Each shard is intentionally minimal:

  • in-memory map
  • per-shard lock
  • TTL (time to live) metadata per key

This ensures predictable behavior, easy reasoning about correctness, and a clear upgrade path.

TTL expiration is handled lazily on reads, which keeps the write path fast and avoids background sweeps in early versions.

API Design

LightShard exposes a clean gRPC interface:

  • Set
  • SetWithTTL
  • Get
  • Delete
  • Exists
  • TTL

The API is binary-safe (values as bytes), language-agnostic, and easy to embed into other systems.

This makes LightShard suitable as a building block, not just a standalone service.

Read & Write Flow

Write

  1. Client sends request via gRPC.
  2. Shard Manager hashes the key.
  3. Request routed to the correct shard.
  4. Shard applies the mutation in-memory.

Read

  1. Client sends Get.
  2. Shard Manager routes to the shard.
  3. Shard checks key + TTL.
  4. Value returned or rejected.

Why In-Memory First?

LightShard intentionally starts as an in-memory system because it simplifies correctness, highlights sharding trade-offs, and keeps latency extremely low.

Persistence is treated as an orthogonal concern, which allows LightShard to later integrate with a storage engine.

Key Takeaways

  • Sharding is a design choice, not just a scaling trick.
  • Explicit shard ownership simplifies concurrency.
  • Clean APIs make systems composable.

LightShard represents the middle layer of a backend stack — sitting between application logic and storage engines.

Running LightShard via Docker

LightShard is published as a Docker image and can be run locally without building from source.

Pull the image

docker pull manojayyanavara/lightshard:latest

Run LightShard

docker run -p 8982:8982 \
  -e GRPC_PORT=:8982 \
  manojayyanavara/lightshard:latest

This starts the LightShard gRPC server on port 8982.

You can now connect using any gRPC client (for example grpcurl, BloomRPC, or ezy) and interact with the key-value APIs.

Sample Requests using grpcurl

Below are example commands using grpcurl to interact with LightShard. These assume the server is running locally on port 8982.

Note: value fields are defined as bytes in protobuf. grpcurl accepts them as base64-encoded strings.

Set a key

grpcurl -plaintext \
  -d '{"key":"key1","value":"dmFsdWUx"}' \
  localhost:8982 kvstore.KvStore/Set

(dmFsdWUx = base64(“value1”))

Get a key

grpcurl -plaintext \
  -d '{"key":"key1"}' \
  localhost:8982 kvstore.KvStore/Get

Set a key with TTL (25 seconds)

grpcurl -plaintext \
  -d '{"key":"temp","value":"dGVtcHZhbA==","ttlSeconds":25}' \
  localhost:8982 kvstore.KvStore/SetWithTTL

Check TTL

grpcurl -plaintext \
  -d '{"key":"temp"}' \
  localhost:8982 kvstore.KvStore/TTL

Delete a key

grpcurl -plaintext \
  -d '{"key":"key1"}' \
  localhost:8982 kvstore.KvStore/Delete

Check existence

grpcurl -plaintext \
  -d '{"key":"key1"}' \
  localhost:8982 kvstore.KvStore/Exists