# not_redis

⚠️ **Warning**: This project was vibe-coded using [OpenCode](https://opencode.ai) and [MiniMax 2.1 Free](https://minimax.io). Code was generated by AI and may contain bugs, edge cases, or unexpected behavior. Review thoroughly before production use.

---

## Description

not_redis is a Redis-compatible in-memory data structure library written in Rust. It provides Redis-like APIs without the networking overhead, external service dependencies, or operational complexity of running a Redis server.

**Key Goals:**

- Zero-config, embeddable Redis-compatible storage
- Thread-safe concurrent access via Tokio and DashMap
- RESP-compatible data types and command semantics
- No network overhead - runs in-process with your application
- Minimal dependencies for security and simplicity

## Features

- **Strings**: GET, SET, DEL, EXISTS, EXPIRE, TTL, PERSIST
- **Hashes**: HSET, HGET, HGETALL, HDEL
- **Lists**: LPUSH, RPUSH, LLEN
- **Sets**: SADD, SMEMBERS
- **Utilities**: PING, ECHO, DBSIZE, FLUSHDB

## Installation

```toml
[dependencies]
not_redis = "0.1"
```

## Getting Started

```rust
use not_redis::Client;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Client::new();
    client.start().await;

    // String operations
    client.set("user:1:name", "Alice").await?;
    let name: String = client.get("user:1:name").await?;
    println!("Name: {}", name);

    // Hash operations
    client.hset("user:1", "email", "alice@example.com").await?;
    client.hset("user:1", "age", "30").await?;
    let email: String = client.hget("user:1", "email").await?;
    let profile: Vec<String> = client.hgetall("user:1").await?;
    println!("Email: {}", email);

    // List operations
    client.lpush("user:1:todos", "buy milk").await?;
    client.lpush("user:1:todos", "walk dog").await?;
    let count: i64 = client.llen("user:1:todos").await?;
    println!("Todos: {}", count);

    // Set operations
    client.sadd("user:1:tags", "rust").await?;
    client.sadd("user:1:tags", "developer").await?;
    let tags: Vec<String> = client.smembers("user:1:tags").await?;
    println!("Tags: {:?}", tags);

    // Expiration
    client.set("temp:key", "expires soon").await?;
    client.expire("temp:key", 60).await?;
    let ttl: i64 = client.ttl("temp:key").await?;
    println!("TTL: {} seconds", ttl);

    // Utilities
    let pong: String = client.ping().await?;
    println!("Ping: {}", pong);

    let size: i64 = client.dbsize().await?;
    println!("DB size: {}", size);

    // Cleanup
    let _: String = client.flushdb().await?;

    Ok(())
}
```

## Async Runtime

not_redis requires a Tokio runtime. If you're using it in a non-Tokio context:

```rust
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = Client::new();
    client.start().await;
    // ... your code ...
}
```

## API Reference

### Client

```rust
let mut client = Client::new();
client.start().await;
```

| Method                 | Description                                               |
| ---------------------- | --------------------------------------------------------- |
| `get(key)`             | Get value by key                                          |
| `set(key, value)`      | Set key-value pair                                        |
| `del(key)`             | Delete key, returns count                                 |
| `exists(key)`          | Check if key exists                                       |
| `expire(key, seconds)` | Set key expiration                                        |
| `ttl(key)`             | Get remaining TTL (-2=missing, -1=no expiry, >=0=seconds) |
| `persist(key)`         | Remove expiration, returns success                        |
| `flushdb()`            | Clear all keys                                            |

### Hash Operations

| Method                    | Description           |
| ------------------------- | --------------------- |
| `hset(key, field, value)` | Set hash field        |
| `hget(key, field)`        | Get hash field        |
| `hgetall(key)`            | Get all fields/values |
| `hdel(key, field)`        | Delete hash field     |

### List Operations

| Method              | Description       |
| ------------------- | ----------------- |
| `lpush(key, value)` | Push to list head |
| `rpush(key, value)` | Push to list tail |
| `llen(key)`         | Get list length   |

### Set Operations

| Method              | Description     |
| ------------------- | --------------- |
| `sadd(key, member)` | Add to set      |
| `smembers(key)`     | Get all members |

### Utility Operations

| Method      | Description    |
| ----------- | -------------- |
| `ping()`    | Returns "PONG" |
| `echo(msg)` | Echo message   |
| `dbsize()`  | Number of keys |

## Thread Safety

not_redis uses `DashMap` for thread-safe concurrent access. Multiple threads can share a single `Client` instance.

```rust
use std::sync::Arc;
use not_redis::Client;

let client = Arc::new(Client::new());
let client_clone = client.clone();

tokio::spawn(async move {
    client_clone.set("key", "value").await.unwrap();
});
```

## Error Handling

```rust
use not_redis::{Client, RedisError};

match client.get("nonexistent").await {
    Ok(value) => println!("Found: {}", value),
    Err(RedisError::NoSuchKey(key)) => println!("Key not found: {}", key),
    Err(RedisError::WrongType) => println!("Wrong data type"),
    Err(RedisError::ParseError) => println!("Parse error"),
    Err(e) => println!("Other error: {:?}", e),
}
```

## Why not_redis?

| Feature    | Redis                        | not_redis             |
| ---------- | ---------------------------- | --------------------- |
| Setup      | Requires Redis server        | No setup needed       |
| Network    | TCP/IP overhead              | In-process, zero-copy |
| Operations | Requires redis-cli or client | Direct API calls      |
| Deployment | Additional service           | Single binary         |

## Limitations

- No persistence (data lost on restart)
- No networking layer
- No clustering or replication
- Limited command set (growing)
- No Lua scripting
- No pub/sub

## Benchmarks

Benchmarks were run on Apple Silicon (M1) with not_redis and Redis (via redis-rs) to compare performance. Redis was running in a Docker container on the same machine.

### Single-Threaded Operations

| Operation      | not_redis   | Redis    | Difference |
|----------------|-------------|----------|------------|
| string/set     | 360,000 ops/s | 1,300 ops/s | **+27,646%** |
| string/get     | 356,000 ops/s | 360 ops/s | **+98,889%** |
| hash/hset      | 348,000 ops/s | 1,250 ops/s | **+27,740%** |
| hash/hget      | 319,000 ops/s | 1,400 ops/s | **+22,771%** |
| list/lpush     | 358,000 ops/s | N/A | - |
| list/rpush     | 362,000 ops/s | N/A | - |
| set/sadd       | 351,000 ops/s | N/A | - |

### Throughput (Batch Operations)

| Operation           | not_redis    | Redis     | Difference |
|---------------------|--------------|-----------|------------|
| batch_writes/100    | 4.6M ops/s   | N/A       | -          |
| batch_writes/1000   | 5.2M ops/s   | N/A       | -          |
| batch_reads/100     | 3.1M ops/s   | N/A       | -          |
| batch_reads/1000    | 3.3M ops/s   | N/A       | -          |

### Why not_redis is Faster

not_redis significantly outperforms Redis for in-process operations because:

1. **Zero network overhead** - not_redis runs in the same process, eliminating TCP/IP communication
2. **No serialization/deserialization** - Direct memory access vs Redis protocol parsing
3. **No connection management** - No connection pooling overhead
4. **Cache-friendly** - Data stays in-process, maximizing CPU cache utilization

For applications that don't need Redis's networking capabilities, not_redis provides substantial performance improvements while offering a compatible API.

## Contributing

Issues and PRs welcome. Note: This is a vibe-coded project - expect quirks.

## License

MIT
