iamkira420 /
kotlin-shell
Basic shell implemented in Kotlin, coding challenge from @codecrafters-io
29/100 healthLoading repository data…
schangj09 / repository
Challenge from codecrafters.io in which I build my own version of Redis from scratch in Java. This has been an excellent exercise to practice working with TCP, multithreading and along the way examine some AI tools for code and test generation.
A transparent discovery signal based on current public GitHub metadata.
This score does not audit code, security, maintainers, documentation quality, or suitability. Verify the repository and its current documentation before adoption.
In this challenge, I have used the codecrafters.io platform to build a Redis clone step
by step with some of the functionality of Redis. The base challenge includes implementing several basic commands like
PING , SET and GET , and expiring keys. Along the way I've crafted (and refactored) the event loop, services,
Redis serialization and more.
Note: If you want to see more details about the stages and how codecrafters works, check out codecrafters.io.
The basic steps include setting up a server TCP connection which accepts incoming connections, handles basic commands and responses on those connections. Afterwards, I added support for multiple clients.
For example, in Java the ServerSocket.accept method blocks while waiting. In this commit, I created a separate thread for waiting on new connections and refactored the processing loop into a separate class that can process commands and send responses.
I implemented a class hirearchy to represent the data types of the Resp protocol. And a separate hirearchy to represent the commands of the Redis protocol.
In the replication extension, I've added support for running the instance as a replication "follower" service. This includes implementing a handshake protocol while starting up the replication service, as well as sending ACK responses from the follower to the leader.
In the final stages of replication, I added the WAIT command, which blocks until getting responses from replication servers. However, the blocking command uncovered an issue in my original design, that required a fair amount of refactoring.
For this stage, I refactored the EventLoop to make a separate thread for reading commands from the input stream and save them into a queue for processing. Then the command loop polls the input queue instead of reading directly from the input stream. These changes were also essential to support blocking reads in the last extension for Streams.
This extension provides steps to implement reading the input state from a file in the
Redis RDB file format. This challenge includes parsing the input file and
initializing the in-memory data map with stored values and expiration timestamps. I added support for the command line
options --dbfilename and --dir and
I wrote the classes and unit tests for reading encoded binary data from the RDB file format.
EncodedValue provides the decoding for a number or special
format value. RdbFileParser has methods to read the
sections of the file.
This extension adds support for XADD , XRANGE and XREAD with blocking. The primary challenges included blocking on
read and supporting multiple streams with auto incrementing ids.
StreamsWaitManager is a singleton instance class
that enables a read command to wait on multiple streams. The reading thread will block on a special lock that is created
just for that XREAD command. Then when an item is added to any stream via XADD, then any locks that exist for that
stream are notified to unblock the waiting XREAD command.
The entry point for the Redis implementation is in Main.
The program is divided into separation of concerns with a modules for the service itself, handling of connections,
classes for parsing options, the serialization protocol and commands. The
ConnectionManager owns the connections from clients and it
owns the thread to process incoming commands. The service classes
LeaderService and
FollowerService inherit shared functionality in
RedisServiceBase. These classes own the main processing loop
for commands and they provides the methods that implement each of the commands.
Most commands are synchronous commands and execute on the main processor thread, so the service primarly executes commands sequentially on a single thread. However, the command can identify itself as a blocking command, in which case, the processing loop will execute it on a separate thread (allocated through a Java cached thread pool - see Executors.newCachedThreadPool).
For the special case of a replication service, it has a regular ServerSocket for accepting connections, but it also
opens a client connection to the leader server. There is a handshake with the leader that is implemented in the
ConnectionToLeader class, but after that is complete, the
ownership of the socket connection to leader is transferred to the
ConnectionManager and all future inputs from the leader are
treated as incoming commands to the replication server (replicated commands or PING or GETACK requests from the
leader).
There is a protocol package with classes that represent input and output data. All input and output on the stream is represented as a Redis Serialization Protocol (RESP) object. Commands are usually represented as a Resp Array.
The commands package includes a class for each supported command. The
ArgReader provides functionality for declaring and
validating a set of expected optional and required arguments to command. This enables common validation processing and
error messages for invalid command arguments.
There are 2 more packages to support the extensions for databases rdb and streams.
Selected from shared topics, language and repository description—not editorial ratings.
iamkira420 /
Basic shell implemented in Kotlin, coding challenge from @codecrafters-io
29/100 health