Support single instance image by using local key set & sqlite#2
Support single instance image by using local key set & sqlite#2
Conversation
- Add DbPool enum (Pg/Sqlite) with query dispatch macros in src/db.rs - Add Cache enum (Redis/InMemory) in src/cache.rs - Create SQLite migration files in migrations_sqlite/ - Update config.rs to make Redis optional - Update state.rs to use DbPool + Cache - Update all services to use portable SQL and new abstractions - Update middleware and routes to use Cache instead of Redis directly - Update main.rs for dual-mode initialization Agent-Logs-Url: https://github.com/poly-workshop/llm-gateway-rs/sessions/46818ccf-35c2-4bbd-b867-d2409cdea35b Co-authored-by: slhmy <31381093+slhmy@users.noreply.github.com>
- Box the Redis ConnectionManager in Cache enum to fix large_enum_variant - Use is_some_and instead of map_or - Fix list_logs to construct separate query objects per DB variant - Update Dockerfile, .env.example, and README.md for single-instance mode Agent-Logs-Url: https://github.com/poly-workshop/llm-gateway-rs/sessions/46818ccf-35c2-4bbd-b867-d2409cdea35b Co-authored-by: slhmy <31381093+slhmy@users.noreply.github.com>
…or logging Agent-Logs-Url: https://github.com/poly-workshop/llm-gateway-rs/sessions/46818ccf-35c2-4bbd-b867-d2409cdea35b Co-authored-by: slhmy <31381093+slhmy@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a “single-instance” deployment mode that runs the gateway with SQLite for persistence and an in-memory cache for key/model route lookups, while preserving the existing PostgreSQL + Redis mode.
Changes:
- Introduces
DbPool+ query-dispatch macros to support PostgreSQL and SQLite with shared service logic. - Introduces
Cacheabstraction to support Redis or in-memory caching, and updates services/middleware/routes to use it. - Adds SQLite migrations and updates startup/config/docs to auto-select the correct DB/cache mode.
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/state.rs | Switches shared state from PgPool/Redis manager to DbPool/Cache. |
| src/main.rs | Initializes Pg vs SQLite pool, runs correct migrations, selects Redis vs in-memory cache, warms caches. |
| src/config.rs | Makes redis_url optional; adds SQLite auto-detection helper. |
| src/db.rs | Adds DbPool enum and query/execute/scalar macros for DB-agnostic dispatch. |
| src/cache.rs | Adds Cache enum with Redis + in-memory implementations for SET/HASH ops used by services. |
| src/services/provider_service.rs | Updates provider CRUD to use DbPool and portable timestamp binds. |
| src/services/model_service.rs | Updates model CRUD + route resolution to use DbPool + Cache instead of Redis directly. |
| src/services/key_service.rs | Updates key CRUD/validation to use DbPool + Cache instead of Redis directly. |
| src/services/log_service.rs | Ports logging + dashboard queries to DbPool, adds SQLite-friendly SQL variants. |
| src/middleware/auth.rs | Switches user-key auth middleware to validate via Cache + DbPool. |
| src/routes/admin.rs | Updates admin handlers to use Cache and refresh caches after provider/model changes. |
| src/routes/proxy.rs | Updates proxy routing to resolve model routes via Cache. |
| migrations_sqlite/001_init.sql | Adds initial SQLite schema for keys/providers/models. |
| migrations_sqlite/002_request_logs.sql | Adds SQLite request log table + indexes. |
| migrations_sqlite/003_token_budget.sql | Adds SQLite token budget columns. |
| migrations_sqlite/004_token_coefficients.sql | Adds SQLite token coefficient columns. |
| Cargo.toml | Enables sqlx SQLite feature. |
| Cargo.lock | Adds transitive deps needed for SQLite builds (e.g., cc). |
| Dockerfile | Copies SQLite migrations into the image build context. |
| .env.example | Documents SQLite single-instance configuration. |
| README.md | Documents single-instance mode and updates architecture/usage docs. |
Comments suppressed due to low confidence (1)
src/services/log_service.rs:145
where_clauseis reused for bothcount_queryanddata_query, but it hard-codes filter placeholders as$3/$4to account forLIMIT $1 OFFSET $2. That makescount_queryinvalid (it has no$1/$2binds), so filtered log listing will error at runtime. Consider building separate WHERE clauses (one indexed from$1for the count query and one from$3for the data query), or switch tosqlx::QueryBuilderto avoid manual placeholder indexing entirely.
// Build dynamic WHERE clauses
let mut conditions: Vec<String> = vec![];
if params.key_id.is_some() {
conditions.push("r.user_key_id = $3".to_string());
}
if params.model.is_some() {
let idx = if params.key_id.is_some() { 4 } else { 3 };
conditions.push(format!("r.model_requested = ${idx}"));
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let pool = sqlx::sqlite::SqlitePoolOptions::new() | ||
| .max_connections(5) | ||
| .connect(&config.database_url) | ||
| .await?; | ||
|
|
||
| // Enable WAL mode and foreign keys for better performance and correctness | ||
| sqlx::query("PRAGMA journal_mode = WAL") | ||
| .execute(&pool) | ||
| .await?; | ||
| sqlx::query("PRAGMA foreign_keys = ON") | ||
| .execute(&pool) | ||
| .await?; |
There was a problem hiding this comment.
SQLite PRAGMAs are executed once against the pool, but settings like PRAGMA foreign_keys = ON are per-connection in SQLite. With max_connections(5), some pooled connections may still have foreign keys disabled, leading to inconsistent referential integrity. Prefer configuring these via SqliteConnectOptions / SqlitePoolOptions::connect_with (or after_connect) so every new connection enables foreign_keys (and WAL/busy_timeout if needed).
Summary
Adds support for running the LLM Gateway as a single-instance deployment without PostgreSQL or Redis dependencies. When
DATABASE_URLstarts withsqlite:, the gateway automatically uses SQLite for persistence and an in-memory cache for key/route lookups.Changes
New files
src/db.rs—DbPoolenum wrapping eitherPgPoolorSqlitePool, withdb_query_as!,db_execute!, anddb_query_scalar!macros for database-agnostic query dispatchsrc/cache.rs—Cacheenum supporting both Redis (multi-instance) and in-memory (single-instance) key/route cachingmigrations_sqlite/— SQLite-compatible migration files (4 files mirroring the PostgreSQL migrations)Modified files
Cargo.toml— Addedsqlitefeature to sqlxsrc/config.rs— Maderedis_urloptional; auto-detect SQLite mode fromDATABASE_URLsrc/state.rs— UsesDbPool+Cacheinstead ofPgPool+ConnectionManagersrc/main.rs— Dual-mode initialization: PG+Redis or SQLite+InMemory based onDATABASE_URLsrc/services/*.rs— All services updated to use portable SQL and DbPool/Cache abstractionssrc/middleware/auth.rs— UsesCacheinstead of Redis directlysrc/routes/admin.rs— UsesCacheinstead of Redis directlysrc/routes/proxy.rs— UsesCacheinstead of Redis directlyDockerfile— Copiesmigrations_sqlite/into the build.env.example— Documents single-instance SQLite configurationREADME.md— Documents single-instance mode, updated architecture diagramUsage
Single-instance mode (no external dependencies)
Full deployment (PostgreSQL + Redis)
Design
DbPoolenum + macros dispatch queries to either PgPool or SqlitePool. SQL uses$1/$2parameter syntax which sqlx supports for both databases.Cacheenum wraps either RedisConnectionManageror an in-memoryHashMap-based store withRwLockfor thread safety.date_trunc(PG) vsstrftime(SQLite).NOW()replaced with bind parameters for portability.