Skip to content

Question: Redis as a HTTP Server #411

@aminroosta

Description

@aminroosta

I'm planning to write the equivalent of webdis, but implemented in rust as a dynamic module.

Here is a tiny example that seems to work just fine. I'd love to hear your thoughts on the drawbacks of this approach.

Expand to see the Code:
use axum::{routing::get, Router};
use lazy_static::lazy_static;
use redis_module::{redis_module, Context, RedisString, Status, ThreadSafeContext};
use tokio::sync::oneshot;
use std::{
    sync::Mutex,
    thread::{self, JoinHandle},
};

lazy_static! {
    static ref SERVER_THREAD: Mutex<Option<ServerHandle>> = Mutex::new(None);
}

struct ServerHandle {
    handle: JoinHandle<()>,
    shutdown_tx: oneshot::Sender<()>,
}

fn init(_ctx: &Context, _args: &[RedisString]) -> Status {
    let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();

    let handle = thread::spawn(move || {
        let runtime = tokio::runtime::Runtime::new().unwrap();
        runtime.block_on(async {
            let app = Router::new().route("/", get(root));
            let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();

            axum::serve(listener, app)
                .with_graceful_shutdown(async {
                    shutdown_rx.await.unwrap();
                })
                .await
                .unwrap();
        });
    });

    let mut server_thread = SERVER_THREAD.lock().unwrap();
    *server_thread = Some(ServerHandle {
        handle,
        shutdown_tx,
    });

    Status::Ok
}

fn deinit(_ctx: &Context) -> Status {
    let mut server_thread = SERVER_THREAD.lock().unwrap();

    if let Some(ServerHandle {
        shutdown_tx,
        handle,
    }) = server_thread.take()
    {
        let _ = shutdown_tx.send(());
        handle.join().unwrap();
    }

    Status::Ok
}

async fn root() -> String {
    let ctx = ThreadSafeContext::new();
    let version = ctx.lock().get_redis_version().unwrap();

    format!("{}.{}.{}", version.major, version.minor, version.patch)
}

//////////////////////////////////////////////////////

redis_module! {
    name: "hello",
    version: 1,
    allocator: (redis_module::alloc::RedisAlloc, redis_module::alloc::RedisAlloc),
    data_types: [],
    init: init,
    deinit: deinit,
    commands: [
    ],
}

I'm able to LOAD and UNLOAD the module.

127.0.0.1:6379> MODULE LOAD /Users/user/axredis/target/release/libaxredis.dylib
OK
127.0.0.1:6379> MODULE UNLOAD hello
OK

While the module is loaded, I can get the redis/valkey version over HTTP.

$ curl 127.0.0.1:3000
8.0.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions