Skip to content

Bocaio/OptimusX-DB

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

In-Memory Database (Redis-Go)

A minimalist Redis implementation built from scratch in Go. It supports concurrent client connections, speaks the RESP protocol, and uses mutex-protected memory to ensure data consistency under load.


Key Features

  • High Performance: Native Go implementation using goroutines for concurrent request handling.
  • Thread Safe: Robust concurrency control using sync.Mutex to protect shared maps.
  • RESP2 Protocol: Full support for Redis Serialization Protocol parsing and encoding.
  • Automatic Cleanup: Background worker that periodically sweeps expired keys to free memory.
  • Lazy Expiration: Expired keys are also cleared on-read to ensure consistency.
  • Slow-Client Handling: Incomplete RESP chunks are buffered and re-parsed across reads, so fragmented or delayed client input is handled gracefully without dropping commands.
  • Graceful Shutdown: On SIGINT, all client goroutines and background workers are signalled via a shared done channel and waited on with sync.WaitGroup before the process exits.

🛠️ Supported Commands

Command Usage Description
PING PING Check server health (returns PONG)
ECHO ECHO <msg> Returns the input message as a bulk string
SET SET <key> <val> [EX s] [PX ms] Stores a string with optional TTL
GET GET <key> Retrieves a value; handles lazy expiration
RPUSH RPUSH <list> <val...> Appends one or more values to a list
LRANGE LRANGE <list> <start> <stop> Returns a range of elements from a list
DEL DEL <key> Deletes a key and returns 1 if it existed, 0 otherwise
EXISTS EXISTS <key> Returns 1 if the key exists and is not expired, 0 otherwise
SUBSCRIBE SUBSCRIBE <channel> Subscribes the client to the specified channel
UNSUBSCRIBE UNSUBSCRIBE <channel> Removes the client from the specified channel
PUBLISH PUBLISH <channel> <msg> Posts a message to the given channel

📂 Project Structure

.
├── app/
│   ├── main.go                # Entry point — signal handling & graceful shutdown
│   ├── server.go              # TCP accept loop & goroutine lifecycle management
│   ├── handle-request.go      # Per-connection dispatcher & command routing
│   └── command-stream.go      # Streaming RESP reader with partial-read buffering
├── commands/                  # Individual command logic
│   ├── PING.go                # Health-check command
│   ├── ECHO.go                # Echo command
│   ├── set-get.go             # SET / GET / DEL / EXISTS + in-store expiry sweep
│   ├── RPUSH.go               # List append operation
│   ├── LRANGE.go              # List range read operation
│   └── publish-subscribe.go   # SUBSCRIBE / UNSUBSCRIBE / PUBLISH + dead-conn cleanup
├── background/
│   └── cleanup.go             # Periodic background expiration sweep
├── helper/
│   ├── parse-RESP.go          # Fast RESP2 array parsing
│   ├── encode-RESP-array.go   # RESP encoding for responses
│   ├── filter.go              # Bounded channel filter for subscribe streams
│   └── or-chan.go             # Recursive `or-channel` fan-in for shutdown signals
├── types/
│   └── types.go               # Thread-safe Storage, Store, Lists, StreamDict & CommandType structs
├── testing-scripts/           # Automated validation scripts
│   ├── set-get.sh             # Basic String validation
│   ├── push-range.sh          # List operation validation
│   ├── expiry.sh              # TTL/Expiration logic testing
│   ├── DEL.sh                 # DEL command validation
│   ├── EXISTS.sh              # EXISTS command validation
│   ├── SUBSCRIBE.sh           # Pub/Sub subscription test (stays open)
│   ├── PUBLISH.sh             # Pub/Sub single message test
│   ├── multi-PUBLISH.sh       # Pub/Sub batch message stress test
│   ├── multi-pubsub.sh        # Multi-subscriber / multi-publisher scenario test
│   ├── persist-connection.sh  # Long-lived connection / pipelining test
│   └── slow-client.sh         # Fragmented TCP / slow-client simulation
├── start_program.sh           # Convenience build & run script
└── go.mod                     # Go module definition

🚀 Getting Started

1. Run the Server

go build -o redis-server ./app && ./redis-server

2. Connect with CLI

redis-cli ping
redis-cli set mykey "Hello Go" EX 60
redis-cli get mykey

🧪 Testing

We include automated shell scripts to validate the implementation:

# Test basic SET/GET functionality
./testing-scripts/set-get.sh

# Test List operations (RPUSH/LRANGE)
./testing-scripts/push-range.sh

# Test Expiration and TTL logic
./testing-scripts/expiry.sh

# Test DEL & EXISTS commands
./testing-scripts/EXISTS.sh

# Test Pub/Sub (Run SUBSCRIBE in one terminal, PUBLISH in another)
./testing-scripts/SUBSCRIBE.sh
./testing-scripts/PUBLISH.sh "Your Message"

# Test batch publishing
./testing-scripts/multi-PUBLISH.sh

# Simulate a slow/fragmented client (sends SET byte-by-byte with 300ms delays)
./testing-scripts/slow-client.sh

📝 Implementation Details

  • Concurrency Control: Shared state (Maps) are protected by sync.Mutex within the Store and Lists structs to prevent race conditions during concurrent access.
  • Memory Management: Expiry is managed via *time.Time. A nil pointer signifies no expiration.
  • Storage Separation: Strings and Lists are maintained in separate thread-safe maps, mirroring Redis' internal architecture for efficient type-specific operations.
  • Pub/Sub Mechanism: Implemented using Go channels for real-time message delivery. Each channel is represented by a Route containing a broadcast channel and a subscriber count.
  • Slow-Client Resilience: The request handler accumulates partial TCP reads into a temporary buffer and re-parses after each chunk. When the RESP parser detects an incomplete message (declared element count exceeds actually parsed elements), the read loop continues gathering data instead of rejecting the command. This allows the server to correctly handle clients that send data in small or delayed fragments over a single connection.
  • Robustness: The RESP parser validates bulk string lengths to prevent corruption from malformed client input.
  • Graceful Shutdown: A shared done channel is closed on SIGINT. Each client handler spawns a watcher goroutine that selects on both done (global shutdown) and clientDone (client disconnect), closing the connection and exiting cleanly in either case. A sync.WaitGroup tree (mainServer → per-client) ensures the process only exits after all goroutines have terminated, preventing memory leaks.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors