|
| 1 | +# Redis Backend Phase 1 Refactor Design |
| 2 | + |
| 3 | +## Context |
| 4 | + |
| 5 | +`src-tauri/src/datasources/redis.rs` is over 3,600 lines and mixes connection |
| 6 | +setup, Redis command execution, key scanning, typed key operations, stream |
| 7 | +logic, sorted set helpers, geo helpers, server metadata, raw console execution, |
| 8 | +DTOs, and string-based error handling. `src-tauri/src/commands/redis.rs` is over |
| 9 | +1,200 lines and mixes Tauri command boundaries with connection cache/retry |
| 10 | +logic and command log handling. |
| 11 | + |
| 12 | +Existing Rust integration tests import `dbpaw_lib::datasources::redis::*` |
| 13 | +directly. The frontend calls fixed Tauri command names through |
| 14 | +`src/services/api.ts`. Phase 1 must therefore keep public behavior and public |
| 15 | +paths stable while reducing backend module size and stopping further Redis |
| 16 | +string-error growth. |
| 17 | + |
| 18 | +## Goals |
| 19 | + |
| 20 | +- Keep all Tauri command names, signatures, and frontend TypeScript wrappers |
| 21 | + unchanged. |
| 22 | +- Keep `dbpaw_lib::datasources::redis::*` usable for existing integration tests. |
| 23 | +- Split Redis datasource internals by responsibility. |
| 24 | +- Introduce Redis-specific error conversion so new and moved datasource code can |
| 25 | + use structured errors internally. |
| 26 | +- Convert Redis errors to `String` only at the command boundary. |
| 27 | +- Preserve current Redis runtime behavior, including standalone, cluster, and |
| 28 | + sentinel connection behavior. |
| 29 | + |
| 30 | +## Non-Goals |
| 31 | + |
| 32 | +- Do not redesign the Redis UI or TypeScript service API. |
| 33 | +- Do not rename existing Tauri commands. |
| 34 | +- Do not change Redis integration test semantics. |
| 35 | +- Do not introduce a new Redis driver trait or broader datasource abstraction. |
| 36 | +- Do not fully rewrite command registration in `src-tauri/src/lib.rs`. |
| 37 | + |
| 38 | +## Architecture |
| 39 | + |
| 40 | +`src-tauri/src/datasources/redis.rs` remains the public facade module. It will |
| 41 | +declare private or crate-visible submodules under `src-tauri/src/datasources/redis/` |
| 42 | +and re-export the same public types and functions currently used by tests and |
| 43 | +commands. |
| 44 | + |
| 45 | +Planned datasource modules: |
| 46 | + |
| 47 | +- `connection.rs`: `RedisConnection`, connection cache handle types, standalone, |
| 48 | + cluster, and sentinel connection construction, database selection, host parsing, |
| 49 | + timeout handling, and low-level query helpers. |
| 50 | +- `error.rs`: `RedisError`, `RedisResult<T>`, conversion from |
| 51 | + `redis::RedisError`, validation failures, scan cursor failures, unsupported |
| 52 | + cluster routing, and conversion into `AppError` or final command-boundary text. |
| 53 | +- `models.rs`: DTOs, payloads, enums, and result structs. |
| 54 | +- `scan.rs`: standalone scan, cluster scan, cursor encoding/decoding, cluster |
| 55 | + master discovery, node address parsing, wildcard guard, and key metadata lookup. |
| 56 | +- `key_value.rs`: key validation, get/set/page/patch/delete/rename/ttl, bitmap, |
| 57 | + HyperLogLog, set/list helpers, and batch mget/mset/key operations. |
| 58 | +- `stream.rs`: stream entry parsing, stream view/range, groups, ack, pending, |
| 59 | + claim, trim, and readgroup operations. |
| 60 | +- `zset.rs`: sorted set score, rank, range by score, range by lex, lex count, |
| 61 | + pop min, and pop max operations. |
| 62 | +- `geo.rs`: geo add, position, distance, and search operations. |
| 63 | +- `server.rs`: server info, config, slowlog, and cluster info parsing. |
| 64 | +- `raw.rs`: raw command tokenization, Redis value formatting, and raw execution. |
| 65 | + |
| 66 | +The facade should make call sites continue to look like: |
| 67 | + |
| 68 | +```rust |
| 69 | +use dbpaw_lib::datasources::redis; |
| 70 | + |
| 71 | +let mut conn = redis::connect(&form, None).await?; |
| 72 | +let value = redis::get_key(&mut conn, key).await?; |
| 73 | +``` |
| 74 | + |
| 75 | +## Command Boundary |
| 76 | + |
| 77 | +`src-tauri/src/commands/redis.rs` keeps all existing `#[tauri::command]` |
| 78 | +functions and return shapes. The command file may get a small private submodule |
| 79 | +or helper extraction for cache/retry logic, but Phase 1 does not split commands |
| 80 | +into multiple public command modules. |
| 81 | + |
| 82 | +The command layer remains responsible for: |
| 83 | + |
| 84 | +- Loading a `ConnectionForm` from `AppState`. |
| 85 | +- Acquiring or creating cached Redis connections. |
| 86 | +- Retrying once after Redis IO-style connection failures. |
| 87 | +- Converting datasource errors into `String` for Tauri. |
| 88 | +- Appending raw console command logs. |
| 89 | + |
| 90 | +## Error Handling |
| 91 | + |
| 92 | +New datasource internals should return `RedisResult<T>` instead of |
| 93 | +`Result<T, String>`. `RedisError` should cover at least: |
| 94 | + |
| 95 | +- Redis server/client errors from `redis::RedisError`. |
| 96 | +- Validation errors such as empty keys or invalid write payloads. |
| 97 | +- Invalid database selection. |
| 98 | +- Invalid scan cursor. |
| 99 | +- Unsupported routing or command behavior for cluster mode. |
| 100 | +- Parse/format errors that are internal to Redis response handling. |
| 101 | + |
| 102 | +`RedisError` converts into `AppError` using existing categories: |
| 103 | + |
| 104 | +- Connection and IO failures map to `AppError::ConnectionFailed` or timeout/auth |
| 105 | + variants when the source clearly indicates that class. |
| 106 | +- Redis command failures map to `AppError::query_failed`. |
| 107 | +- User input and cursor validation failures map to `AppError::validation`. |
| 108 | +- Unsupported cluster/command behavior maps to `AppError::unsupported`. |
| 109 | +- Unexpected parse/state failures map to `AppError::internal`. |
| 110 | + |
| 111 | +The command boundary converts `RedisError` to `String` via `AppError`, matching |
| 112 | +the project rule that structured errors cross the backend from the inside out. |
| 113 | +During Phase 1, legacy helper functions may temporarily retain `Result<T, String>` |
| 114 | +behind the facade where required to keep the refactor mechanical, but newly |
| 115 | +moved code should not add new string tags such as `[REDIS_ERROR]` or |
| 116 | +`[REDIS_SCAN_ERROR]`. |
| 117 | + |
| 118 | +IO retry detection should move away from parsing `[REDIS_ERROR]` prefixes and |
| 119 | +toward a Redis-specific predicate on `RedisError`. Existing retry unit tests |
| 120 | +should be updated to assert the typed predicate. |
| 121 | + |
| 122 | +## Testing |
| 123 | + |
| 124 | +Minimum verification after Rust changes: |
| 125 | + |
| 126 | +- Run targeted Redis command/helper tests if present. |
| 127 | +- Run `cargo check` from `src-tauri` before declaring completion. |
| 128 | + |
| 129 | +Additional verification when local dependencies allow it: |
| 130 | + |
| 131 | +- Run Redis integration tests that use `src-tauri/tests/redis_integration.rs`. |
| 132 | +- Use `IT_REUSE_LOCAL_DB=1` when iterating against an already-running Redis |
| 133 | + instance. |
| 134 | + |
| 135 | +Because Redis integration tests may require Docker or local Redis services, |
| 136 | +failure to run them should be reported explicitly rather than hidden. |
| 137 | + |
| 138 | +## Migration Strategy |
| 139 | + |
| 140 | +1. Add `src-tauri/src/datasources/redis/` modules and move types/functions in |
| 141 | + small responsibility groups. |
| 142 | +2. Keep `src-tauri/src/datasources/redis.rs` as the facade and re-export |
| 143 | + existing public API. |
| 144 | +3. Add `RedisError` and migrate the connection/query helpers first, because most |
| 145 | + other modules depend on them. |
| 146 | +4. Migrate scan, key/value, stream, zset, geo, server, and raw modules one group |
| 147 | + at a time. |
| 148 | +5. Update `commands/redis.rs` helpers to consume typed Redis errors internally |
| 149 | + while returning `String` from Tauri commands. |
| 150 | +6. Run formatting and Rust verification after each meaningful group or at least |
| 151 | + before final completion. |
| 152 | + |
| 153 | +## Risks |
| 154 | + |
| 155 | +- Moving many functions can accidentally change visibility. The facade should |
| 156 | + re-export deliberately and tests should continue importing through the old |
| 157 | + path. |
| 158 | +- Redis cluster and sentinel behavior is more fragile than standalone behavior. |
| 159 | + Connection code should be moved before behavior is changed, and cluster-specific |
| 160 | + helpers should remain covered by existing integration tests where available. |
| 161 | +- Full error migration may be too large for one mechanical pass. The acceptable |
| 162 | + Phase 1 fallback is to introduce the typed error path at core connection/query |
| 163 | + boundaries and prevent new string-tag protocols while leaving isolated legacy |
| 164 | + conversions for later cleanup. |
0 commit comments