A Redis-compatible in-memory key-value store written in Go, implementing the RESP (Redis Serialization Protocol) for client communication.
-
RESP Protocol Support: Full implementation of Redis Serialization Protocol
- Array commands (
*) - Bulk strings (
$) - Simple strings (
+,-) - Integers (
:) - Booleans (
#) - Blob errors (
!) - Null values (
_)
- Array commands (
-
Core Commands:
SET key value- Set a key-value pair (returns+OK)GET key- Retrieve a value by key (returns bulk string or$-1for nil)DEL key- Delete a key (returns+OKor$-1)EXISTS key- Check if a key exists (returns:1or:0)TTL key- Get remaining time-to-live for a key (returns:secondsor:-1/:-2)EXPIRE key seconds- Set expiration for a key (returns+OKor:0)EXPIREAT key timestamp- Set expiration using Unix timestamp (returns+OKor:0)PERSIST key- Remove expiration from a key (returns:1or:0)BGSAVE- Start background save of the database (returns+OK)
-
Numeric Commands:
INCR key- Increment a numeric key by 1 (returns:new_value)DECR key- Decrement a numeric key by 1 (returns:new_value)INCRBY key increment- Increment a numeric key by specified amount (returns:new_value)DECRBY key decrement- Decrement a numeric key by specified amount (returns:new_value)
-
Advanced TTL Features:
- Automatic Expiration: Expired keys are automatically deleted when accessed
- Dynamic TTL Calculation: TTL returns actual remaining seconds until expiration
- Expired Key Cleanup: Keys past their expiration time are removed from storage
-
Persistence:
- AOF (Append Only File) persistence
- Automatic command logging for data-modifying operations
- Recovery from AOF file on startup
-
Interactive Mode:
- Command-line interface with
>>prompt - Automatic conversion of plain text commands to RESP format
- Support for both RESP and plain text input
- Command-line interface with
The project follows a modular, command-based architecture with clear separation of concerns. Each command is implemented as a separate module following the Command Pattern, providing better maintainability and extensibility:
- Command Pattern: Each command is encapsulated in its own struct with a consistent Execute() method
- Separation of Concerns: Commands are isolated from parsing, storage, and persistence logic
- Easy Extensibility: Adding new commands requires minimal changes to existing code
- Maintainability: Each command can be modified independently without affecting others
- Testability: Individual commands can be unit tested in isolation
- Consistent Interface: All commands follow the same pattern for predictable behavior
YAKVS/
βββ aof/ # AOF persistence module
β βββ aof.go # AOF file management
βββ command/ # Command implementations
β βββ BgsaveCommand.go # BGSAVE command handler
β βββ Decr.go # DECR command handler
β βββ DecrBy.go # DECRBY command handler
β βββ Del.go # DEL command handler
β βββ Exists.go # EXISTS command handler
β βββ Expire.go # EXPIRE command handler
β βββ ExpireAt.go # EXPIREAT command handler
β βββ Get.go # GET command handler
β βββ Incr.go # INCR command handler
β βββ IncrBy.go # INCRBY command handler
β βββ Persist.go # PERSIST command handler
β βββ Set.go # SET command handler
β βββ Ttl.go # TTL command handler
βββ parser/ # RESP protocol parser
β βββ parser.go # Streaming parser implementation
β βββ parser_test.go # Comprehensive test suite
βββ snapshot/ # Snapshot functionality
β βββ snapshot.go # Snapshot operations
βββ store/ # Key-value storage
β βββ kvObj.go # Key-value object definitions
β βββ store.go # In-memory store with interface
βββ utils/ # Utility functions
β βββ utils.go # RESP conversion and validation
βββ main.go # Main application entry point
βββ execute.go # Command execution engine
βββ base.aof # AOF persistence file
- Command Pattern Implementation: Each command is a separate struct with Execute() method
- Command Handlers: Individual files for each command (SET, GET, DEL, etc.)
- Command Metadata: Each command includes syntax, help text, and examples
- Extensible Design: Easy to add new commands by creating new command files
- Consistent Interface: All commands follow the same pattern for maintainability
- AOFManager: Centralized AOF file operations
- WriteCommand(): Persist commands to AOF file
- ReadAndExecuteCommands(): Replay commands from AOF on startup
- ShouldPersistCommand(): Determine which commands to persist
- StreamingParser: Efficient RESP protocol parsing
- ParseCommand(): Main parsing entry point
- ParseArray(): Handle RESP arrays
- ParseBulkString(): Handle RESP bulk strings
- Comprehensive test coverage: 100% test coverage for all parsing functions
- Store: In-memory key-value storage
- NewStore(): Constructor for store instances
- CRUD Operations: GetValue, SetValue, DeleteValue, Exists
- StoreInterface: Interface for future extensibility
- Snapshot Operations: Background save functionality
- Start(): Initialize snapshot process
- Future Extensions: Planned for RDB-style snapshots
- ToRESP(): Convert plain text commands to RESP format
- IsRESPFormat(): Detect RESP protocol format
- PreprocessInput(): Handle escape sequences (deprecated)
- Go 1.21.1 or later
- Git
-
Clone the repository:
git clone https://github.com/shubhdevelop/YAKVS.git cd YAKVS -
Build the application:
go build -o YAKVS
-
Run the application:
./YAKVS
Start the application and use the interactive prompt:
$ ./YAKVS
YAKVS
>> SET mykey "Hello World"
Parsing RESP command: *3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$11\r\nHello World\r\n
Executing command: &{Name:SET Args:[mykey Hello World]}
+OK
>> GET mykey
Parsing RESP command: *2\r\n$3\r\nGET\r\n$5\r\nmykey\r\n
Executing command: &{Name:GET Args:[mykey]}
$11
Hello World
>> DEL mykey
Parsing RESP command: *2\r\n$3\r\nDEL\r\n$5\r\nmykey\r\n
Executing command: &{Name:DEL Args:[mykey]}
+OK
>> EXISTS mykey
Parsing RESP command: *2\r\n$6\r\nEXISTS\r\n$5\r\nmykey\r\n
Executing command: &{Name:EXISTS Args:[mykey]}
:0
>> INCR counter
Parsing RESP command: *2\r\n$4\r\nINCR\r\n$7\r\ncounter\r\n
Executing command: &{Name:INCR Args:[counter]}
:1
>> INCRBY counter 5
Parsing RESP command: *3\r\n$6\r\nINCRBY\r\n$7\r\ncounter\r\n$1\r\n5\r\n
Executing command: &{Name:INCRBY Args:[counter 5]}
:6
>> DECR counter
Parsing RESP command: *2\r\n$4\r\nDECR\r\n$7\r\ncounter\r\n
Executing command: &{Name:DECR Args:[counter]}
:5
>> DECRBY counter 2
Parsing RESP command: *3\r\n$6\r\nDECRBY\r\n$7\r\ncounter\r\n$1\r\n2\r\n
Executing command: &{Name:DECRBY Args:[counter 2]}
:3
>> exitThe application supports both plain text commands and native RESP protocol:
Plain Text Input (automatically converted):
>> SET key value
Native RESP Input:
>> *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
YAKVS now returns proper RESP protocol responses for all commands:
Command Response Types:
SET,DEL,EXPIRE,EXPIREAT: Return+OKon successGET: Returns$<length>\r\n<value>\r\nor$-1\rfor nilEXISTS: Returns:1(true) or:0(false)TTL: Returns:<remaining_seconds>or:-1(no expiry) or:-2(key doesn't exist/expired)PERSIST: Returns:1(success) or:0(key doesn't exist or no TTL)INCR,DECR,INCRBY,DECRBY: Return:<new_value>with the updated numeric value
TTL Response Details:
:<positive_number>: Remaining seconds until expiration:-1: Key exists but has no expiration set:-2: Key doesn't exist or has expired (automatically cleaned up)
Example RESP Responses:
>> SET key value
+OK
>> GET key
$5
value
>> EXISTS key
:1
>> TTL key
:-1
>> INCR counter
:1
>> INCRBY counter 5
:6
>> DECR counter
:5
>> DECRBY counter 2
:3
>> DEL key
+OK
Run the comprehensive test suite:
# Run all tests
go test ./...
# Run parser tests with verbose output
go test ./parser -v
# Run tests for specific module
go test ./store -v- Modular Design: Each component is in its own package
- Interface-Based: Uses interfaces for extensibility
- Test-Driven: Comprehensive test coverage
- Error Handling: Proper error handling throughout
The new command-based architecture makes adding commands much easier and more maintainable:
-
Create a new command file in
command/directory (e.g.,NewCommand.go):package command import ( "fmt" "github.com/shubhdevelop/YAKVS/parser" "github.com/shubhdevelop/YAKVS/store" ) // NewCommand handles the NEWCOMMAND command type NewCommand struct { Command *parser.Command Store *store.Store } // NewNewCommand creates a new NEWCOMMAND command instance func NewNewCommand(cmd *parser.Command, store *store.Store) *NewCommand { return &NewCommand{ Command: cmd, Store: store, } } // Execute executes the NEWCOMMAND command func (nc *NewCommand) Execute() { // Implementation here fmt.Println("+OK\r") } // Command metadata (optional but recommended) type NewCommandMeta struct { Name string Syntax string HelpShort string HelpLong string Examples string } func NewMeta() *NewCommandMeta { return &NewCommandMeta{ Name: "NEWCOMMAND", Syntax: "NEWCOMMAND arg1 arg2", HelpShort: "NEWCOMMAND does something useful", HelpLong: "Detailed description...", Examples: ">> NEWCOMMAND arg1 arg2\n+OK", } }
-
Add command to
execute.go:case "NEWCOMMAND": newCmd := command.NewNewCommand(cmd, store) newCmd.Execute()
-
Update
utils/utils.goto support command conversion:case "NEWCOMMAND": // Add to ToRESP() function
-
Add to persistent commands in
aof/aof.go(if needed):persistentCommands := map[string]bool{ "NEWCOMMAND": true, }
- Linting: No linting errors
- Testing: 100% test coverage for parser module
- Documentation: Comprehensive inline documentation
- Error Handling: Proper error propagation and handling
- RESP Protocol Parser
- Core Key-Value Operations
- Numeric Operations (INCR, DECR, INCRBY, DECRBY)
- RESP Response Format (Redis-compatible)
- TTL and Expiration Support
- AOF Persistence
- Interactive Command Line Interface
- Command Conversion (Plain Text β RESP)
- Modular Architecture
- Command Pattern Implementation
- BGSAVE Command Support
- Comprehensive Testing
- Error Handling
- Additional Redis Commands (HSET, HGET, LPUSH, etc.)
- Configuration Management
- Network Server Mode
- Clustering Support
- Memory Optimization
- Advanced Data Types: Lists, Sets, Hashes, Sorted Sets
- Background Expiration: Automatic cleanup without key access
- Persistence Options: RDB snapshots, AOF rewriting
- Network Protocol: TCP server for remote connections
- Replication: Master-slave replication
- Clustering: Distributed key-value store
- Performance: Memory optimization, connection pooling
- Monitoring: Metrics and health checks
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by Redis and its RESP protocol
- Built with Go's excellent concurrency primitives
- Test-driven development approach
YAKVS - A modern, modular key-value store implementation in Go π