# Redis Key-Pattern Search in DBX: Architecture and Implementation

> Explore DBX's Redis key-pattern search using a Tauri-Rust architecture. Learn how batched SCAN operations optimize performance for standalone and cluster Redis.

- Repository: [skyler/dbx](https://github.com/t8y2/dbx)
- Tags: architecture
- Published: 2026-07-02

---

**DBX implements Redis key-pattern search through a layered Tauri-Rust architecture that batches SCAN operations server-side to minimize network round-trips while supporting both standalone and cluster Redis deployments.**

The DBX database client (t8y2/dbx) provides efficient Redis key-pattern search capabilities through a sophisticated multi-layered architecture. This implementation leverages Tauri's frontend-backend bridge combined with a Rust-based core library to handle pattern matching, database selection, and batch processing across both direct and clustered Redis connections.

## Architecture Overview

DBX implements key-pattern searching across three distinct layers:

1. **Frontend (TypeScript)** – UI components invoke Tauri commands through `redisScanKeys` and `redisScanKeysBatch` functions
2. **Tauri Command Layer (Rust)** – Bridges frontend requests to the core library in [`src-tauri/src/commands/redis_cmd.rs`](https://github.com/t8y2/dbx/blob/main/src-tauri/src/commands/redis_cmd.rs)
3. **Core Library (Rust)** – `crates/dbx-core` handles the actual Redis SCAN operations, connection pooling, and batch iteration logic

This architecture allows DBX to perform complex pattern searches (e.g., `user:*`) while optionally returning key type metadata and supporting both single-node and clustered Redis deployments.

## Frontend TypeScript API

The frontend interface in [`apps/desktop/src/lib/tauri.ts`](https://github.com/t8y2/dbx/blob/main/apps/desktop/src/lib/tauri.ts) exposes two distinct scanning modes:

```typescript
// apps/desktop/src/lib/tauri.ts
export async function redisScanKeys(
  connectionId: string,
  db: number,
  cursor: number,
  pattern: string,
  count: number,
) {
  // Single SCAN iteration - returns one page of results
  return invoke("redis_scan_keys", { connectionId, db, cursor, pattern, count });
}

export async function redisScanKeysBatch(
  connectionId: string,
  db: number,
  cursor: number,
  pattern: string,
  count: number,
  maxIterations: number,
  includeTypes: boolean,
) {
  // Server-side batch processing - multiple SCAN cycles in one call
  return invoke("redis_scan_keys_batch", {
    connectionId,
    db,
    cursor,
    pattern,
    count,
    maxIterations,
    includeTypes,
  });
}

```

The `count` parameter controls how many keys Redis returns per SCAN iteration, while `maxIterations` determines how many cursor steps the backend executes before returning aggregated results to the UI.

## Tauri Command Layer

The Tauri commands in [`src-tauri/src/commands/redis_cmd.rs`](https://github.com/t8y2/dbx/blob/main/src-tauri/src/commands/redis_cmd.rs) act as thin wrappers that forward requests to the core library:

```rust
// src-tauri/src/commands/redis_cmd.rs
pub async fn redis_scan_keys(
    state: State<'_, Arc<AppState>>,
    connection_id: String,
    db: u32,
    cursor: u64,
    pattern: String,
    count: usize,
) -> Result<RedisScanResult, String> {
    dbx_core::redis_ops::redis_scan_keys_core(
        &state,
        &connection_id,
        db,
        cursor,
        &pattern,
        count,
    )
    .await
}

```

The batch variant forwards additional parameters for server-side iteration:

```rust
pub async fn redis_scan_keys_batch(
    state: State<'_, Arc<AppState>>,
    connection_id: String,
    db: u32,
    cursor: u64,
    pattern: String,
    count: usize,
    max_iterations: usize,
    include_types: bool,
) -> Result<RedisScanResult, String> {
    dbx_core::redis_ops::redis_scan_keys_batch_core(
        &state,
        &connection_id,
        db,
        cursor,
        &pattern,
        count,
        max_iterations,
        include_types,
    )
    .await
}

```

Both commands are registered in [`src-tauri/src/lib.rs`](https://github.com/t8y2/dbx/blob/main/src-tauri/src/lib.rs) to expose them to the frontend.

## Core Library Implementation

The actual Redis operations reside in [`crates/dbx-core/src/redis_ops.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-core/src/redis_ops.rs). The `redis_scan_keys_core` function delegates to the batch implementation with a single iteration:

```rust
// crates/dbx-core/src/redis_ops.rs
pub async fn redis_scan_keys_core(
    state: &AppState,
    connection_id: &str,
    db: u32,
    cursor: u64,
    pattern: &str,
    count: usize,
) -> Result<RedisScanResult, String> {
    redis_scan_keys_batch_core(state, connection_id, db, cursor, pattern, count, 1, true).await
}

```

The batch core performs up to `max_iterations` SCAN cycles server-side:

```rust
pub async fn redis_scan_keys_batch_core(
    state: &AppState,
    connection_id: &str,
    db: u32,
    cursor: u64,
    pattern: &str,
    count: usize,
    max_iterations: usize,
    include_types: bool,
) -> Result<RedisScanResult, String> {
    ensure_redis_pool(state, connection_id).await?;
    let connections = state.connections.read().await;
    let pool = connections.get(connection_id).ok_or("Connection not found")?;

    match pool {
        PoolKind::Redis(redis) => match redis {
            RedisConnection::Direct(con) => {
                let mut con = con.lock().await;
                redis_driver::select_db(&mut *con, db).await?;
                redis_driver::scan_keys_batch(
                    &mut *con,
                    cursor,
                    pattern,
                    count,
                    max_iterations,
                    include_types,
                )
                .await
            }
            RedisConnection::Cluster(cluster) => {
                redis_driver::ensure_cluster_db(db)?;
                // Cluster-specific batch handling...
            }
        },
        _ => Err("Not a Redis connection".to_string()),
    }
}

```

### Handling Direct Connections

For standalone Redis servers, DBX uses `RedisConnection::Direct` and explicitly selects the database index via `redis_driver::select_db`. This ensures pattern searches operate against the correct logical database (0-15 in standard Redis configurations).

### Handling Cluster Deployments

When connecting to `RedisConnection::Cluster`, DBX validates the database selection through `ensure_cluster_db` before executing distributed scans. Since Redis Cluster handles key distribution across nodes, the scan operation automatically traverses the cluster topology while the batch logic accumulates results across multiple cursor iterations.

### Batch Processing Benefits

Setting `max_iterations > 1` drastically reduces frontend-to-backend round-trips. A naive implementation requesting one cursor page at a time would generate a network request for every SCAN iteration. By batching up to `max_iterations` cycles on the server side, DBX can fetch hundreds or thousands of keys in a single request, improving latency and reducing load on the Redis server.

## Web API Integration

Beyond the desktop application, the same core functions are exposed via HTTP routes in [`crates/dbx-web/src/routes/redis.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-web/src/routes/redis.rs). This allows external tools, browser extensions, or third-party integrations to utilize identical key-pattern search logic through the DBX web server.

## Summary

- **DBX uses a three-tier architecture** (TypeScript → Tauri → Rust core) to execute Redis SCAN commands with minimal network overhead
- **Batch mode (`max_iterations`)** processes multiple cursor iterations server-side before returning results to the frontend
- **Dual connection support** handles both `Direct` (standalone) and `Cluster` Redis configurations with proper database selection
- **Optional type metadata** returns Redis key types (string, hash, list, etc.) when `include_types` is enabled
- **Shared core logic** is exposed via both Tauri commands and HTTP routes for maximum flexibility across desktop and web interfaces

## Frequently Asked Questions

### How does DBX handle large key spaces when scanning?

DBX utilizes the `max_iterations` parameter in `redis_scan_keys_batch_core` to perform multiple SCAN cycles on the server side before returning results to the frontend. This approach significantly reduces network round-trips compared to cursor-by-cursor fetching, making it suitable for databases containing millions of keys.

### Does DBX support Redis Cluster for pattern searches?

Yes, the core library explicitly handles `RedisConnection::Cluster` variants in [`crates/dbx-core/src/redis_ops.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-core/src/redis_ops.rs). For cluster connections, DBX validates database selection through `ensure_cluster_db` while leveraging Redis Cluster's distributed scan capabilities to search across all nodes in the topology.

### What information does DBX return for each matched key?

When the `include_types` parameter is set to `true`, DBX returns `RedisKeyInfo` objects containing the key name, its Redis data type (string, hash, list, set, etc.), and the next cursor position for pagination. This eliminates the need for additional TYPE command round-trips for each matched key.

### Can the Redis key-pattern search be used outside the desktop application?

Yes, the identical `redis_scan_keys_core` functions are exposed via HTTP routes in [`crates/dbx-web/src/routes/redis.rs`](https://github.com/t8y2/dbx/blob/main/crates/dbx-web/src/routes/redis.rs). This allows external tools, browser extensions, or automated scripts to utilize the same scanning logic and connection pooling through the DBX web server API.