Node In Memory DB
- Published on
- Duration
- 5 days
- Role
- Developer
About
This project took inspiration from an in memory database written in python. As a challenge I wanted to see if I could rewrite the program in node.js
Some challenges I faced along the way was not having to deal with socket file like objects like python does. Instead the socket is already a streamable
piece of data called a Buffer. I then had to find a way to keep track of where I was in the buffer, so a cursor property in the protocol handler was made.
I took this a step further and wrote my own restoration function. For every flush in the database a log is generated of the commands that came before the flush. This allows me to recreate the database from a log file by playing back the log.
Code Snippets
Client class
class Client {
constructor(host = "127.0.0.1", port = 31337) {
this.protocol = new ProtocolHandler();
this.partialData = null;
this.socket = new net.Socket();
this.socket.connect(port, host, () => {
console.log("Connected to the server");
});
Server class
class Server extends EventEmitter {
constructor(host = "127.0.0.1", port = 31337) {
super();
this.host = host;
this.port = port;
this.server = net.createServer(this.connectionHandler.bind(this));
this.kv = new Map();
this.commands = this.getCommands();
this.protocolHandler = new ProtocolHandler();
this.currentLog = `log-${Date.now()}.txt`;
}
Protocol Handler (Shared by the client and server)
class ProtocolHandler {
constructor() {
this.cursor = 0;
this.handlers = {
"+": this.handleSimpleString,
"-": this.handleError,
":": this.handleInteger,
$: this.handleString,
"*": this.handleArray,
"%": this.handleDict,
};
}
Handling Requests
handleRequest(buffer) {
//the first byte will tell us the data type we are dealing with.
const firstByte = decoder.write(buffer.slice(this.cursor, this.cursor + 1));
if (!firstByte) {
throw new Error("Invalid data");
}
const handler = this.handlers[firstByte];
if (!handler) {
throw new Error("Bad request");
}
this.cursor++;
return handler.call(this, buffer);
}
Writing to the socket
Each element in the buffer is terminated by a \r\n similar to redis
write(buf, data) {
if (typeof data === "string") {
const strData = Buffer.from(data, "utf-8");
buf.push(Buffer.from(`$${strData.length}\r\n`));
buf.push(strData);
buf.push(Buffer.from("\r\n"));
} else if (typeof data === "number") {
buf.push(Buffer.from(`:${data}\r\n`));
} else if (data instanceof Error) {
buf.push(Buffer.from(`-${data.message}\r\n`));
} else if (Array.isArray(data)) {
buf.push(Buffer.from(`*${data.length}\r\n`));
for (const item of data) {
this.write(buf, item);
}
} else if (typeof data === "object" && data !== null) {
// Assuming it's a dictionary
const keys = Object.keys(data);
buf.push(Buffer.from(`%${keys.length}\r\n`));
for (const key of keys) {
this.write(buf, key);
this.write(buf, data[key]);
}
} else if (data === null) {
buf.push(Buffer.from("$-1\r\n"));
} else {
throw new Error("Unrecognized type: " + typeof data);
}
}
In-Memory Database Over TCP
A simple, efficient, and lightweight in-memory database that communicates over TCP using a custom protocol.
🚀 Getting Started
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
📋 Prerequisites
- Node.js v14.x.x or later - npm v6.x.x or later
🛠️ Installation
- Clone the repository:
git clone git@github.com:jmurphy1196/node-in-memory-database.git
- Change to the project directory:
cd node-in-memory-db
- Install the dependencies:
npm install
- Start the server:
node server.js
You should now have the server running on 127.0.0.1:31337
.
🖥️ Usage
For interaction, use the provided client. Here are some basic commands:
const { Client } = require('./client');
const client = new Client();
client.set("key", "value");
client.get("key");
📈 Features
- In-Memory Storage: Fast data retrieval and storage with no persistence overhead.
- Custom Protocol: Efficient and simple protocol for communication.
- TCP Communication: Reliable data transfer over TCP.
🛠️ Built With
- Node.js: JavaScript runtime.
📄 License
This project is licensed under the MIT License - see the LICENSE.md file for details.