This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
IMPORTANT: After completing any significant task, update this CLAUDE.md file to reflect the changes. This includes:
- New services, components, or APIs added
- Changed behaviors, logic, or algorithms
- New configuration options or environment variables
- Updated integration flows or data pipelines
- Bug fixes that change expected behavior
- New anti-patterns, rules, or validations added
Do NOT ask "should I update CLAUDE.md?" - just update it as part of task completion.
IMPORTANT: Do NOT commit changes automatically. Wait for the user to explicitly tell you to commit. This allows the user to:
-
Review all changes before committing
-
Batch multiple related changes into a single commit
-
Provide a custom commit message if needed
-
2026-08-20: Chat access policy enforcement is fail-closed.
UserDataAccessPolicyServicewalks the whole parse tree (TablesNamesFinderplus CTEs/UNIONs/subqueries), denies unparseable or unhandled SQL, requires an actor exceptINTERNAL/SCHEDULED, and exempts onlyCOUNT(*)/COUNT(1)withoutGROUP BY. MCP and Editor queries take the actor fromSecurityContext(MCP tokens included). Allowed schemas are persisted on the policy row. RAG, brain relationships, and vault-first metadata are schema-scoped. Public dashboard share is refused on connections with an active policy.COMMENT/CALL/REPLACEas mutating when they appear as table, column, or function names. The guard matches statement verbs: mutating CTEs,WITH … DELETE,FOR UPDATE, andEXPLAIN DELETE.SELECT * FROM commentis allowed. Dashboards and/api/mcp/query-readonlyshare this guard. -
2026-02-04: Moved all Markdown docs into
docs/(root docs underdocs/root/), added a rootREADME.mdstub, and updated doc links. -
2026-03-12: Added a Phase 1 DeepSQL MCP stdio server in
mcp/with read-only tools for connections, schema, chat, SQL execution, and EXPLAIN. Seedocs/root/MCP_PHASE1.md. -
2026-03-30: Main chat execution was tightened to stay schema-agnostic. Do not add customer-specific table names, column names, SQL templates, or prompt-to-table shortcuts in chat classifier, planner, resolver, composer, or execution paths. Fix chat behavior through generic semantic ranking, context retrieval, and guardrails instead.
-
2026-04-14: Added a quick local regression flow via
scripts/run-local-regression-suite.shandscripts/local-deploy.sh. The runner pairs frontend build + health probes with the backendApiSmokeTest. Frontend lint is opt-in viaLOCAL_REGRESSION_RUN_FRONTEND_LINT=1because the repo currently has broader pre-existing lint debt.
DBA Agent is an AI-powered Database Performance Assistant with autonomous troubleshooting capabilities. It combines RAG-powered SQL generation, real-time monitoring, and automated playbook execution to provide comprehensive database management.
Tech Stack:
- Backend: Spring Boot 4.0.3 (Java 25), Spring AI 2.0.0-M2, PostgreSQL vault DB, MySQL/PostgreSQL target DB support
- Frontend: React 19.2.3 with Vite 7.3.0, Tailwind CSS 4.1.18
- AI: bring-your-own LLM through
LlmProviderRegistry— OpenAI, Azure OpenAI, or any OpenAI-compatible server (vLLM, Ollama, LM Studio, TGI). Vector store: pgvector or Azure AI Search for RAG - Caching: Redis/Valkey (for RAG/application caching)
- Chat Memory: JDBC/PostgreSQL (Spring AI JdbcChatMemoryRepository)
Status: Implemented (Spring AI 2.0.0-M2)
The system supports cross-session learning through two mechanisms:
- JDBC Chat Memory - Conversation history persistence in PostgreSQL (via Spring AI)
- User Feedback Storage - Corrections, teachings, and ratings stored in PostgreSQL
Components:
spring-ai-starter-model-chat-memory-repository-jdbc- Spring AI JDBC chat memory starterSpringAIChatMemoryConfig- CreatesMessageWindowChatMemorybean (default 50 messages)ChatMemoryService- Service wrapper for conversation managementChatMemoryController- REST API for memory inspection/clearing
Endpoints:
GET /api/memory/status- Check if JDBC memory is availableGET /api/memory/connection/{connId}/conversations- List all chats for a connectionGET /api/memory/connection/{connId}/chat/{chatId}- Get conversation historyDELETE /api/memory/connection/{connId}/chat/{chatId}- Clear a conversationDELETE /api/memory/connection/{connId}- Clear all conversations for connection
Storage:
Chat memory is stored in Spring AI's JDBC tables (spring_ai_chat_memory) in the same PostgreSQL vault database. No additional infrastructure required.
Graceful Degradation: If JDBC repository is unavailable, ChatMemoryService.isAvailable() returns false and endpoints return empty responses with available: false.
Components:
FeedbackController(/api/feedback/*) - REST API for feedback submissionFeedbackService- Stores feedback in PostgreSQL, builds context for promptsChatFeedbackentity - Stores all feedback types with metadata
Feedback Types:
| Type | Purpose | Used in Prompts |
|---|---|---|
THUMBS_UP |
Positive rating | No (analytics only) |
THUMBS_DOWN |
Negative rating with optional comment | No (analytics only) |
CORRECTION |
"Use 'active' not 'enabled'" | Yes |
TEACHING |
"status values are: confirmed, pending" | Yes |
COLUMN_VALUES |
Auto-collected low-cardinality values | Yes |
Endpoints:
POST /api/feedback/thumbs-up- Record positive feedbackPOST /api/feedback/thumbs-down- Record negative feedback with reasonPOST /api/feedback/correction- Record a correction for table/columnPOST /api/feedback/teaching- Record a teaching about domain knowledgePOST /api/feedback/column-values- Record column value specificationsGET /api/feedback/connection/{connId}/stats- Get feedback statisticsGET /api/feedback/connection/{connId}- Get recent feedback
Integration with ChatService:
The FeedbackService.buildFeedbackContext(connectionId) method generates a prompt section with all learnable feedback (corrections, teachings, column values) which is injected into the system prompt before each chat request.
Components:
ColumnValueCollectionService- Collects distinct values for low-cardinality columns- Triggered automatically after Key Column Analysis completes (async via virtual threads)
- Background scheduler refreshes values every 7 days (cron:
0 0 3 * * *) - Rate-limited to prevent CPU spikes on large databases (batch processing with delays)
Azure AI Search Embedding: Values are embedded into Azure AI Search with document type COLUMN_VALUES for semantic RAG retrieval. This helps the chat agent answer questions like "What are valid statuses for orders?" and use exact values in SQL WHERE clauses.
Frontend UI: View and manage cached column values in Brain → Overview → Column Values panel.
See section 16 "Column Value Embedding for Better SQL Filtering" for full implementation details.
cd backend
# Build
mvn clean install
# Run in development mode (auth disabled)
mvn spring-boot:run
# Run in production mode (auth enabled)
mvn spring-boot:run -Dspring-boot.run.profiles=prod
# Run all tests
mvn test
# Run integration tests only
mvn test -Dtest="*IntegrationTest"
# Run specific test class
mvn test -Dtest=BrainControllerIntegrationTest
# Run specific test method
mvn test -Dtest=ConnectionControllerIntegrationTest#testListConnectionsBackend runs on: http://localhost:8080/api
# Install dependencies
npm install
# Run development server
npm run dev
# Lint code
npm run lint
# Run the quick local deploy regression suite
npm run test:local-regression
# Start local services, then run the quick regression suite
npm run local-deploy
# Build for development
npm run build
# Build for production
npm run build:production
# Preview production build
npm run preview
# Start the Phase 1 MCP server
npm run mcp:phase1Frontend runs on: http://localhost:3000
Development credentials: admin/admin (auth bypass enabled in dev mode)
# Start vault database (PostgreSQL)
docker compose up -d postgres
# Stop database
docker compose downVault DB defaults:
- URL: jdbc:postgresql://localhost:5432/dba_agent?sslmode=disable
- User: postgres
- Password: postgres
Layered Design:
Controller Layer → Service Layer → Repository Layer → Database
Key Service Patterns:
-
Dynamic Connection Management
ConnectionServicemanages HikariCP connection pools- Each database connection gets its own pool (ConcurrentHashMap)
- Supports PostgreSQL and MySQL with database-specific configurations
- Thread-safe connection retrieval with automatic pool creation
- SSH Tunnel Support: Connections can optionally use SSH tunneling for accessing databases behind firewalls/VPCs (see SSH Tunneling section below)
- Provider Registry Integration:
ConnectionServicedelegates toDatabaseProviderRegistryfor all database-specific operations (JDBC URL building, driver class, connection properties)
-
RAG (Retrieval-Augmented Generation) System
- Three-tier implementation: Azure AI Search → Redis → In-memory
TrainingServiceorchestrates DDL generation → embedding → storageEmbeddingServicedelegates to the embedding provider resolved fromLlmProviderRegistry+LlmConfigResolverOpenAiCompatibleEmbeddingProvidercalls the OpenAI Java SDK (com.openai:openai-java) and caches one client per credential bundleOpenAISdkConfigis deleted. There is no eagerly built client and no LLM credential is required to boot- Spring AI's
OpenAiEmbeddingAutoConfigurationis excluded inDbaAgentApplication;LlmConfig.embeddingModelsupplies a@PrimaryProviderBackedEmbeddingModelsoVectorStoreandQuestionAnswerAdvisorembed through the same provider
AzureSearchServicemanages persistent vector storage (index: dba-agent-training-data)BrainInsightEmbeddingServiceembeds Brain insights (workload, patterns, cardinality) into Azure SearchChatService.processMessage()retrieves top-K relevant examples for context
-
Playbook Execution Engine
- JSON-based step definitions with tools and parameters
PlaybookExecutionServiceorchestrates multi-step executionPlaybookToolsServiceexecutes diagnostic tools (table_growth_rate, index_fragmentation, etc.)PlaybookSchedulerServicehandles cron-based and manual triggers- Alert routing: browser/Slack/email with severity filtering
-
Chat Service Architecture (Spring AI 2.0)
SpringAIChatServiceusesChatClientwith advisor chain pattern- Advisors automatically inject context in order (memory → feedback → column values → schema → performance → RAG)
FeedbackLearningAdvisorretrieves user corrections/teachings from vector store for cross-session memoryColumnValueAdvisorprovides low-cardinality column values for filter hints- Scans database schema dynamically for context
- Critical Rule: All SQL queries MUST use table-qualified columns (table.column_name)
- Critical Rule: Chat-path logic must remain schema-agnostic. Never hardcode customer-specific table names, column names, SQL fragments, or prompt-to-table shortcuts in main chat flow. Resolve behavior through generic semantic ranking and schema-aware reasoning.
- Supports image analysis for screenshots
- Automatically executes SQL from markdown code blocks
-
MCP Server + CLI (
@deepsql/mcp)mcp/deepsql-phase1-server.jsexposes DeepSQL to MCP clients over stdio using JSON-RPC 2.0 (auto-detects newline-delimited and LSPContent-Lengthframing). ThedeepsqlCLI shim spawns this with the saved auth token from~/.config/deepsql/auth.json, so editor configs never need to embed a token.- Agentless surface: brain tools + policy-gated SQL execution + AI-enriched plan analysis. Coding agents (Claude Code, Cursor, Codex) use these to ground their own SQL/answer generation. There is no chat-pipeline
answer_questiontool — DeepSQL's value is the brain, not an embedded agent. - Tools:
list_connections,get_schema,get_database_objects— connection metadataget_brain_context— wraps/training/context/{cid}(or/training/retrieve/{cid}whentopKis set) for embedding-ranked retrievallist_business_rules— wraps/business-rules/connection/{cid}get_relationships— wraps/brain/inferred-relationships/{cid}get_anti_patterns— wraps/brain/table-anti-patterns/{cid}and/brain/query-anti-patterns/{cid}analyze_slow_queries— wraps/slow-queries/analyze/{cid}get_index_recommendations— wraps/index-recommendations/{cid}/top?limit=N; serves the workload-weighted, recurrence-ranked top-N index recommendations with evidence + optional HypoPG cost-deltaapply_index_recommendation— wraps/index-recommendations/{rec_id}/apply?mode=&confirm=; the only write tool in the MCP surface that takes a server-resolved recommendation id (DDL is server-generated, never client-supplied). DRY_RUN uses HypoPG (Postgres) to estimate cost-delta without writes. APPLY runsCREATE INDEX CONCURRENTLY/DROP INDEX CONCURRENTLY(configurable viaconcurrent=false). APPLY_AND_MEASURE also runsEXPLAIN ANALYZEfor wall-clock timings. APPLY modes requireconfirm=true.execute_sql— runs any SQL through the canonical Editor endpointPOST /connections/{cid}/query. Backend enforces role-based policy viaQueryExecutionPolicyService: developers get SELECT/WITH/SHOW/EXPLAIN; admins additionally get DML/DDL with a two-stepconfirmMutationflow plus a WHERE-clause guard forUPDATE/DELETE. EXPLAIN and EXPLAIN ANALYZE are just SQL — pass them as the query string.analyze_query_plan— wrapsPOST /explain/analyzefor AI-enriched plan analysis. Returns the parsed plan tree, performance issues, index recommendations, and an LLM-written summary that takes the connection's schema + business rules into account.useAnalyze=trueactually runs the query (EXPLAIN ANALYZEsemantics); for mutating statements the same admin/WHERE/confirm gates apply.
- Origin propagation: every call carries
X-DeepSQL-Client-Type(cli/mcp/editor),X-DeepSQL-Client-Agent(claude-code/cursor/codex/terminal/web — sourced fromDEEPSQL_MCP_USER_IDfor the MCP server,--caller-agent/DEEPSQL_CALLER_AGENTfor the CLI), andX-DeepSQL-Client-Versionheaders.SqlExecutionAuditServicewrites them into everysecurity_eventsrow so admins can trace which surface ran which statement. - Server-side query truncation: pg_stat_statements (
track_activity_query_size, default 1024B) and performance_schema (performance_schema_max_sql_text_length, default 1024B) silently truncate long queries. Provider code flags such rows withSlowQuery.sourceTruncated=true;SlowQueryService.recoverTruncatedQueriesFromLineage(...)then looks inquery_lineage(vault DB) for a previously-ingested log-file copy with the full text and setsqueryTextRecoveredFromLogs=trueon success. Chat / MCP summarizers report three states (clean / recovered / still-truncated). - Deprecated (one-cycle forwarding aliases, removed in 0.14.0):
execute_readonly_sqltool → forwards toexecute_sql;explain_readonly_sql→ forwards toanalyze_query_plan; same for the/api/mcp/query-readonlyand/api/mcp/explain-readonlyendpoints behind them.McpControllerlogs a deprecation warning on each call. - Editor install (one command per editor — writes the MCP entry AND a DBA-consult skill file in one shot):
deepsql mcp config --install --for claude-code→claude mcp add --scope user(falls back to~/.claude.json) +~/.claude/skills/deepsql/SKILL.mddeepsql mcp config --install --for claude-desktop→~/Library/.../Claude/claude_desktop_config.json(Desktop has no skills surface)deepsql mcp config --install --for cursor→~/.cursor/mcp.json+~/.cursor/rules/deepsql.mdc(with globs on**/*.sql,**/migrations/**,**/schema/**,**/models/**,**/*.prisma,**/entities/**)deepsql mcp config --install --for codex→~/.codex/config.toml+ guarded section in~/.codex/AGENTS.md
- Skill body lives in
mcp/skills/SKILL_BODY.md; rendered with per-editor frontmatter at install time.--no-skillopts out,--forceoverwrites a stale entry,--printshows snippets without touching disk. - Cursor's project-level dev config is in
.cursor/mcp.json. Customer-facing example configs live inmcp/claude_desktop_config.customer.example.jsonandmcp/codex_config.customer.example.toml(both updated to use thedeepsql mcpshim — token-embedded shape still works but is no longer the recommended path). - See
docs/root/MCP_PHASE1.mdfor full tool semantics;mcp/CLAUDE.mdships in the npm tarball with the runtime usage skill;mcp/AGENT-SETUP.mdis the paste-into-agent install playbook.
-
Security/Authentication
- JWT-based stateless authentication (10-hour expiry)
JwtUtilfor token generation/validationSecurityConfigwith CORS support for multiple domains- Auth can be disabled for development:
security.auth.enabled=false EncryptionServicefor credential encryption- Invite Code System for private beta signup (see section 17)
-
Database Provider Registry Pattern
- Extensible architecture for adding new database types without modifying core services
DatabaseProviderRegistryauto-discovers and registers allDatabaseDialectSpring beans- Supported Databases:
- PostgreSQL: aliases
postgres,postgresql,pg,aurora-postgresql,amazon-aurora-postgresql - MySQL: aliases
mysql,mariadb,aurora-mysql,amazon-aurora-mysql
- PostgreSQL: aliases
- Provider Interface Hierarchy:
DatabaseDialect (composite) ├── ConnectionProvider - JDBC URLs, driver class, connection properties, default ports ├── IntrospectionProvider - Schema/table/column metadata, indexes, constraints, foreign keys ├── SlowQueryProvider - Slow query collection (pg_stat_statements, performance_schema) ├── PerformanceMetricsProvider - Active queries, index stats, cache stats ├── LockProvider - Lock detection and analysis ├── ExplainPlanProvider - EXPLAIN plan parsing (JSON format) ├── ConfigurationProvider - Database configuration analysis └── QueryExecutionProvider - Query fingerprinting, normalization - Spring DI Integration: All 16 provider classes are
@Componentbeans with@RequiredArgsConstructorfor dependency injection - Alias Collision Detection: Registry logs warnings if multiple dialects register the same alias
- Canonical Name Resolution: Use
registry.getCanonicalName(dbType)to normalize aliases before any branching logic:String dbType = providerRegistry.getCanonicalName(connection.getDbType()); // "postgresql" → "postgres", "mariadb" → "mysql", "aurora-mysql" → "mysql"
- Usage in Services: Services call
registry.getDialect(dbType)then access specific providers:// Connection operations DatabaseDialect dialect = registry.getDialect("postgres"); String jdbcUrl = dialect.connection().buildJdbcUrl(request, tunnelPort); int port = dialect.connection().getDefaultPort(); // Schema introspection IntrospectionProvider introspection = dialect.introspection(); List<ColumnDetail> columns = introspection.getColumnDetails(conn, database, tableName); List<IndexDetail> indexes = introspection.getIndexDetails(conn, database, tableName); Map<String, List<String>> relationships = introspection.getTableRelationships(conn, database); // Slow query collection List<SlowQuery> queries = dialect.slowQueries().collectSlowQueries(conn, threshold, limit);
- Refactored Services (now use providers instead of hardcoded if/else):
ConnectionService- Delegates JDBC URL building, driver class, connection properties to ConnectionProviderSshTunnelService- Uses ConnectionProvider for default port lookupSlowQueryService- UsesgetCanonicalName()for database type normalizationSlowQueryCollectorService- UsesConnectionService.getJdbcTemplate()for SSH/alias supportSlowQueryLogParserService- UsesgetCanonicalName()for parser selection (MySQL vs PostgreSQL)SchemaIntrospectionService- Fully delegates to IntrospectionProvider (reduced from 537 → 95 lines)PerformanceMonitoringService- UsesgetCanonicalName()in all 8 monitoring methodsPlaybookToolsService- UsesgetCanonicalName()for all 10 diagnostic tool methodsIndexAdvisorService- UsesgetCanonicalName()for index health analysisExplainPlanService- UsesgetCanonicalName()for EXPLAIN plan parsingDatabaseAdvisorService- UsesgetCanonicalName()for performance analysisDatabaseConfigurationService- UsesgetCanonicalName()for configuration tuning recommendationsIndexRecommendationService- UsesgetCanonicalName()for recommendation generationQueryAnalysisService- UsesgetCanonicalName()for query validationQueryOptimizationService- UsesgetCanonicalName()for AI-powered optimizationPerformanceInsightsService- UsesgetCanonicalName()for metrics collectionQueryExecutorService- UsesgetCanonicalName()for database type normalizationQueryPlanCacheService- Uses canonical name comparisons for plan cachingStatsCollectorService- UsesgetCanonicalName()for DBA stats collectionTableHealthScoreService- UsesgetCanonicalName()for health score calculationDataLifecycleClassificationService- UsesgetCanonicalName()for lifecycle classificationDependencyCriticalityService- UsesgetCanonicalName()for dependency analysis
- Adding New Database Support (e.g., Oracle):
- Create package
com.dbaagent.provider.oracle - Implement all 8 provider interfaces as
@Componentbeans - Create
OracleDialectimplementingDatabaseDialectwith@Component - Add JDBC driver dependency to
pom.xml - Registry auto-discovers the new dialect on startup
- Create package
- Anti-Pattern: Do NOT add new
if/elseorswitchstatements for database types in services. Always delegate to providers.
Key Service Categories:
- Core: SpringAIChatService (primary), ChatService (legacy), ConnectionService, SshTunnelService, QueryExecutorService, DatabaseProviderRegistry
- Database Providers: PostgresDialect, MySQLDialect (each with 8 provider implementations: Connection, Introspection, SlowQuery, PerformanceMetrics, Lock, ExplainPlan, Configuration, QueryExecution)
- Spring AI Advisors: FeedbackLearningAdvisor, ColumnValueAdvisor, SchemaContextAdvisor, PerformanceInsightsAdvisor
- Feedback/Learning: FeedbackService
- RAG/Training: TrainingService, EmbeddingService, AzureSearchService, BrainInsightEmbeddingService
- Playbooks: PlaybookService, PlaybookExecutionService, PlaybookSchedulerService, PlaybookToolsService
- Monitoring: GrowthAnomalyDetectionService, PerformanceInsightsService, StatsCollectorService
- Database Analysis: SchemaIntrospectionService
- Brain Package (
com.dbaagent.service.brain.*):- brain/core: BrainService, BrainScoreService, BrainTaskService, BrainNoteService
- brain/workload: WorkloadMetricsCollectorService, WorkloadCharacterizationService
- brain/config: KnobIdentificationService, ConfigTuningService
- brain/query: CardinalityEstimationService, AdaptivePlanScoringService, PlanPatternLibraryService
- brain/keycolumn: KeyColumnAnalysisService, ColumnValueCollectionService, JoinRelationshipInferenceService
- brain/classification: SchemaClassificationService, AccessPatternClassificationService, AntiPatternDetectionService, TemporalClassificationService, TableHealthScoreService, BusinessDomainClassificationService, DataSensitivityClassificationService, PartitionReadinessService, RelationshipClassificationService, DataLifecycleClassificationService, CacheAffinityClassificationService, QueryComplexityClassificationService, SchemaEvolutionRiskService, DenormalizationCandidateService, CostAttributionService, ShardingReadinessService, DataQualityScoreService, DependencyCriticalityService, GrowthPredictionService
- brain/analysis: ColumnProfilingService, ColumnDisambiguationService, QueryQualityAnalysisService, ScalabilitySimulationService
- brain (root): BrainLearningScheduler (orchestrates background learning)
- Query Analysis: SlowQueryService, QueryPerformanceService, ExplainPlanService, QueryPlanCacheService
- Performance Monitoring: PerformanceMonitoringService (slow queries, locks, cache stats, index usage)
- Schema Change Tracking: SchemaChangeTrackingService (snapshots, drift detection, change history)
- Index Advisor: IndexAdvisorService, IndexRecommendationService (health reports, cost-benefit analysis)
- Slow Query Ingestion: SlowLogIngestionService, IngestionJobService, SlowLogIngestionScheduler, CloudWatchLogFetchService, S3LogFetchService, AzureBlobLogFetchService, GcpCloudLoggingFetchService, DatadogLogFetchService, ElasticsearchLogFetchService
- Query Optimization: QueryOptimizationService, QueryFingerprintService, SlowQueryAlertService, SlowQueryDashboardService, QueryOptimizationCacheRepository
- Performance Actions: PerformanceActionAggregatorService, PerformanceActionRepository (ROI-based unified recommendations)
LLM Usage by Service (bring-your-own provider):
There is no per-service deployment property any more. Every service below resolves through
the same two roles — chat and embedding — configured once via
DEEPSQL_CHAT_* / DEEPSQL_EMBEDDING_* (see LLM Providers below). The model is
whatever you set in DEEPSQL_CHAT_MODEL / DEEPSQL_EMBEDDING_MODEL.
| Service | Role | Purpose |
|---|---|---|
ChatService |
chat | Conversational SQL generation, schema analysis |
QueryOptimizationService |
chat | AI-powered query optimization suggestions |
SlowQueryService |
chat | Slow query analysis summaries |
ExplainPlanService |
chat | EXPLAIN plan interpretation |
DatabaseAdvisorService |
chat | Database performance recommendations |
EmbeddingService |
embedding | RAG vector embeddings |
BrainInsightEmbeddingService |
embedding (via EmbeddingService) |
Brain insight embedding (workload, patterns, cardinality) |
Spring AI Integration:
ChatServiceuses Spring AIChatClient, backed byRefreshableChatModel, which asksLlmConfigResolver+LlmProviderRegistryfor aChatModelper call. Credentials are therefore re-resolved on every call and key rotation needs no restart.- The old
spring.ai.openai.*block that pinned the base URL to an Azure deployment path is gone, along withAzureOpenAIConfig,OpenAISdkConfigandResponsesApiConfig. Spring AI's OpenAI chat/embedding/image/audio/moderation auto-configurations are excluded inDbaAgentApplicationso they cannot build a second, independently configured model. Do not reintroduce them.
LLM Providers (com.dbaagent.llm)
LlmProviderRegistry auto-discovers LlmChatProvider / LlmEmbeddingProvider beans,
exactly like DatabaseProviderRegistry. Chat and embedding are indexed in separate
maps — Anthropic publishes no embeddings API, so a single index would force an
if/else-on-provider-type. Ids and aliases are case-insensitive; a collision throws at
startup. Startup logs:
Registered 1 LLM chat providers [openai] and 1 embedding providers [openai]
| Type | File | Notes |
|---|---|---|
| Chat | llm/openai/OpenAiCompatibleChatProvider.java |
id openai; aliases azure, azure-openai, openai-compatible, self-hosted |
| Chat transport | llm/openai/ResponsesApiChatModel.java |
Responses API or chat completions, picked per model |
| Embedding | llm/openai/OpenAiCompatibleEmbeddingProvider.java |
id openai; aliases azure, azure-openai |
| Spring bridge | llm/spring/ProviderBackedEmbeddingModel.java |
@Primary EmbeddingModel for VectorStore / QuestionAnswerAdvisor |
| Resolution | llm/LlmConfigResolver.java |
DB tier, then environment tier |
One provider covers OpenAI, Azure OpenAI and self-hosted vLLM/Ollama/LM Studio/TGI because
it dispatches on endpoint shape, not on a provider id: an .azure.com /
.azure-api.net base URL selects Azure's api-key header, everything else uses
Authorization: Bearer.
Configuration resolution — two tiers, no property-default tier. A credential default
in a properties file is how the production Azure key reached git history;
SelfHostPropertiesSafetyTest now guards against it.
- Database —
llm.<role>.provider, thenllm.<role>.<providerId>.<field>insystem_config, where<role>ischatorembedding. Namespaced per provider because model ids are not portable between providers, and so that evaluating a provider and reverting does not discard the original credentials. - Environment —
DEEPSQL_{CHAT,EMBEDDING}_{PROVIDER,API_KEY,ENDPOINT,MODEL,API_VERSION,USE_RESPONSES_API,TEMPERATURE}.PROVIDERgates the bundle: unset, nothing else in it is read.
The DB tier is written by the onboarding wizard.
SetupController'sPOST /setup/llm-configwritesllm.chat.provider/llm.chat.<providerId>.<field>and the matchingllm.embedding.*twins from the one credential it collects (an operator filling that form expects RAG to work too).GET /setup/statusderiveshasLlmConfigfromLlmConfigResolver.resolveChat() != null.Until this was fixed the wizard wrote a flat pre-BYO namespace (
llm.provider,llm.openai.api-key,llm.openai.endpoint,llm.chat-model,llm.embedding-model) that intersected nothingLlmConfigResolverreads: keys were stored,{"success": true}was returned, and nothing changed — whilehasLlmConfig, read fromllm.openai.api-key, stayedfalseon every correctly env-configured install.V109__drop_legacy_llm_config.sqlremoves those orphaned rows — apply it by hand, since nothing executesdb/migration/(see the changelog note below).
Chat resolution is per call. RefreshableChatModel asks the resolver for credentials
on every call and rebuilds the delegate only when they change, so key rotation needs no
restart. getDefaultOptions() deliberately swallows LlmNotConfiguredException and
UnsupportedLlmProviderException and returns neutral options — an operator who cannot
start the app cannot fix the setting that stopped it. call() / stream() still raise.
/api/llm/v1 resolves like everything else. LlmProxyController — the OpenAI-shaped
gateway the DeepSQL CLI agent points at — calls LlmConfigResolver.resolveChat() and picks
its auth header with OpenAiEndpoints.isAzure(endpoint), so the chat bundle configures it
too. Unresolvable credentials give a 503 naming DEEPSQL_CHAT_*, never a 200 with an error
body. It previously bound azure.openai.endpoint / azure.openai.key via @Value, which
401'd for every OpenAI-key self-hoster; azure.openai.* is now read by no code at all,
though the properties themselves still sit in application*.properties.
-
Slow Query Log Ingestion System
- Supports multiple log source providers:
- AWS S3: Fetch slow query logs from S3 buckets
- AWS CloudWatch Logs: Pull logs from CloudWatch log groups
- Azure Blob Storage: Download logs from Azure storage containers
- GCP Cloud Logging: Query Cloud SQL slow logs via Stackdriver
- Datadog APM: Fetch database query logs from Datadog Log Search API
- Elasticsearch/ELK: Search slow query logs indexed in Elasticsearch
SlowLogIngestionServiceorchestrates fetching, parsing, and storing slow query logsIngestionJobServicehandles async ingestion with progress tracking and cancellationSlowLogIngestionSchedulerruns per-connection auto-scheduled ingestionSlowLogSourceConfigstores per-connection settings (provider, credentials, schedule)- Integrates with
QueryPerformanceServicefor regression detection - All credentials are encrypted at rest using
EncryptionService
- Supports multiple log source providers:
-
Query Performance & Regression Detection
QueryPerformanceServicetracks query execution times over time- Detects performance regressions by comparing current vs baseline execution times
QueryPerformanceRegressionstores detected regressions with severity (CRITICAL, SEVERE, MODERATE, MINOR)- Supports acknowledgment and resolution workflow for regressions
- Feeds into Brain Score calculation (regressions penalize Query Quality Score)
-
AI-Powered Query Optimization System
QueryOptimizationServicegenerates AI-powered optimization suggestions using Azure OpenAI- Analyzes slow queries and provides: query rewrites, index recommendations, schema suggestions
- Includes EXPLAIN plan analysis for SELECT queries
- Batch optimization for top N slow queries
- Estimated performance improvement percentages
-
Query Fingerprint Tracking
QueryFingerprintServicetracks normalized query patterns over timeQueryFingerprintentity stores: fingerprint hash, baseline/current metrics, performance history- Trend analysis: IMPROVING, STABLE, DEGRADING, CRITICAL
- Regression detection at fingerprint level
- Historical performance data points for trend visualization
-
Slow Query Alerts & Notifications
SlowQueryAlertServiceintegrates with PlaybookAlert system- Configurable thresholds for critical/high severity queries
- Creates alerts for: individual critical queries, summary alerts, health degradation
- Supports acknowledgment workflow
- Alert summary with counts by severity
- Multi-channel notifications: Email (
EmailService.sendSlowQueryAlert()) and Slack/Webhook (WebhookService.sendSlowQueryAlert()) - Slack Block Kit formatting for rich alert messages
- Alert throttling and channel configuration per connection
-
Slow Query Dashboard Widgets
SlowQueryDashboardServiceprovides aggregated widget data- Widgets: Overview, Trend, Top Offenders, Regressions, Health
- Time series data for trend visualization
- Top queries by: execution time, frequency, impact score
- Health score history with distribution
-
Enhanced Schema Classification System
- Comprehensive table and schema analysis with 8 specialized classification services
- Prerequisites (Order of Operations): Schema Classification depends on Key Column Analysis data. If you see 0 tables:
- First, ingest slow query logs (Brain → Slow Query Source)
- Run Key Column Analysis (Brain → Key Columns → Analyze)
- Then run Schema Classification (Brain → Schema Overview → Analyze Schema)
- Table Role Classification (in
SchemaClassificationService.classifyTables()):- FACT: Tables with many outbound FKs, high row counts, or transaction-like names (payment, order, booking)
- DIMENSION: Tables with many inbound joins, few outbound FKs, smaller row counts
- BRIDGE: Tables connecting other tables (both inbound and outbound joins)
- LOOKUP: Small reference tables (<1000 rows) with inbound joins
- EVENT_LOG: Log/audit/event tables detected by naming patterns (*_log, *_audit, *_history, *_event, *_trail)
- ORPHANED: Tables with no detected relationships
- Access Pattern Classification (
AccessPatternClassificationService):- Analyzes pg_stat_user_tables for read/write patterns
- Types: READ_HEAVY, WRITE_HEAVY, APPEND_ONLY, UPDATE_INTENSIVE, MIXED_WORKLOAD, RARELY_ACCESSED
- Returns read/write ratios and operation counts
- Anti-Pattern Detection (
AntiPatternDetectionService):- Detects 10 schema anti-patterns: GOD_TABLE, WIDE_TABLE, SPARSE_TABLE, POLYMORPHIC_ASSOCIATION, EAV_PATTERN, MISSING_TIMESTAMP, OVER_INDEXED, UNDER_INDEXED, DUPLICATE_INDEXES, UNUSED_COLUMNS
- Severity levels: CRITICAL, HIGH, MEDIUM, LOW, NONE
- Generates specific remediation recommendations
- Temporal Classification (
TemporalClassificationService):- Identifies temporal data patterns using column name analysis
- Types: TIME_SERIES, SCD_TYPE_1, SCD_TYPE_2, AUDIT_LOG, EVENT_SOURCING, SNAPSHOT, NONE
- Suggests appropriate retention and archival strategies
- Table Health Score (
TableHealthScoreService):- Calculates composite health score (0-100) from 5 components (20% each):
- Index Efficiency, Data Quality, Schema Design, Access Efficiency, Maintenance Health
- Provides detailed breakdown with per-component scores
- Identifies tables needing attention
- Calculates composite health score (0-100) from 5 components (20% each):
- Business Domain Classification (
BusinessDomainClassificationService):- Classifies tables into business domains using name/column pattern matching
- Domains: CUSTOMER, PRODUCT, TRANSACTION, LOCATION, TEMPORAL, CONFIGURATION, SECURITY, COMMUNICATION, ANALYTICS, CONTENT, UNKNOWN
- Confidence scores for classification accuracy
- Data Sensitivity Classification (
DataSensitivityClassificationService):- Detects sensitive columns using regex patterns (PII, financial, health data)
- Sensitivity levels: PII_HIGH, PII_MEDIUM, FINANCIAL, HEALTH, REGULATED, PUBLIC
- Compliance flag detection: GDPR, CCPA, PCI-DSS, SOX, HIPAA, HITECH, SOC2
- Lists specific sensitive columns found
- Partition Readiness (
PartitionReadinessService):- Evaluates tables for partitioning suitability
- Types: PARTITION_CANDIDATE_TIME, PARTITION_CANDIDATE_RANGE, PARTITION_CANDIDATE_LIST, ALREADY_PARTITIONED, NOT_PARTITION_CANDIDATE
- Identifies partition key candidates with estimated benefit (0-100%)
- Row count and size-based recommendations
- Relationship Classification (
RelationshipClassificationService):- Combines FK constraints, naming patterns, and query analysis
- Strength levels: STRONG (FK exists), INFERRED (naming pattern), WEAK (join pattern only)
- Relationship types: ONE_TO_ONE, ONE_TO_MANY, MANY_TO_ONE, MANY_TO_MANY
- Data integrity metrics: orphan counts, referential integrity percentage
- Index coverage analysis for join performance
-
Key Column Analysis System
KeyColumnAnalysisServiceanalyzes query patterns to identify important columns- Tracks column usage across: JOINs, WHERE clauses, GROUP BY, ORDER BY operations
- Calculates importance scores (0-100) based on weighted usage counts
- Case Normalization: All table and column names are normalized to lowercase to prevent duplicates (e.g., "Users" vs "users")
- Usage Metrics (two columns in UI):
- Query Runs (
slowQueryUsage): Total weighted usage count from slow queries - Unique Qry (
distinctQueriesCount): Number of distinct queries referencing the column
- Query Runs (
- Rate Limiting (prevents CPU spikes on large databases):
brain.key-columns.max-queries-per-source=1000- Max queries processed from each sourcebrain.key-columns.batch-size=100- Process queries in batchesbrain.key-columns.delay-between-batches-ms=50- Pause between batches- Progress logging every 100 queries: "Processed 100/500 slow queries..."
- Anti-Pattern Detection with index-awareness:
- UNINDEXED_FILTER: Columns used in WHERE ≥5 times without an index
- UNINDEXED_JOIN: Columns used in JOINs ≥5 times without an index
- UNINDEXED_ORDERBY: Columns used in ORDER BY ≥3 times without an index
- Important: All index recommendations skip columns that already have an index (
indexName != null)
- Additional anti-patterns: HEAVY_SKEW_JOIN, HEAVY_SKEW_GROUPBY (data distribution issues)
- Composite index detection for frequently co-occurring columns
- Index stats enrichment from
pg_stat_user_indexes - Supports Brain Rules for custom column behaviors (NO_INDEX_RECOMMENDATION, etc.)
- Column Value Collection: Automatically triggers
ColumnValueCollectionServiceafter analysis completes (see section 16)
-
Advanced Classification Services (Phase 6)
- 10 additional classification services for comprehensive table analysis
- Data Lifecycle Classification (
DataLifecycleClassificationService):- Classifies tables as HOT, WARM, COLD, or FROZEN based on access patterns
- Analyzes last access timestamps and access frequency per day
- Thresholds: HOT (7 days, >100 ops/day), WARM (30 days), COLD (90 days), FROZEN (90+ days)
- Recommendations for storage tier optimization and archival strategies
- Cache Affinity Classification (
CacheAffinityClassificationService):- Identifies tables suitable for caching based on read/write ratios and size
- Levels: HIGH_CACHE_VALUE, MEDIUM_CACHE_VALUE, LOW_CACHE_VALUE, NO_CACHE_BENEFIT
- Factors: read frequency (35%), write volatility (25%), size (25%), update frequency (15%)
- Provides estimated cache hit rate predictions
- Query Complexity Classification (
QueryComplexityClassificationService):- Analyzes slow query history to classify query patterns per table
- Types: SIMPLE_LOOKUP, RANGE_SCAN, COMPLEX_JOIN, AGGREGATION_HEAVY, FULL_SCAN, MIXED
- Uses regex patterns to detect JOINs, aggregations, subqueries
- Provides complexity scores and pattern breakdown percentages
- Schema Evolution Risk (
SchemaEvolutionRiskService):- Assesses risk of modifying tables based on multiple factors
- Risk levels: CRITICAL, HIGH, MEDIUM, LOW, MINIMAL
- Factors: table size (40%), dependencies (30%), index count (20%), column count (10%)
- Provides DDL impact estimates and migration strategy recommendations
- Denormalization Candidates (
DenormalizationCandidateService):- Identifies tables that could benefit from denormalization
- Analyzes join frequency patterns from InferredTableRelationship data
- Criteria: high join frequency (>100), small lookup tables (<10K rows)
- Recommends specific strategies: redundant columns or materialized views
- Cost Attribution (
CostAttributionService):- Calculates estimated storage and compute costs per table
- Cost tiers: HIGH_COST, MEDIUM_COST, LOW_COST, MINIMAL_COST
- Breaks down costs: storage, IOPS, compute (based on AWS RDS pricing)
- Provides cost per row and cost as percentage of total database
- Sharding Readiness (
ShardingReadinessService):- Evaluates tables for horizontal sharding suitability
- Readiness levels: READY, NEEDS_REFACTORING, COMPLEX, NOT_RECOMMENDED, NOT_NEEDED
- Identifies shard key candidates (tenant_id, user_id, region, date columns)
- Analyzes FK constraints that would complicate cross-shard queries
- Recommends optimal shard count based on table size
- Data Quality Score (
DataQualityScoreService):- Calculates comprehensive data quality scores (0-100)
- Quality levels: EXCELLENT (80+), GOOD (60-80), FAIR (40-60), POOR (<40)
- Dimensions: completeness (25%), validity (25%), uniqueness (20%), consistency (15%), timeliness (15%)
- Analyzes null ratios, constraint coverage, and FK relationships
- Dependency Criticality (
DependencyCriticalityService):- Assesses the criticality of tables based on dependency graph
- Criticality levels: CRITICAL, HIGH, MEDIUM, LOW, ISOLATED
- Calculates cascade impact (transitive closure of dependents)
- Provides dependency depth analysis and change impact areas
- Growth Prediction (
GrowthPredictionService):- Predicts table growth using historical TableGrowthHistory data
- Growth categories: EXPLOSIVE (>50%/mo), RAPID (20-50%), STEADY (5-20%), SLOW (1-5%), STABLE (<1%), SHRINKING
- Predictions for 30/90/365 days out
- Alerts for approaching size thresholds (10GB warning, 100GB critical)
- Confidence scores based on historical data availability
-
SSH Tunneling for VPC/Firewall Access
SshTunnelServicemanages SSH tunnels for database connections behind firewalls/VPCs- Enables connecting to production databases that are not directly accessible
- Recommended Access Pattern: Use
ConnectionService.getJdbcTemplate(connectionId, request)for all database operations:// This works seamlessly for BOTH SSH-tunneled AND direct connections JdbcTemplate jdbc = connectionService.getJdbcTemplate(connectionId, request); jdbc.queryForList("SELECT * FROM table");
- Authentication Types:
PASSWORD: Username/password authenticationPRIVATE_KEY: SSH private key authentication (PEM format), with optional passphrase
- Tunnel Lifecycle:
- Tunnels are established when creating connection pools
- Automatic port forwarding from localhost to remote database host
- Tunnels are closed when connection pools are destroyed
- Thread-safe tunnel management with ConcurrentHashMap
- Connection Flow with SSH:
Client Request → SSH Tunnel (localhost:localPort → remoteHost:dbPort) → Database - Configuration Fields:
sshEnabled: Boolean to enable/disable SSH tunnelingsshAuthType: PASSWORD or PRIVATE_KEYsshHost: Bastion/jump host addresssshPort: SSH port (default 22)sshUsername: SSH usernamesshPassword: Password for PASSWORD authsshPrivateKey: PEM-formatted private key for PRIVATE_KEY authsshPassphrase: Passphrase for encrypted private keys
- Security:
- All SSH credentials are encrypted at rest using
EncryptionService(AES-GCM) - SSL is automatically disabled for tunneled connections (SSH provides encryption)
- Strict host key checking disabled by default (can be made configurable)
- All SSH credentials are encrypted at rest using
- JSch Library: Uses
com.github.mwiede:jsch:2.27.7for SSH operations - Frontend UI: SSH tunnel configuration section in DBConfigModal with collapsible panel
-
Column Value Embedding for Better SQL Filtering
ColumnValueCollectionServicecollects and caches distinct values for low-cardinality columns- Integration with Key Column Analysis:
- Automatically triggered after Key Column Analysis completes (async)
- Analyzes columns with
distinctCount < 100(configurable viaspring.ai.column-values.low-cardinality-threshold)
- Azure AI Search Integration:
- Document type:
COLUMN_VALUES - Document ID format:
{connectionId}::COLUMN_VALUES::{tableName}::{columnName} - Semantic embedding text includes table, column, data type, valid values, and example filters
- Enables RAG retrieval when users ask about filtering or column values
- Document type:
- Chat Context Enhancement (
ChatService.buildKeyColumnContext()):- Key columns now display actual values for low-cardinality columns
- Example output:
• orders.status (importance: 92) | 5 distinct | HIGH selectivity VALUES: 'pending', 'confirmed', 'shipped', 'delivered', 'cancelled'
- REST API Endpoints (in
BrainController):GET /api/brain/column-values/{connectionId}- List cached column valuesGET /api/brain/column-values/{connectionId}/stats- Get statisticsPOST /api/brain/column-values/{connectionId}/refresh- Trigger manual refreshPOST /api/brain/column-values/embed-all- Embed all unembedded values
- Configuration Properties:
spring.ai.column-values.low-cardinality-threshold=100- Max distinct values to consider "low cardinality"spring.ai.column-values.sample-size=20- Number of sample values to storespring.ai.column-values.background-sampling.enabled=true- Enable daily refreshspring.ai.column-values.background-sampling.cron=0 0 3 * * *- Refresh schedule
- Rate Limiting (prevents CPU spikes on large databases):
spring.ai.column-values.batch-size=50- Max columns processed per runspring.ai.column-values.delay-between-columns-ms=100- Pause between DB queriesspring.ai.column-values.delay-between-embeddings-ms=500- Pause between Azure OpenAI calls
- Database Storage: Values cached in
column_value_cachetable with embedded flag for Azure Search sync - Frontend UI (
ColumnValuesPanel.js):- Located in Brain tab → Overview section (below 3D ER diagram and Key Columns)
- Shows all cached column values grouped by table
- Displays embedding status (Embedded/Pending) for each column
- Stats summary: Total Cached, Low Cardinality, Embedded in AI Search
- Actions: Refresh Values, Embed All, Reload
- Values displayed as clickable tags with overflow handling
- Explains how column values improve SQL generation
-
Cloud Provider Context for Feature Gating
- Connections can optionally specify cloud provider and managed service type
- Enables feature gating based on deployment environment (e.g., CloudWatch log ingestion only for AWS)
- Fields (stored in
encrypted_credentialstable):cloudProvider:aws,azure,gcp,self-hosted, ornullmanagedService: Service-specific type (see table below)
- Supported Configurations:
Cloud Provider Managed Service Options awsrds,aurora,ec2azureazure-flexible,azure-single,azure-vmgcpcloud-sql,alloydb,gceself-hosted(none) - Backend Implementation:
DatabaseConnection.cloudProviderandDatabaseConnection.managedServiceentity fieldsConnectionRequestDTO includes both fieldsCredentialServicesaves/loads fields during connection create/update/read- Fields are plain text (not encrypted) - they're metadata, not secrets
- Frontend UI (
DBConfigModal.js):- "Deployment (Optional)" dropdown after SSL toggle
- Conditional service type selector based on cloud provider choice
- Values reset when cloud provider changes
- Use Cases:
- Show CloudWatch log source option only for
cloudProvider: 'aws' - Show Azure Monitor integration only for
cloudProvider: 'azure' - Enable RDS Performance Insights features for
managedService: 'rds'or'aurora' - Adjust default SSL settings based on managed service type
- Show CloudWatch log source option only for
- Migration:
V40__add_cloud_provider_fields.sqladds columns with indexes
-
Invite Code System for Private Beta
- Enables controlled user registration during private beta
InviteCodeServicegenerates and manages invite codesInviteCodeControllerprovides REST API for code management- Code Format: 8-character uppercase alphanumeric (excludes confusing chars: 0, O, 1, I, L)
- Example Code:
BETA2K7X,MZGE685Q - Shareable Link:
https://yourapp.com/signup?code=BETA2K7X - Features:
- Configurable max uses per code (1 to unlimited)
- Optional expiration dates
- Usage tracking (current uses, remaining uses)
- Code deactivation for immediate invalidation
- Admin notes for tracking code purpose
- REST API Endpoints:
POST /api/invite-codes- Generate new code (admin, requires auth)GET /api/invite-codes- List all codes (admin, requires auth)GET /api/invite-codes/valid- List valid codes only (admin)DELETE /api/invite-codes/{id}- Deactivate code (admin)GET /api/invite-codes/validate/{code}- Validate code (public, no auth)
- Signup Flow:
- User visits
/signup?code=BETA2K7X(code pre-filled from URL) - Frontend validates code in real-time via public API
- On submit, backend validates code and creates user
- Code usage count incremented after successful registration
- User linked to invite code via
invite_code_idforeign key
- User visits
- Configuration:
security.signup.enabled=true- Enable/disable signup entirelysecurity.signup.invite-only=true- Require invite code for signup
- Frontend Components:
Signup.jsx- Registration page with invite code fieldauthAPI.validateInviteCode(code)- Public validationinviteCodeAPI- Admin operations (generate, list, deactivate)
-
Role-Based Access Control (RBAC)
- Expanded role system with configurable access levels
- Roles:
Role Description VIEWER Read-only access. Can view dashboards, browse schema, view slow queries, but cannot execute queries or modify anything. EDITOR Standard user. Can execute queries, use chat, run analysis, but cannot manage users or system settings. ADMIN Full access. Can manage users, roles, connections, and all system settings. - Permissions:
- VIEW_DASHBOARD, VIEW_SCHEMA, VIEW_SLOW_QUERIES, VIEW_BRAIN (all roles)
- EXECUTE_QUERIES, USE_CHAT, RUN_ANALYSIS, EXECUTE_PLAYBOOKS, USE_INDEX_ADVISOR (EDITOR+)
- MANAGE_CONNECTIONS, MANAGE_USERS, MANAGE_INVITE_CODES, MANAGE_SETTINGS (ADMIN only)
- Backend Components:
Role.java- Enum with VIEWER, EDITOR, ADMIN and permission mappingsPermission.java- Enum of all system permissionsUserManagementService- User listing, role updates, deletionAdminController- REST API for admin operations (/api/admin/*)
- Security Configuration:
@EnableMethodSecurityfor@PreAuthorizeannotations/api/admin/**endpoints require ADMIN role- JWT tokens include
roleandpermissionsclaims
- REST API Endpoints:
GET /api/admin/users- List all users (ADMIN only)GET /api/admin/users/{id}- Get user details (ADMIN only)PUT /api/admin/users/{id}/role- Update user role (ADMIN only)DELETE /api/admin/users/{id}- Delete user (ADMIN only)GET /api/admin/roles- Get all roles with permissions (ADMIN only)GET /api/admin/impersonate- List switchable users and current profile-switch status (ADMIN only)POST /api/admin/impersonate-{ userId }start viewing the product as that user (ADMIN only; cannot target admins or self). Setsimpersonate_usercookie and restamps the access JWT withimpUidso policy (and Agent Bearer fallback) run as the target while the JWT subject stays the admin.DELETE /api/admin/impersonate- Stop profile switch and restore the admin sessionGET /api/auth/me- Get current user's profile including role/permissions; while switching, this is the target user plusimpersonating/impersonatorUsername. Also setsX-Remote-Userto the effective username for nginx/agent-apiauth_request.
- Frontend Components:
PermissionGuard.jsx- Wrapper component for permission-based renderingUsersTab.jsx- Admin user management tab in WorkspaceuseAuth.jsx- Updated withhasPermission(),hasRole(),isAdmin,canExecute, etc.adminAPIinclient.js- API methods for user management
- localStorage Additions:
userRole: User's role (VIEWER, EDITOR, ADMIN)userPermissions: JSON array of permission strings
- Default Behavior:
- New signups default to EDITOR role
- Admin user (username: 'admin') cannot have role changed or be deleted
-
Brain 2.0: ML-Based Database Optimization
- ML-based database tuning inspired by CMU OtterTune and optd research projects
- Key Concepts:
- Workload Characterization: Classifies workloads using statistical analysis (OLTP, OLAP, MIXED, WRITE_HEAVY, READ_HEAVY, BATCH)
- Knob Identification: Ranks configuration parameters by impact using Lasso-inspired approach
- Adaptive Query Optimization: Learns from actual execution to calibrate cost models
- Plan Pattern Memoization: Caches query plan patterns with optimization suggestions
- Knowledge Transfer: Leverages learnings across similar workloads
- Service Architecture (consolidated into
com.dbaagent.service.brain.*):Service Package Purpose WorkloadMetricsCollectorServicebrain.workload Collects 100+ database metrics (pg_stat_*, performance_schema) WorkloadCharacterizationServicebrain.workload Classifies workload type with fingerprint vectors KnobIdentificationServicebrain.config Ranks 14+ knobs per database type by impact ConfigTuningServicebrain.config ML + AI hybrid configuration recommendations CardinalityEstimationServicebrain.query Independent cardinality estimation using TDigest/HyperLogLog AdaptivePlanScoringServicebrain.query Learns from actual vs estimated execution times PlanPatternLibraryServicebrain.query Manages query plan pattern cache BrainLearningSchedulerbrain Orchestrates continuous background learning - Entity Classes (
com.dbaagent.model.brain):WorkloadMetricsSnapshot- Raw and reduced metrics per snapshotWorkloadProfile- Workload classification with fingerprint vectorsKnobRanking- Ranked configuration parameters with metadataConfigurationObservation- Records of configuration experimentsTuningExperiment- A/B testing lifecycle for config changesColumnStatistics- Independent cardinality estimation dataPlanExecution- Tracks actual vs estimated executionPlanPattern- Query plan patterns with cached suggestionsCostCalibration- Learned cost model adjustmentsBrainLearningProgress- Tracks overall Brain 2.0 readinessBrainV2Alert- Alerts for workload changes, config drift, etc.
- Brain Score Enhancement (6 dimensions, updated weights):
Dimension Weight Source Schema Design 15% Original Query Quality 15% Original Index & Access 20% Original Scalability 10% Original Config Tuning 15% Brain 2.0 (knob rankings, experiments) Query Intelligence 15% Brain 2.0 (cardinality accuracy, patterns) Workload Understanding 5% Brain 2.0 (classification confidence) Learning Progress 5% Brain 2.0 (milestone achievements) - REST API (consolidated into
BrainControllerat/api/brain):- Workload:
POST /workload/collect/{id},POST /workload/characterize/{id},GET /workload/profile/{id},GET /workload/status/{id},GET /workload/similar/{id} - Config:
POST /config/knobs/{id},GET /config/top-knobs/{id},GET /config/rankings/{id},POST /config/recommendations/{id},POST /config/experiments/{id} - Statistics:
POST /statistics/{id}/tables/{table},GET /statistics/{id},POST /statistics/{id}/estimate,POST /statistics/{id}/refresh - Executions:
POST /executions/{id},GET /executions/{id},GET /executions/{id}/cardinality-errors,GET /calibration/{id} - Patterns:
POST /patterns/{id}/suggestions,GET /patterns/{id}/reliable,GET /patterns/{id}/most-used,GET /patterns/{id}/stats - Overview:
GET /ml-overview/{id}(combined workload, config, and query intelligence status)
- Workload:
- Configuration Properties:
brain.v2.learning.enabled=true brain.v2.learning.metrics-interval-minutes=15 brain.v2.learning.characterization-interval-hours=4 brain.v2.learning.statistics-refresh-days=7
- Database Migrations:
- V42:
brain_v2_workload_intelligence.sql- workload_metrics_snapshot, workload_profile, workload_similarity - V43:
brain_v2_config_tuning.sql- knob_ranking, configuration_observation, tuning_experiment, cost_calibration - V44:
brain_v2_query_intelligence.sql- column_statistics, plan_execution, plan_pattern - V45:
brain_v2_score_enhancement.sql- brain_score new columns, brain_learning_progress, brain_v2_alert
- V42:
-
Performance Action System (ROI-Based Recommendations)
- Unified performance recommendation system that aggregates suggestions from multiple sources
- Actions are ranked by ROI (Return on Investment) = impact / effort × 100
- Entity Classes (
com.dbaagent.model):PerformanceAction- Unified action with impact/effort scores, status, and metadataQueryOptimizationCache- Caches AI optimization results to prevent redundant API calls
- Action Categories:
Category Description Examples INDEXCreate/modify indexes Missing index, composite index QUERY_REWRITERewrite slow queries Query optimization suggestions CONFIGConfiguration tuning Memory settings, connection pool SCHEMASchema changes Anti-patterns, normalization MAINTENANCEMaintenance tasks VACUUM, ANALYZE, REINDEX - Action Sources:
Source Service Description INDEX_ADVISORIndexAdvisorService Index recommendations SLOW_QUERY_ANALYSISQueryOptimizationService AI query optimization BRAIN_CONFIG_TUNINGConfigTuningService Configuration recommendations ANTI_PATTERN_DETECTIONAntiPatternDetectionService Schema anti-patterns KEY_COLUMN_ANALYSISKeyColumnAnalysisService Missing indexes on key columns BRAIN_INSIGHTSBrainScoreService General recommendations - Action Status Workflow:
PENDING→IN_PROGRESS→COMPLETED/DISMISSED - Services:
PerformanceActionAggregatorService- Collects and aggregates actions from all sourcesQueryOptimizationService- Enhanced with caching and query sanitizationQueryNormalizer.sanitize()- Centralized query text cleanup (removesUSE db;,SET timestamp=?;, etc.)
- REST API (
PerformanceActionControllerat/api/performance-actions):GET /{connectionId}- Get all pending actions sorted by ROIGET /{connectionId}/top?limit=N- Get top N actions by ROIGET /{connectionId}/by-category/{category}- Filter by categoryGET /{connectionId}/by-source/{source}- Filter by sourceGET /{connectionId}/summary- Get action counts by category/statusPOST /{connectionId}/refresh- Re-collect actions from all sourcesPUT /{actionId}/status- Update action status (complete/dismiss)
- Query Optimization Caching (
QueryOptimizationCacheRepository):- Caches AI optimization results by connection + query fingerprint
- Prevents redundant Azure OpenAI API calls for the same query patterns
- Tracks access count and last accessed timestamp
GET /api/slow-queries/{connectionId}/optimizations/cached- Batch fetch cached optimizations
- Frontend Components:
PerformanceInsightsTab- Redesigned with action-focused UIPerformanceActionCard- Displays action with impact/effort/ROI visualizationTableHeatmap- Visualizes table usage patterns (read/write heat)QueryDetailDialog- Detailed slow query analysis with optimization view- TanStack Query hooks:
usePerformanceActions,usePerformanceActionsSummary,useRefreshPerformanceActions,useUpdateActionStatus,useCachedOptimizations
- Database Migrations:
- V48:
create_query_optimization_cache.sql- query_optimization_cache table - V49:
create_performance_action.sql- performance_action table with ROI indexes
- V48:
API Layer (src/lib/api/client.js):
- Centralized axios client with 120s timeout
- Request interceptor: Auto-adds JWT Bearer token from localStorage
- Response interceptor: Handles 401/403 with redirect to login
- 25+ API modules: authAPI, connectionAPI, schemaAPI, trainingAPI, brainAPI, chatAPI, slowLogSourceAPI, queryPerformanceAPI, slowQueriesAPI (with optimization, alerts, fingerprints, dashboard, cached optimizations), performanceActionsAPI, etc.
State Management:
- Zustand for client-side UI state (preferred over Context API)
- TanStack Query v5 for server state (data fetching/caching)
- DashboardContext (legacy) - now delegates to Zustand store
Zustand Stores (src/lib/stores/):
| Store | Purpose | Persistence |
|---|---|---|
useDashboardStore |
activeTab, connectionId, dashboardConfig, isBuildingDashboard | localStorage (connectionId, activeTab) |
useConnectionStore |
selectedConnectionId, lastConnections (recent history) | localStorage |
useChatStore |
messages, chatIds, sendingTabs (per connection/tab) | localStorage (per tab) |
useUIStore |
modals, panels, menus, drag state, capture state | None (ephemeral) |
Zustand Usage Patterns:
// 1. Selector hooks (recommended - optimized re-renders)
import { useActiveTab, useDashboardActions } from '@/lib/stores'
const activeTab = useActiveTab()
const { setActiveTab } = useDashboardActions()
// 2. Direct store access
import { useDashboardStore } from '@/lib/stores'
const activeTab = useDashboardStore((state) => state.activeTab)
// 3. Outside React (imperative updates)
useDashboardStore.getState().setActiveTab('brain')
// 4. Legacy compatibility (during migration)
import { useDashboard } from '@/contexts/DashboardContext'
const { activeTab, setActiveTab } = useDashboard() // delegates to ZustandMigration Status: DashboardContext now uses Zustand under the hood. Components can be migrated incrementally to use Zustand directly for better performance.
Zustand Anti-Patterns to Avoid:
-
❌ Creating new action objects in render:
const actions = { setActiveTab: store.setActiveTab }- causes infinite loops -
❌ Calling setters in useEffect without guard conditions - use
if (currentValue !== newValue)checks -
❌ Using
useDashboard()context in new code - use Zustand hooks directly -
✅ Use cached action references:
const { setActiveTab } = useDashboardActions() -
✅ Use selector hooks for values:
const activeTab = useActiveTab() -
✅ Add
data-testidattributes to interactive elements for E2E testing -
Independent Chat Threads Per Tab: Each tab maintains its own independent chat thread per connection. Multiple tabs can send requests simultaneously without interference. Chat messages are stored per tab in localStorage (
chat-{connectionId}-{tab}) and chat IDs per tab (chatId-{connectionId}-{tab}). Responses are always routed to the originating tab's localStorage, regardless of which tab is currently active. Component state (messages) only updates if the response is for the currently active tab. When switching connection or tab, messages are automatically reloaded from localStorage for that connection+tab. ApendingContextChangeRefguard in PromptPanel prevents the save effects from writing the previous connection's messages/chatId to the new key during the same commit (seePromptPanel.js). ThesendingTabsRefSet tracks which tabs are currently sending requests, allowing concurrent requests from different tabs. -
localStorage Persistence:
dashboard-store: Zustand persisted state (activeTab, connectionId) - auto-managed by Zustand persist middlewareselectedConnectionId: Currently selected database connection (persists across page reloads)dashboard-{connectionId}: Dashboard configuration per connectionchat-{connectionId}-{tab}: Chat history per connection and tabchatId-{connectionId}-{tab}: Chat IDs per connection and tabauthToken: JWT authentication tokenusername: Logged-in usernameuserRole: User's role (VIEWER, EDITOR, ADMIN) for RBACuserPermissions: JSON array of permission strings for RBAC
TanStack Query v5 (Server State):
@tanstack/react-queryv5.90.16 with DevTools- Query client configured in
src/lib/queryClient.js(5min staleTime, 10min gcTime) - Query Keys (
src/lib/queryKeys.js):- Factory pattern for consistent, hierarchical keys
- Supports all 29 API domains: connections, brain, training, slowQueries, etc.
- Helper Hooks (
src/lib/hooks/):useApiQuery.js: Generic query/mutation wrappers with connection-based enablingusePollingQuery.js: Auto-refresh queries with configurable intervalsuseStreamingQuery.js: SSE integration for training statususeParallelQueries.js: Replace Promise.all patterns
- Domain Hooks (
src/lib/hooks/queries/):useConnections.js: Connection CRUD operationsuseBrain.js: 20+ hooks for brain features (understanding, key columns, schema classification, etc.)useTraining.js: Training status, history, streaminguseActiveQueries.js: Active query monitoring with pollinguseSlowQueries.js: 20+ hooks for slow query features (includesuseCachedOptimizations,useBatchOptimize)usePerformanceActions.js: Performance action CRUD, summary, refresh (ROI-based recommendations)useGrowthMonitoring.js,usePlaybooks.js,usePerformance.js,useSentinel.js,useIndex.js,useSavedItems.js
- Usage Pattern:
import { useConnections, useActiveQueries } from '@/lib/hooks/queries' // Fetch connections (auto-cached) const { data: connections, isLoading } = useConnections() // Polling example (auto-refresh every 3s) const { data: queries } = useActiveQueries(connectionId, { autoRefresh: true, interval: 3000 }) // Mutations with auto-invalidation const { mutate: saveConnection } = useSaveConnection()
- Migration Status: Foundation complete, Wave 1 components migrated (PromptPanel, ActiveQueryTab). Remaining components can be migrated incrementally.
Component Structure:
src/pages/Home.jsx- Main layout with resizable panelssrc/components/PromptPanel.js- Left sidebar (chat, connections, query results)src/components/Workspace.js- Main content area with tab switchingsrc/components/tabs/- 40+ specialized tabs organized by category:- Core: SqlRunnerTab, PreviewTab, TablesOverviewTab
- Analysis: SlowQueryAnalysisTab, ExplainPlanTab, QueryPerformanceTab
- Brain: 35+ subcomponents for database intelligence
- Monitoring: GrowthMonitoringTab, ActiveQueryTab, PerformanceInsightsTab
- Admin: PlaybooksTab, ConfigurationTunerTab, IndexRecommendationsTab
Key UI Libraries:
- Monaco Editor for SQL editing
- Recharts for data visualization
- React-window for virtual scrolling
- React-resizable-panels for split views
- React-markdown for formatted output
- React-force-graph-2d for dependency graphs
- React-force-graph-3d for 3D ER diagram (Brain Overview)
Design Philosophy:
- Sleek & Modern: Clean, minimalist interface with focus on content
- Lovable: Delightful micro-interactions and thoughtful details
- Minimal Chrome: Reduce UI chrome to maximize content space
- Smooth Transitions: All interactions should feel fluid and responsive
Typography:
- Primary Font: Inter (Google Fonts or system fallback)
- Font Stack:
'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif - Base Font Size: 16px (1rem) for body text
- Font Weights:
- Regular (400) for body text
- Medium (500) for emphasis
- Semibold (600) for headings and labels
- Bold (700) for strong emphasis (use sparingly)
- Line Height: 1.5 for body text, 1.2-1.3 for headings
- Letter Spacing: Default for body, slightly tighter (-0.01em to -0.02em) for headings
Color Palette:
- Primary Background:
#FFFFFF(white) - Secondary Background:
#F9FAFB(very light grey) - Tertiary Background:
#F3F4F6(light grey) - Primary Text:
#111827(near black) - Secondary Text:
#6B7280(medium grey) - Tertiary Text:
#9CA3AF(light grey) - Borders/Dividers:
#E5E7EB(light grey) - Hover States:
#F3F4F6(light grey background) - Active/Focus:
#111827with subtle outline or background change - Accent/Interactive:
#111827(black) for primary actions,#6B7280for secondary - Error States:
#DC2626(red) - use sparingly - Success States:
#059669(green) - use sparingly - Avoid: Color accents beyond black/white/grey unless absolutely necessary
Spacing & Layout:
- Base Unit: 4px (0.25rem)
- Spacing Scale: 4, 8, 12, 16, 20, 24, 32, 40, 48, 64, 80, 96px
- Container Padding: 16-24px for mobile, 24-32px for desktop
- Component Padding: 12-16px for compact, 16-20px for comfortable
- Border Radius:
- Small: 4-6px (buttons, badges)
- Medium: 8px (cards, inputs)
- Large: 12px (modals, large cards)
- Max Content Width: 1200-1400px for main content areas
- Grid Gaps: 16-24px between grid items
Transitions & Animations:
- Duration:
- Fast: 150ms (hover states, quick feedback)
- Normal: 200-300ms (most interactions)
- Slow: 400-500ms (page transitions, complex animations)
- Easing:
- Default:
cubic-bezier(0.4, 0, 0.2, 1)(ease-in-out) - Smooth:
cubic-bezier(0.25, 0.46, 0.45, 0.94)(ease-out) - Snappy:
cubic-bezier(0.4, 0, 1, 1)(ease-in)
- Default:
- Properties to Animate:
transform,opacity,background-color,border-color - Avoid Animating:
width,height,top,left(usetransforminstead) - Hover States: Subtle scale (1.01-1.02) or background color change
- Loading States: Smooth skeleton screens or subtle pulse animations
Component Patterns:
Buttons:
- Primary: Black background (
#111827), white text, 8px border radius - Secondary: Transparent/white background, black text, grey border
- Minimal padding: 10-12px vertical, 16-20px horizontal
- Smooth hover transition (200ms)
- Disabled: 40% opacity
Cards:
- White background, subtle border (
#E5E7EB) - 8-12px border radius
- Padding: 16-24px
- Subtle shadow on hover:
0 1px 3px rgba(0, 0, 0, 0.1) - Smooth transition on hover
Inputs:
- Minimal border: 1px solid
#E5E7EB - Border radius: 6-8px
- Padding: 10-12px vertical, 12-16px horizontal
- Focus: Border color change to
#111827with smooth transition - Placeholder:
#9CA3AF(tertiary text color)
Tables:
- Minimal borders: 1px solid
#E5E7EBbetween rows - Row hover:
#F9FAFBbackground - Header: Semibold (600),
#6B7280text - Cell padding: 12-16px
- Striped rows: Optional, use
#F9FAFBfor even rows
Modals/Dialogs:
- Backdrop:
rgba(0, 0, 0, 0.5)with smooth fade-in - Content: White background, 12px border radius
- Padding: 24-32px
- Smooth scale + fade animation (300ms)
- Max width: 500-600px for standard modals
Icons:
- Size: 16px (small), 20px (medium), 24px (large)
- Color: Inherit from parent or use
#6B7280for secondary icons - Stroke width: 1.5-2px for outline icons
- Smooth color transition on hover
Badges/Tags:
- Small border radius: 4-6px
- Padding: 4-6px vertical, 8-12px horizontal
- Background:
#F3F4F6, text:#111827 - Font size: 12-14px
- Font weight: Medium (500)
Tooltips (IMPORTANT - Use Rich Tooltips):
- ALWAYS use HelpTooltip component from
src/components/tabs/Brain/components/HelpTooltip.jsfor tooltips - NEVER use plain
titleattribute - it provides poor UX with limited formatting - Define help content in
src/components/tabs/Brain/utils/helpText.js- centralized, reusable, consistent - Rich tooltip content structure:
{ title: 'Feature Name', // Required: Term or feature name description: 'What it does...', // Required: Main explanation impact: 'Effect on performance', // Optional: Performance/behavior impact recommendation: 'Best practice', // Optional: What to do optimization: 'How to optimize', // Optional: Optimization tips examples: 'code or values' // Optional: Code examples }
- Usage pattern:
import { HelpTooltip } from './components/HelpTooltip' import { FEATURE_HELP } from './utils/helpText' <HelpTooltip content={FEATURE_HELP.buttonName}> <button>Action</button> </HelpTooltip>
- Visual style (handled by HelpTooltip component):
- Dark background:
#1f2937 - White text with grey description
- 6px border radius
- Sections with dividers for impact/recommendation
- Smooth fade-in animation (150ms)
- Fixed positioning via portal (avoids overflow issues)
- Dark background:
Loading States:
- Skeleton screens:
#F3F4F6background with subtle pulse - Spinners: Minimal, use Inter font or simple geometric shapes
- Progress bars: Thin (2-3px), black background
- Avoid flashy animations; keep it subtle
Accessibility:
- Contrast: Maintain WCAG AA contrast ratios (4.5:1 for text)
- Focus States: Visible outline (2px solid
#111827with offset) - Keyboard Navigation: All interactive elements must be keyboard accessible
- Screen Readers: Proper ARIA labels and semantic HTML
- Motion: Respect
prefers-reduced-motionmedia query - Touch Targets: Minimum 44x44px for mobile interactions
Implementation Notes:
- Use Tailwind CSS utility classes aligned with these guidelines
- Create reusable component variants (e.g.,
Button,Card,Input) that enforce these patterns - Avoid inline styles; use Tailwind classes or CSS modules
- Keep component styles co-located with components when possible
- Use CSS variables for colors if custom styling is needed:
--color-primary: #111827 - Test transitions on lower-end devices to ensure smoothness
Anti-Patterns to Avoid:
- ❌ Heavy shadows or excessive depth
- ❌ Bright colors or gradients (stick to black/white/grey)
- ❌ Abrupt state changes (always use transitions)
- ❌ Cluttered interfaces (maintain minimal chrome)
- ❌ Inconsistent spacing (use the spacing scale)
- ❌ Multiple font families (stick to Inter)
- ❌ Over-animated elements (keep animations subtle and purposeful)
- ❌ Plain
titleattributes for tooltips (use HelpTooltip component instead)
Schema ER Diagram & Inferred Relationships:
GET /api/connections/{connectionId}/visualizationreturns ER diagram and dependency graph.- VisualizationService builds entities from schema scan and relationships from:
- Foreign keys from schema metadata (explicit DB relationships).
- Inferred relationships from slow query JOINs, stored in
inferred_table_relationship(indexed by connection_id for fast loading).
- Inferred data is populated by JoinRelationshipInferenceService (Brain → Inferred Relationships → Analyze), which parses QueryLineage and SlowQueryHistory SQL with JSQLParser, extracts JOINs, and persists them. Run "Analyze inferred relationships" after slow query ingestion so the ER diagram shows both FK and JOIN-inferred links.
Chat Query Flow:
User Message
↓
ChatController.chat()
↓
ChatService.processMessage()
├─ SchemaScannerService.scanSchema() [DB metadata]
├─ TrainingService.cachedRetrieveRelevant() [RAG examples]
├─ buildClassificationContext() [Schema Intelligence]
│ ├─ SchemaClassificationService.getLatestClassification()
│ └─ TableClassificationRepository (roles, access patterns, anti-patterns)
├─ buildPerformanceInsightsContext() [Performance Intelligence]
│ ├─ SlowQueryHistoryRepository (slow query health)
│ ├─ QueryPerformanceRegressionRepository (degraded queries)
│ ├─ IndexRecommendationRepository (missing indexes)
│ ├─ KeyColumnAnalysisRepository (key columns for filtering/joining)
│ ├─ InferredTableRelationshipRepository (JOIN paths)
│ └─ GrowthAnomalyRepository (tables with growth issues)
├─ buildBrainContext() [Brain ML Intelligence]
│ ├─ WorkloadProfileRepository (workload type, metrics, recommendations)
│ ├─ KnobRankingRepository (tunable parameters by impact)
│ ├─ ColumnStatisticsRepository (skipped if Key Column data exists - avoids redundancy)
│ └─ PlanPatternRepository (query optimization patterns)
└─ Builds system prompt with database rules + all insights
↓
OpenAIClient.chat() [Azure OpenAI: gpt-5.4-pro]
↓
Extract SQL from markdown code blocks
↓
QueryExecutorService.execute() [Query execution]
↓
Results → ChatResponse → User display
Classification Context Includes:
- Schema design pattern (STAR, SNOWFLAKE, HYBRID)
- Table role groupings (FACT, DIMENSION, BRIDGE, LOOKUP, EVENT_LOG, ORPHANED)
- Per-table insights: access patterns, health scores, anti-patterns, sensitivity, business domain
- Query optimization hints: read-heavy tables, write-heavy tables, large tables, critical issues
Performance Insights Context Includes:
- Slow query health status and counts (CRITICAL, HIGH severity)
- Performance regressions (queries that degraded with slowdown %)
- Index recommendations (columns that need indexes)
- Key columns ranked by importance (best for WHERE/JOIN)
- Inferred JOIN paths with confidence scores
- Growth anomalies (tables needing LIMIT/pagination)
Brain ML Context Includes (lazy-loaded for tuning/optimization questions):
- Workload characterization (OLTP/OLAP/MIXED/READ_HEAVY/WRITE_HEAVY) with confidence %
- Performance metrics (throughput QPS, P50/P99 latency)
- Workload-specific optimization recommendations
- Top tunable configuration parameters ranked by impact score
- Column statistics with cardinality (highly selective vs low-cardinality columns)
- Reliable query plan patterns with proven optimization suggestions
Playbook Execution Flow:
User/Scheduler Trigger
↓
PlaybookExecutionService creates PlaybookRun (RUNNING)
↓
For each PlaybookStep:
PlaybookToolsService.executeTool()
↓
Captures results → Findings
↓
Apply thresholds → Recommendations
↓
Update PlaybookRun (COMPLETED)
↓
If alerts needed: PlaybookAlert creation + Notification dispatch
RAG Pipeline (Dual-Path Architecture):
The system supports two RAG approaches that can run independently or together:
-
Manual RAG (Primary) - Custom implementation with full features:
- Hybrid search (vector + BM25 keyword scoring)
- Connection-specific filtering (ODATA:
connectionId eq '{id}') - Document type filtering (see table below)
- Multi-tier caching (Redis → Memory → Database)
- Query success/execution time weighting
-
QuestionAnswerAdvisor (Optional) - Spring AI's automatic RAG:
- Pure vector similarity search via VectorStore
- Automatic context injection via advisor pattern
- Simpler but less feature-rich
RAG Document Types:
| Type | Description | Source |
|---|---|---|
SCHEMA_DDL |
Table DDL (CREATE TABLE statements) | TrainingService on schema training |
QUERY_EXAMPLE |
Sample SQL queries with natural language | Manual training examples |
DOCUMENTATION |
Custom documentation about schema/domain | Manual training |
COLUMN_VALUES |
Low-cardinality column values for filtering | ColumnValueCollectionService |
QUERY_PATTERN |
Reliable query plan patterns with optimization tips | BrainInsightEmbeddingService |
WORKLOAD_INSIGHT |
Workload characterization and tuning recommendations | BrainInsightEmbeddingService |
CARDINALITY_INSIGHT |
Cardinality accuracy recommendations (ANALYZE TABLE, histograms) | BrainInsightEmbeddingService |
Brain Insight Embedding:
BrainInsightEmbeddingServiceembeds Brain insights into Azure AI Search for semantic retrieval- Endpoint:
POST /api/brain/insights/{connectionId}/embed- Embeds all insights - Individual endpoints:
/embed/patterns,/embed/workload,/embed/cardinality - Enables chat to find relevant optimization suggestions when users ask about tuning
Manual RAG Flow:
Schema Analysis → DDL Generation → Embedding Creation (Azure OpenAI)
↓
Azure AI Search Storage (persistent vector DB)
↓
TrainingService.cachedRetrieveRelevant() → Hybrid Search (Vector + Keyword)
↓
trainingContext injected into system prompt
QuestionAnswerAdvisor Flow (when enabled):
User Question → QuestionAnswerAdvisor.before()
↓
VectorStore.similaritySearch() → Top-K documents
↓
Auto-injected into prompt by advisor
Configuration:
# Enable QuestionAnswerAdvisor (default: false, manual RAG is primary)
spring.ai.rag.advisor.enabled=false
# Enable comparison mode to log both approaches (for A/B testing)
spring.ai.rag.advisor.comparison-mode=false
# RAG parameters
spring.ai.rag.top-k=10
spring.ai.rag.similarity-threshold=0.7Slow Query Ingestion Flow:
Manual Trigger or Auto-Scheduler
↓
SlowLogIngestionService.ingestNow() or IngestionJobService (async)
↓
Fetch logs based on provider type:
├── S3LogFetchService (AWS S3)
├── CloudWatchLogFetchService (AWS CloudWatch)
├── AzureBlobLogFetchService (Azure Blob Storage)
├── GcpCloudLoggingFetchService (GCP Cloud Logging)
├── DatadogLogFetchService (Datadog APM)
└── ElasticsearchLogFetchService (Elasticsearch/ELK)
↓
SlowQueryLogParserService.parseAndAnalyze()
↓
SlowQueryHistoryService.saveAnalysis()
↓
QueryPerformanceService.recordQueryExecution() [for regression detection]
↓
QueryFingerprintService.processAnalysis() [auto-fingerprint tracking]
↓
SlowQueryAlertService.processAnalysisForAlerts() [auto-alert creation]
↓
Update SlowLogSourceConfig (lastProcessedAt, nextScheduledRunAt)
Brain Score Calculation:
BrainScoreService.calculateBrainScore()
↓
├─ Schema Design Score (25%) - FK constraints, normalization
├─ Query Quality Score (25%) - Slow queries, regressions, severity
├─ Index & Access Score (30%) - Key column coverage, anti-patterns
└─ Scalability Score (20%) - Large tables, partitioning readiness
↓
Weighted aggregate → Overall Brain Score (0-100)
↓
Stored in BrainScore entity with detailed breakdown JSON
AI Query Optimization Flow:
User selects slow query → SlowQueryController.optimizeQuery()
↓
QueryOptimizationService.optimizeQuery()
├─ Get database context (connection type, schema)
├─ Run EXPLAIN if SELECT query
├─ Build prompt with: query stats, schema context, EXPLAIN results
↓
Azure OpenAI (gpt-5.4-pro) analysis
↓
Parse AI response → OptimizationResult
├─ Optimized query (rewritten)
├─ Suggestions (categorized: QUERY_REWRITE, INDEX, SCHEMA, CONFIG)
├─ Index recommendations
└─ Estimated improvement percentage
Query Fingerprint Tracking Flow:
SlowQueryAnalysis completed → QueryFingerprintService.processAnalysis()
↓
For each slow query:
├─ Normalize query (replace literals with ?)
├─ Generate SHA-256 fingerprint hash
├─ Find or create QueryFingerprint entity
├─ Update current metrics from slow query
├─ Add to performance history (last 50 data points)
└─ Calculate trend (compare current vs baseline)
↓
Store updated fingerprints with trend analysis
↓
Detect regressions (>50% degradation triggers alert)
Slow Query Alert Notification Flow:
SlowQueryAlertService.processAnalysisForAlerts()
↓
For each slow query exceeding thresholds:
├─ CRITICAL (>5s avg): Create individual alert
└─ HIGH (>1s avg): Track for summary
↓
Create summary alert if critical count > threshold
↓
SlowQueryAlertService.sendNotifications(alert, config)
├─ Email: EmailService.sendSlowQueryAlert() → HTML formatted email
├─ Slack/Webhook: WebhookService.sendSlowQueryAlert() → Block Kit formatted
└─ Browser: PlaybookAlert stored for UI display
↓
Update alert.channelsSent list
Enhanced Schema Classification Flow:
SchemaClassificationService.classifySchema(connectionId)
↓
For each table in schema:
├─ AccessPatternClassificationService.classifyAccessPattern()
│ → Analyzes pg_stat_user_tables read/write stats
├─ AntiPatternDetectionService.detectAntiPatterns()
│ → Checks 10 anti-patterns with severity scoring
├─ TemporalClassificationService.classifyTemporalType()
│ → Identifies time-series, SCD, audit patterns
├─ TableHealthScoreService.calculateHealthScore()
│ → Computes 5-component health score (0-100)
├─ BusinessDomainClassificationService.classifyDomain()
│ → Matches table to business domain
├─ DataSensitivityClassificationService.classifySensitivity()
│ → Detects PII, financial, health data columns
└─ PartitionReadinessService.assessPartitionReadiness()
→ Evaluates partitioning suitability
↓
RelationshipClassificationService.classifyRelationships()
├─ Analyze FK constraints (STRONG)
├─ Detect naming patterns (INFERRED)
└─ Check join patterns from slow queries (WEAK)
↓
Store TableClassification + TableRelationshipClassification
↓
Update SchemaClassification aggregates
Test Structure:
- Base class:
backend/src/test/java/com/dbaagent/integration/BaseIntegrationTest.java - Uses
@SpringBootTestwith@ActiveProfiles("test") - Test configuration:
backend/src/test/resources/application-test.properties
Test Coverage:
- ConnectionController: 6 tests
- GrowthMonitoringController: 8 tests
- SlowQueryController: 19 tests (6 original + 13 slow query improvements)
- BrainController: 11 tests
- ChatPromptIntegrationTest: 20 tests (chat accuracy and speed)
- Unit tests: SlowQueryLogParserService, S3LogFetchService, KeyColumnAnalysisScoringTest, JoinRelationshipInferenceServiceTest
- Total: 64 integration tests
Important: Integration tests use real database connections (no mocking). Update TEST_CONNECTION_ID in application-test.properties with a valid connection ID from your database.
Test File: backend/src/test/java/com/dbaagent/integration/ChatPromptIntegrationTest.java
Tests DBA chat prompts for accuracy and response speed. Categories:
| Category | Tests | Description | Target Speed |
|---|---|---|---|
| Schema Questions | 5 | Table count, largest tables, overview, database type | < 500ms (fast-path) |
| Slow Query Questions | 3 | Slowest query, top N queries, performance health | < 500ms (fast-path) |
| Index Recommendations | 2 | Missing indexes, index suggestions | < 500ms (fast-path) |
| Workload Questions | 2 | Workload type, OLTP vs OLAP | < 500ms (fast-path) |
| LLM-Routed Questions | 4 | DB version, table-specific indexes, temporal queries, SQL generation | < 30s (LLM) |
| Edge Cases | 3 | Empty message, missing connection, long question | N/A (error handling) |
| Performance Benchmarks | 1 | Batch fast-path benchmark with assertions | < 500ms avg |
Running Chat Prompt Tests:
# Run all chat prompt tests
mvn test -Dtest=ChatPromptIntegrationTest
# Run specific test by name
mvn test -Dtest=ChatPromptIntegrationTest#testTableCount
mvn test -Dtest=ChatPromptIntegrationTest#testFastPathBatch
# Run with verbose output
mvn test -Dtest=ChatPromptIntegrationTest -DtrimStackTrace=falseTest Configuration:
- Requires valid
TEST_CONNECTION_IDfor any database connection (MySQL, PostgreSQL, etc.) - Set in
application-test.propertiesor as environment variable - Fast-path tests validate response time < 500ms (includes test overhead)
- LLM tests validate response time < 30s
- Tests assert
response.isSuccess()for accuracy validation - Tests check for expected keywords in responses
Performance Thresholds:
FAST_PATH_THRESHOLD_MS = 500- Fast-path questions should complete under 500msLLM_THRESHOLD_MS = 30000- LLM-routed questions should complete under 30s
Test Output: Tests print timing and response details to stdout for debugging:
=== TEST: How many tables? ===
Duration: 45ms
Response: ### Database Information...
>>> FAST-PATH response detected
- ESLint configured for code quality:
npm run lint - Manual testing via Vite dev server
Required for Backend:
# Vault database (PostgreSQL)
DB_URL=jdbc:postgresql://localhost:5432/dba_agent
DB_USERNAME=postgres
DB_PASSWORD=postgres
# Security
SECURITY_JWT_SECRET=<your-secret>
ADMIN_BOOTSTRAP_SECRET=<bootstrap-secret>
# LLM — bring your own. Read by LlmConfigResolver from the environment.
# PROVIDER gates the bundle: unset, no other DEEPSQL_CHAT_*/DEEPSQL_EMBEDDING_*
# value is read. `openai` is the only id shipped and covers OpenAI, Azure OpenAI
# and any OpenAI-compatible server. None of these is needed to boot.
DEEPSQL_CHAT_PROVIDER=openai
DEEPSQL_CHAT_API_KEY=<key>
DEEPSQL_CHAT_ENDPOINT=https://api.openai.com/v1 # no working fallback — set it
DEEPSQL_CHAT_MODEL=gpt-4o
DEEPSQL_EMBEDDING_PROVIDER=openai
DEEPSQL_EMBEDDING_API_KEY=<key>
DEEPSQL_EMBEDDING_ENDPOINT=https://api.openai.com/v1 # defaulted
DEEPSQL_EMBEDDING_MODEL=text-embedding-3-large # defaulted
# Optional: DEEPSQL_CHAT_TEMPERATURE, DEEPSQL_CHAT_API_VERSION,
# DEEPSQL_CHAT_USE_RESPONSES_API (true|false|auto)
# /api/llm/v1 — the OpenAI-shaped gateway the DeepSQL CLI agent points at — takes no
# variables of its own. LlmProxyController resolves through LlmConfigResolver.resolveChat()
# and dispatches auth on OpenAiEndpoints.isAzure(endpoint), so the DEEPSQL_CHAT_* bundle
# above configures it too. AZURE_OPENAI_* is read by no code at all.
# Azure AI Search (RAG)
azure.search.api-key=<key>
azure.search.endpoint=https://<resource>.search.windows.net
azure.search.index-name=dba-agent-training-data
# Redis/Valkey (caching)
spring.data.redis.host=localhost
spring.data.redis.port=6379
# Email notifications (optional)
EMAIL_HOST=smtp.gmail.com
EMAIL_USERNAME=<email>
EMAIL_PASSWORD=<password>
# Credential encryption
ENCRYPTION_KEY=<32-char-key>Application Properties:
- Development:
backend/src/main/resources/application.properties(auth disabled) - Production:
backend/src/main/resources/application-prod.properties(auth enabled) - Test:
backend/src/test/resources/application-test.properties
Feature Flags:
security.auth.enabled=false- Disable auth for developmentgrowth-monitoring.enabled=true- Enable growth monitoringazure.search.enabled=true- Enable RAG with Azure Searchslow-query.lineage.backfill.enabled=true- Enable slow query lineage
Environment-based API URL:
- Development:
http://localhost:8080/api(via Vite proxy) - Production: Set via
VITE_API_URLenvironment variable
Build modes:
npm run build- Development buildnpm run build:production- Production build with production API URLnpm run build:aws-production- AWS-specific production build
JPA/Hibernate Configuration:
- Dialect: PostgreSQLDialect
- DDL Mode:
update(auto-migrate) - Connection Pool: HikariCP (75 max, 15 min idle)
Key Entities:
- DatabaseConnection - Database credentials with SSH tunnel config (encrypted at rest)
- Playbook, PlaybookRun, PlaybookAlert - Automation framework
- TrainingDataEmbedding - Cached RAG embeddings
- SchemaMetadata - Schema snapshots
- SlowQuery, SlowQueryHistory, QueryPerformanceHistory - Query performance tracking
- QueryPerformanceRegression - Performance regression detection
- QueryFingerprint - Normalized query pattern tracking with trend analysis
- SlowLogSourceConfig, IngestionJob - Slow query log ingestion
- BrainTask, BrainScore - Database intelligence features
- GrowthAlertConfiguration - Growth monitoring rules
- ChatFeedback - User feedback on AI responses (corrections, teachings, thumbs up/down) for cross-session memory
- ColumnValueCache - Cached low-cardinality column values for RAG embedding
- IndexRecommendation - DBA-grade index suggestions backed by a multi-source workload-weighted pipeline. Sources: JSQLParser-AST slow-query analysis (
Σ calls × mean_exec_timeper role-tagged column, the pganalyze "total time" ROI metric); parsedExplainPlanNodewalks (Seq Scan / Full Table Scan / Nested Loop / filesort findings — replaces dead regex); inferred JOIN relationships (InferredTableRelationship.joinCount); composite co-occurrence (CompositeIndexRecommendation); column anti-patterns with persistedtotalExecutionTimeMs(ColumnAntiPattern); FK + schema-walk hints; partial-index hints whenKeyColumnAnalysis.skewCoefficient > 0.7; redundant-prefix DROP candidates (getDuplicateIndexes); unused-index DROP withpg_stat_database.stats_resetguard. Composite column ordering follows industry rules — equality before range, selectivity-ranked, ORDER BY suffix only with full-equality prefix, 3-column cap (seeCompositeIndexPlanner). Per-candidate fields:occurrenceCount/firstSeenAt/lastSeenAt(cycle recurrence),workloadScoreMs(total query time saved),writeCostScore(write-amplification penalty frompg_stat_user_tables.n_tup_*),evidenceCount,kind(CREATE_INDEX / DROP_INDEX). The 6-hour scheduler accumulates evidence across the configured lookback (default 30d) instead of wiping; ≥3-cycle recurrence auto-promotes to HIGH priority; rows not re-observed within staleness-days (default 14) are aged out.IndexRecommendationService.getTopRecommendationsWithEvidence(connectionId, limit)powers theGET /index-recommendations/{id}/topendpoint and theget_index_recommendationsMCP tool, ranked bypriority → (workloadScoreMs − writeCostScore) → occurrenceCount → lastSeenAt. Each top-N row carries up to 5topEvidencerecords from the V96index_recommendation_evidencetable — fingerprint, calls, mean/total exec time, rows examined, role — so callers can audit why each recommendation exists. A stubbedHypotheticalCostEstimatorinterface is in place for a future HypoPG-backed real-cost-delta validator (Postgres only). Configurable viaindex-recommendations.lookback-days,index-recommendations.staleness-days,index-recommendations.composite.min-workload-ms(default 60000),index-recommendations.composite.max-columns(default 3). See V93 (recurrence), V94 (kind), V95 (workload-score columns), V96 (evidence table) migrations. - SavedQuery, SavedDashboard - User persistence
- TableClassification, SchemaClassification - Enhanced schema analysis with access patterns, anti-patterns, health scores, domains, sensitivity, partitioning
- TableRelationshipClassification - Relationship strength, data integrity metrics, join frequency tracking
- InviteCode - Invite codes for private beta signup (code, max_uses, current_uses, expires_at, active)
- User - Extended with invite_code_id and invited_at for tracking signups
SQL Changelog (db/migration/) — NOT executed:
⚠️ This project has no Flyway.backend/pom.xmldeclares noflyway-coredependency andmvn dependency:listfinds noorg.flywaydbartifact, so nothing inbackend/src/main/resources/db/migration/runs at startup. Schema is managed byspring.jpa.hibernate.ddl-auto=update, backstopped bySchemaColumnCompatibilityInitializer,BrainInitSchemaCompatibilityInitializerandPgVectorRagStoreInitializerfor the cases Hibernate cannot express. The directory is a hand-maintained changelog: it carries duplicate version numbers (V31, V63, V103) that a real Flyway runtime would refuse to start on, which is itself proof it never ran. Apply anything here by hand withpsql. Files now run to V109.
- Located in
backend/src/main/resources/db/migration/ - V5: Playbook tables
- V6: Growth monitoring tables
- V7: Sentinel analytics tables
- V8-V27: Brain features, key columns, scalability simulation
- V28: Inferred table relationships
- V29: Enhanced table classification (access patterns, anti-patterns, temporal, health scores, business domains, data sensitivity, partition readiness, relationship classification)
- V30: Query fingerprints table for trend analysis
- V31-V34: Multi-provider slow log sources, schema change tracking, query plan cache, advanced table classification
- V35: SSH tunnel support for database connections (sshEnabled, sshAuthType, encrypted SSH credentials)
- V36: Spring AI feedback and column value tables (chat_feedback, column_value_cache)
- V37: Chat feedback additional columns (table_name, column_name, user_message, sql, feedback_text)
- V38: Invite codes table for private beta registration
- V39: Roles and permissions tables for RBAC
- V40: Cloud provider context fields (cloudProvider, managedService) for feature gating
- V38: Invite code system for private beta (invite_codes table, users.invite_code_id)
- V39: Role-Based Access Control (users.role column, role_permissions table)
-
Mandatory SQL Rule: All generated SQL queries MUST use table-qualified column names (table.column_name or alias.column_name). This is enforced in ChatService system prompts.
-
Connection Pool Management: Each database connection maintains its own HikariCP pool. Never close pools manually; ConnectionService handles lifecycle.
-
SSH-Aware Database Access: Always use
ConnectionService.getJdbcTemplate(connectionId, request)when services need to execute queries. This method:- Automatically handles SSH tunneling for connections with
sshEnabled=true - Works seamlessly for direct connections (no SSH)
- Ensures connection pools are properly managed
- Do NOT create
DriverManagerDataSourceor direct JDBC connections - they bypass SSH tunnels
- Automatically handles SSH tunneling for connections with
-
RAG Caching: Three-tier cache (memory → Redis → Azure Search). Clear cache when updating training data via TrainingService.
-
Redis Graceful Degradation: The application continues working even when Redis is unavailable.
CacheConfigprovides:- Custom
CacheErrorHandlerthat logs cache operation failures without throwing - Wrapper
CacheManagerthat returns null instead of throwing whengetCache()fails - All services using caching will work without Redis (just skip caching)
- Custom
-
Redis Cluster Mode (Azure):
RedisConfigprovides Azure Redis cluster configuration:- Activated when
spring.data.redis.cluster.nodesis set - Configures SSL, cluster topology refresh, and auto-reconnect
- Required for Azure Cache for Redis in cluster mode
- Activated when
-
Virtual Threads: Enabled via Spring Boot 3.2+ configuration for better concurrency with JDK 25.
-
CLI Components Excluded: The
com.dbaagent.cli.*package is excluded from component scanning (see DbaAgentApplication.java). -
LLM Provider Registry: For provider-specific LLM behavior, use
LlmProviderRegistry:- Do NOT add
if/elseorswitchon provider type. The shippedOpenAiCompatibleChatProviderdispatches on endpoint shape (an.azure.com/.azure-api.netbase URL selects Azure'sapi-keyheader auth), not on a provider id - Chat and embedding providers are indexed independently — some providers offer only one
- Providers are factories over credentials, not
ChatModels, so credentials resolve per call and key rotation needs no restart - Configuration is
DEEPSQL_CHAT_*/DEEPSQL_EMBEDDING_*(see LLM Providers). The per-serviceazure.openai.*-deploymentproperties are gone:chat-deployment,codex-deploymentandembedding-deploymentare read by no code at all ⚠️ The onboarding wizard (SetupController) writes a flat pre-BYO key namespace thatLlmConfigResolverdoes not read, so it does not configure the LLM. Environment variables are the only working path today
- Do NOT add
-
Database Provider Registry: For database-specific operations, use the provider registry pattern:
- Do NOT add
if/elseorswitchstatements for database types in services - Inject
DatabaseProviderRegistryand callregistry.getDialect(dbType).provider().method() - Example:
registry.getDialect("mysql").connection().buildJdbcUrl(request, port) - All providers are Spring beans supporting future dependency injection
- See section 6 "Database Provider Registry Pattern" for full architecture
- Do NOT add
-
API Centralization: ALL API calls must go through
src/lib/api/client.js. Never create direct axios instances. -
TanStack Query for Data Fetching: Use hooks from
@/lib/hooks/queriesfor server state. Benefits:- Automatic caching and background refetching
- Loading/error states handled automatically
- Query invalidation on mutations
- DevTools available in browser (bottom-right corner)
- Example:
const { data, isLoading } = useConnections()instead of manual useState/useEffect
-
Zustand for UI State: Use stores from
@/lib/storesfor client-side state:- Preferred: Use selector hooks for optimized re-renders:
const activeTab = useActiveTab() - Actions: Use action hooks to avoid re-renders:
const { setActiveTab } = useDashboardActions() - Legacy:
useDashboard()from DashboardContext still works (delegates to Zustand) - Benefits: No provider needed, selective subscriptions, built-in persistence middleware
- Stores:
useDashboardStore,useConnectionStore,useChatStore,useUIStore
- Preferred: Use selector hooks for optimized re-renders:
-
Auth State: Managed via
useAuthhook with cross-tab logout sync via storage events. -
Dashboard Persistence: Dashboards are stored in localStorage with connection-specific keys. Clear localStorage when testing dashboard features.
-
Monaco Editor: Uses
@monaco-editor/reactfor SQL editing. Custom SQL syntax highlighting configured. -
Tab Management: Active tab state stored in Zustand (
useDashboardStore). UsesetActiveTab()to switch tabs. -
Image Upload: Screenshots sent as base64 in chat messages for AI analysis. Max size: 4MB.
-
Connection Selection Persistence: Selected connection ID is stored in localStorage (
selectedConnectionId) and restored on page reload. Managed inPromptPanel.jswith validation that the saved connection still exists. -
Database Connection Modal (
DBConfigModal.js):- Database icons use base64 images (configured in
DB_ICONSobject with placeholders) - SSH tunnel configuration with collapsible panel
- Private key file upload support (.pem, .key, .txt files) using FileReader API
- Cloud provider deployment selector (AWS/Azure/GCP/Self-Hosted) with conditional service type dropdowns
- Database icons use base64 images (configured in
-
Landing Feature Scroll (
Landing.jsx): The features section uses a sticky left illustration column with scroll-drivenactiveSection. Avoid adding overflow constraints to ancestors of the sticky container; keeplg:sticky lg:top-24on the illustration wrapper so it stays fixed while the right content scrolls. -
Brain Overview Schema ER Diagram (
SchemaERD3D.js): Renders at the top of Brain → Overview. FetcheserDiagramfromGET /api/connections/{connectionId}/visualization; relationships include both FK-based (from schema) and inferred from slow query JOINs (from persistedinferred_table_relationship). Run Brain → Inferred Relationships → Analyze to populate inferred links so the diagram shows connections even when the DB has no FKs. Uses react-force-graph-2d (or 3d) for zoom/pan and node drag; click/hover a table node to show column detail. Includes a New (24h) toggle in the schema header (left of the role filter) that highlights recently added entities and relations usingGET /api/schema-changes/{connectionId}/changes(TABLE_ADDED,FOREIGN_KEY_ADDEDwithdetectedAtwithin 24 hours). Understanding, Key Columns, and Column Values panels sit below the diagram. -
Login Private Beta Request (
Login.jsx,AuthController.java): Login page includes an inline private beta request flow (Name, Title, Company name, Email). Submissions callPOST /api/auth/beta-signup, persist toprivate_beta_requests, and the UI shows confirmation: "We have received your private beta request, we will get back to you shortly."
- Create REST controller method in
backend/src/main/java/com/dbaagent/controller/ - Implement business logic in corresponding service class
- Add repository method if database access needed
- Add API method to relevant module in
src/lib/api/client.js - Update component to call new API method
- Create component in
src/components/tabs/ - Add tab definition to
ALL_TABSarray inWorkspace.jswith id, icon, label, and permission - Add
data-testid={tab-${tab.id}}attribute to tab button for E2E testing - Add icon from lucide-react
- No provider registration needed - Zustand handles tab state automatically
- Add tool definition to
PlaybookToolsService.executeTool() - Implement tool logic (usually calls existing service methods)
- Define expected parameters and return format
- Update PlaybookStep model if new parameter types needed
- Add UI for tool configuration in PlaybooksTab
- Modify schema or add training examples
- Call
TrainingService.trainConnectionIfNeeded()or use /train API endpoint - Clear Redis cache:
redis-cli FLUSHALL(or use Spring Cache eviction) - Azure Search index updates automatically via AzureSearchService
- Start vault database:
docker compose up -d postgres - Ensure test connection exists in database
- Update
TEST_CONNECTION_IDinbackend/src/test/resources/application-test.properties - Run tests:
mvn test -Dtest="*IntegrationTest" - Check results in
backend/target/surefire-reports/
Backend won't start:
- Check PostgreSQL vault database is running
- Verify
ENCRYPTION_KEYorENCRYPTION_KEYSis set —EncryptionServicefails fast without it - Check for port conflicts on 8080
- Note: a missing LLM credential is not a startup failure. The backend boots
unconfigured and raises
LlmNotConfiguredExceptionwhen something first asks for a model. If it will not start, the LLM config is not the cause.
Frontend can't connect to backend:
- Verify backend is running on http://localhost:8080/api
- Check CORS configuration in SecurityConfig.java
- Clear browser localStorage and cookies
Auth issues:
- Development: Ensure
security.auth.enabled=falsein application.properties - Production: Verify JWT secret is set and valid
- Check token expiry (default 10 hours)
RAG not working:
- Verify the vector store credentials (Azure AI Search, or pgvector reachability)
- Check if training data exists: call /api/training/status
- Ensure Redis is running for caching
- Verify
DEEPSQL_EMBEDDING_PROVIDERandDEEPSQL_EMBEDDING_API_KEYare set. WithoutDEEPSQL_EMBEDDING_PROVIDER, no embedding bundle resolves at all and retrieval stays keyword-only.AZURE_OPENAI_EMBEDDING_DEPLOYMENTis dead — setting it does nothing. - If
EMBEDDING_FAIL_OPEN=true(the default outside the prod profile), embedding failures return an empty vector and degrade retrieval silently. Set it tofalsewhile debugging so the error surfaces.
Tests failing:
- Verify TEST_CONNECTION_ID exists in database
- Check test database connectivity
- Ensure test profile is active (
@ActiveProfiles("test")) - Review
application-test.propertiesconfiguration
SSH tunnel connection issues:
- Verify SSH host is reachable from the application server
- Check SSH username and authentication credentials
- For private key auth: ensure key is in PEM format (starts with
-----BEGIN) - Verify the bastion host can reach the database host
- Check if SSH port (default 22) is open
- Review logs for JSch connection errors
Brain Analysis showing stale/duplicate data:
- Hard refresh browser (Cmd+Shift+R on Mac, Ctrl+Shift+R on Windows)
- Clear browser localStorage:
localStorage.clear()in console - Run fresh Brain Analysis to regenerate data
- Backend normalizes all table/column names to lowercase; if mixed case appears, it's cached UI data
Schema Analysis network error:
- Verify target database connection is accessible
- Check if database host is reachable (especially for localhost connections)
- For local MySQL: ensure MySQL server is running on expected port (default 3306)
- Check connection timeout settings in application.properties
- Use production connection if local database is unavailable
- Review backend logs for connection pool errors
Additional documentation in docs/ folder:
SETUP.md- Detailed setup instructionsQUICKSTART.md- Quick start guideAPI_CLIENT_USAGE.md- Frontend API integration guideRAG_SETUP.md- RAG configuration detailsQUICK_WINS_IMPLEMENTATION_GUIDE.md- Feature implementation guides
No recent activity