Memory In-memory Rapid Async Yield
A high-performance, TCP-based in-memory key-value store built with Node.js. MIRAY provides production-grade storage with WAL (Write-Ahead Log), binary persistence, and automatic checkpointing.
π Named after Miray - This project is lovingly named after my daughter, Miray. Just as she brings light and joy to our lives, this project aims to bring simplicity and performance to in-memory data storage.
In modern application development, you often need a fast, temporary storage solution for:
- Session Management: Store user sessions with automatic expiration
- Caching: Speed up your applications by caching frequently accessed data
- Rate Limiting: Track API usage with time-based limits
- Temporary State: Store temporary application state across services
- Feature Flags: Dynamic feature toggles with TTL support
While Redis is a popular choice, MIRAY offers a lightweight, Node.js-native alternative that:
- β Zero configuration - Works out of the box with sensible defaults
- β Native JavaScript - Built with Node.js, no C bindings or external dependencies
- β Crash-safe - Write-Ahead Log ensures your data survives crashes
- β Developer-friendly - Simple API, easy to understand and extend
- β Production-ready - Handles 100K+ ops/sec with persistence
Perfect for microservices, serverless applications, or any Node.js project needing fast temporary storage.
Install MIRAY globally to use the server and CLI anywhere:
npm install -g miray-server miray-cliInstall the client library in your project:
npm install miray-clientRun MIRAY server using Docker (pulls latest from npm):
# Pull the image
docker pull yasaricli/miray-server
# Run without authentication
docker run -d --name miray-server -p 7779:7779 yasaricli/miray-server
# Run with authentication
docker run -d --name miray-server -p 7779:7779 -e USERNAME=admin -e PASSWORD=secret123 yasaricli/miray-server
# Run with persistent data
docker run -d --name miray-server -p 7779:7779 -v miray-data:/app/data yasaricli/miray-serverThe Dockerfile:
- Installs the latest
miray-serverfrom npm - Uses Alpine Linux for minimal size (~50MB)
- Runs as non-root user for security
- Supports environment variables:
PORT,HOST,USERNAME,PASSWORD - Includes proper signal handling with dumb-init
# Start MIRAY server (default port: 7779)
miray-server
# Or with custom port
miray-server --port 8000
# Or with authentication (username:password)
miray-server --username admin --password secret123You should see:
MIRAY Server starting...
Listening on 0.0.0.0:7779
Authentication disabled (no credentials provided)
Or with authentication enabled:
MIRAY Server starting...
Listening on 0.0.0.0:7779
Authentication enabled (username: admin)
Open a new terminal and start the interactive CLI:
# Connect to server without authentication
miray-cli
# Or connect to specific host/port
miray-cli --host localhost --port 8000
# Or connect with authentication
miray-cli --username admin --password secret123Try these commands:
> HELP
# Shows all available commands
> PING
PONG
> PUSH user:123 "John Doe" 5m
OK
> GET user:123
"John Doe"
> KEYS user:*
1) "user:123"
> TTL user:123
299000 # milliseconds remaining
> INFO
MIRAY Server (WAL + Binary)
# Storage
keys: 1
memory: ~50 bytes
...Install the client:
npm install miray-clientBasic usage:
import MirayClient from 'miray-client';
// Without authentication
const client = new MirayClient({
host: 'localhost',
port: 7779
});
// Or with authentication
const clientWithAuth = new MirayClient({
host: 'localhost',
port: 7779,
username: 'admin',
password: 'secret123'
});
// Connect to server (automatically authenticates if credentials provided)
await client.connect();
// Store data
await client.push('user:123', 'John Doe');
await client.push('session:abc', { userId: 123, role: 'admin' }, '30m');
// Retrieve data
const user = await client.get('user:123');
console.log(user); // "John Doe"
const session = await client.get('session:abc');
console.log(session); // { userId: 123, role: 'admin' }
// Remove data
await client.remove('user:123');
// Batch operations
await client.mPush(['key1', 'value1', 'key2', 'value2']);
const values = await client.mGet(['key1', 'key2']);
// Get all keys matching pattern
const userKeys = await client.keys('user:*');
// Clean up
await client.disconnect();import MirayClient from 'miray-client';
const sessionStore = new MirayClient();
await sessionStore.connect();
// Store session with 1 hour expiration
async function createSession(userId, sessionData) {
const sessionId = generateSessionId();
await sessionStore.push(
`session:${sessionId}`,
{ userId, ...sessionData },
'1h'
);
return sessionId;
}
// Get session
async function getSession(sessionId) {
return await sessionStore.get(`session:${sessionId}`);
}
// Extend session
async function extendSession(sessionId) {
const session = await sessionStore.get(`session:${sessionId}`);
if (session) {
await sessionStore.push(`session:${sessionId}`, session, '1h');
}
}import MirayClient from 'miray-client';
const rateLimiter = new MirayClient();
await rateLimiter.connect();
async function checkRateLimit(apiKey, maxRequests = 100) {
const key = `ratelimit:${apiKey}`;
const current = await rateLimiter.get(key);
if (!current) {
// First request in this minute
await rateLimiter.push(key, '1', '1m');
return { allowed: true, remaining: maxRequests - 1 };
}
const count = parseInt(current, 10);
if (count >= maxRequests) {
const ttl = await rateLimiter.ttl(key);
return { allowed: false, resetIn: ttl };
}
await rateLimiter.push(key, String(count + 1), '1m');
return { allowed: true, remaining: maxRequests - count - 1 };
}import MirayClient from 'miray-client';
const cache = new MirayClient();
await cache.connect();
async function getUser(userId) {
const cacheKey = `user:${userId}`;
// Try cache first
const cached = await cache.get(cacheKey);
if (cached) {
return cached;
}
// Cache miss - fetch from database
const user = await db.users.findById(userId);
// Store in cache for 5 minutes
await cache.push(cacheKey, user, '5m');
return user;
}- β¨ High Performance: 100K+ ops/sec with concurrent connections
- π Crash-Safe: Write-Ahead Log ensures data durability
- π¦ Binary Storage: MessagePack format (50-70% smaller than JSON)
- β±οΈ TTL Support: Automatic expiration (30s, 5m, 2h, 1d)
- π Auto Checkpointing: Periodic snapshots with WAL compaction
- π§Ή Auto Cleanup: Expired keys are automatically removed
- π― Pattern Matching: Wildcard key queries (KEYS user:*)
- π Monitoring: Built-in metrics and connection tracking
- π Batch Operations: MGET, MPUSH, MREMOVE for better performance
MIRAY supports optional username/password authentication to secure your server.
# Start server with authentication
miray-server --username admin --password secret123When authentication is enabled:
- All clients must authenticate before executing commands
- Unauthenticated clients will receive
-ERR NOAUTH Authentication required - Only the
AUTHcommand works before authentication
CLI:
miray-cli --username admin --password secret123JavaScript Client:
const client = new MirayClient({
host: 'localhost',
port: 7779,
username: 'admin',
password: 'secret123'
});
await client.connect(); // Automatically authenticatesManual AUTH Command:
> AUTH admin secret123
OK Authenticated| Command | Description | Example |
|---|---|---|
PING |
Test server connectivity | PING β PONG |
HELP |
Show all available commands | HELP |
AUTH user pass |
Authenticate (if auth enabled) | AUTH admin secret123 |
PUSH key value [ttl] |
Store key-value pair | PUSH user:1 "John" 5m |
GET key |
Retrieve value | GET user:1 |
REMOVE key |
Delete key | REMOVE user:1 |
KEYS pattern |
List matching keys | KEYS user:* |
TTL key |
Get remaining TTL in ms | TTL user:1 |
FLUSH |
Clear all data | FLUSH |
INFO |
Server statistics | INFO |
MGET key1 key2 ... |
Get multiple values | MGET user:1 user:2 |
MPUSH key1 val1 ... |
Set multiple pairs | MPUSH k1 v1 k2 v2 |
MREMOVE key1 key2 ... |
Delete multiple keys | MREMOVE k1 k2 |
The server can be configured via command-line arguments or by editing packages/common/src/config.js:
miray-server --port 8000 --host 127.0.0.1export const config = {
server: {
host: '0.0.0.0',
port: 7779,
},
storage: {
walFile: './data/miray.wal',
snapshotFile: './data/miray.snapshot',
checkpointInterval: 30000, // 30 seconds
cleanupInterval: 1000, // 1 second
},
};ββββββββββββββββ
β Client β (miray-client, miray-cli)
β Packages β
ββββββββ¬ββββββββ
β TCP
βΌ
ββββββββββββββββ
β miray- β
β server β
ββββββββ¬ββββββββ
β
βββββββββββββββ
β β
βΌ βΌ
βββββββββββββββ ββββββββββββββββ
β WAL (write β β Snapshot β
β ahead log) β β (binary) β
βββββββββββββββ ββββββββββββββββ
data/
βββ miray.wal # Write-ahead log (append-only)
βββ miray.snapshot # Binary snapshot (MessagePack)
TL;DR: MIRAY delivers excellent performance for a Node.js-native in-memory store:
- π 100K+ ops/sec with 50-100 concurrent clients
- β‘ 11K reads/sec, 6K writes/sec (single connection)
- π Peak: 143K reads/sec (100 concurrent clients)
- π― Sub-millisecond latency (<0.2ms average)
- π Scales to 1000+ connections (35K ops/sec)
# Single connection test
npm run benchmark
# Concurrent connections test
npm run benchmark:concurrentFor comprehensive performance metrics, optimization strategies, and real-world capacity planning, see:
π PERFORMANCE.md - Complete performance analysis including:
- Detailed benchmark results
- Scalability analysis
- Docker performance impact
- Comparison with Redis
- Tuning recommendations
- Monitoring guide
- Node.js 18+
- npm or yarn
This project uses Lerna for managing multiple packages:
miray/
βββ packages/
β βββ miray-common # Shared configuration and utilities
β βββ miray-server # TCP server implementation
β βββ miray-client # Node.js SDK client
β βββ miray-cli # Interactive command-line interface
βββ benchmarks/ # Performance benchmarks
βββ Dockerfile # Docker image definition
βββ lerna.json # Lerna configuration
High-performance TCP server with WAL and binary persistence.
Node.js SDK for connecting to MIRAY server.
Interactive REPL for MIRAY server.
Shared configuration and utilities.
# Publish all changed packages
npx lerna publish
# Publish specific version
npx lerna publish minor
npx lerna publish major- Create a feature branch
- Make changes in relevant package
- Test with
npm run test - Submit pull request
MIT
Made with β€οΈ for Miray