diff --git a/BOB.md b/BOB.md new file mode 100644 index 0000000000..97946e5972 --- /dev/null +++ b/BOB.md @@ -0,0 +1,178 @@ +# Stargate Data API - Project Analysis Summary + +## Project Overview + +**Stargate Data API** is a standalone HTTP microservice that provides a JSON Document-based interface for accessing data stored in Apache Cassandra clusters. Built with Quarkus 3.29.2 and Java 21, it targets JavaScript developers who interact through client libraries like stargate-mongoose. + +## Core Technology Stack + +- **Framework**: Quarkus 3.29.2 +- **Language**: Java 21 +- **Build Tool**: Maven +- **Backend Databases**: + - DataStax Enterprise (DSE) 6.9.15 + - HyperConverged Database (HCD) 1.2.3 + - Apache Cassandra with Storage Attached Index (SAI) +- **Driver**: Cassandra Java Driver 4.17.0 + custom QueryBuilder 4.19.0-preview1 +- **Container**: Docker with native image support + +## Key Features + +1. **JSON Document API**: MongoDB-like API for document operations on Cassandra +2. **Vector Search**: Embeddings up to 4096 dimensions with cosine, euclidean, dot_product metrics +3. **Embedding Providers**: OpenAI, AWS Bedrock, NVIDIA, Mistral integration +4. **Lexical Search**: Full-text search with configurable analyzers +5. **Reranking**: Document reranking with NVIDIA models +6. **Tables API**: Structured table operations alongside collections +7. **MCP Server**: Model Context Protocol integration (Quarkus MCP 1.7.1) + +## Architecture Components + +### API Layer (`api/`) +- REST endpoints with OpenAPI/Swagger documentation +- Security, authentication, and tenant management +- Request validation and token handling + +### Service Layer (`service/`) +- **Operations**: CRUD for collections and tables +- **Embedding**: Server-side vectorization +- **Reranking**: Document reranking services +- **Schema**: Collection and table schema management +- **Shredding**: Document decomposition for Cassandra storage +- **CQL**: Query building and execution + +### Configuration (`config/`) +- Document limits: 4MB size, 16 levels depth, 2000 properties +- Database limits: 5 collections per database +- Operation limits: 20 documents per insert/update/delete +- Vectorization and feature flags + +## Supported Commands + +**Collections**: `find`, `findOne`, `insertOne`, `insertMany`, `updateOne`, `updateMany`, `deleteOne`, `deleteMany`, `findOneAndUpdate`, `findOneAndReplace`, `findOneAndDelete`, `countDocuments`, `estimatedDocumentCount`, `createCollection`, `findCollections`, `deleteCollection` + +**Tables**: `createTable`, `dropTable`, `alterTable`, `listTables`, `createIndex`, `createVectorIndex`, `createTextIndex`, `dropIndex`, `listIndexes`, `createType`, `dropType`, `alterType`, `listTypes` + +**Keyspaces**: `createKeyspace`, `findKeyspaces`, `dropKeyspace` + +## Document Model + +- **Field Names**: `[a-zA-Z0-9_-]+` pattern (except reserved `_id`) +- **Paths**: Dotted notation (e.g., `address.suburb`, `tags.0`) +- **Filter Operators**: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$exists`, `$not`, `$and`, `$or`, `$nor`, `$all`, `$elemMatch`, `$size` +- **Array Support**: Zero-based indexing with array-specific operators + +## Deployment Options + +1. **Docker Compose**: Quick start with `./start_hcd.sh` or `./start_dse69.sh` +2. **Kubernetes**: Helm charts in `helm/jsonapi/` +3. **Native Executable**: GraalVM support +4. **Dev Mode**: `./mvnw compile quarkus:dev` + +## Testing Infrastructure + +- **Unit Tests**: JUnit 5 + Mockito (5.20.0) +- **Integration Tests**: Testcontainers with DSE/HCD +- **Profiles**: `dse69-it`, `hcd-it` +- **Performance**: NoSQLBench integration + +## Monitoring & Observability + +- **Metrics**: Micrometer + Prometheus +- **Health**: SmallRye Health at `/stargate/health` +- **Logging**: JSON logging, command-level logging +- **API Docs**: Swagger UI at `/swagger-ui/` + +## Key Configuration Defaults + +- Default page size: 20 documents +- Max in-memory sort: 10,000 documents +- Session cache: 300s TTL, max 50 sessions +- LWT retries: 3 attempts +- Max vector dimensions: 4096 floats +- Max string length: 8000 bytes +- Max array length: 1000 elements + +## Notable Design Decisions + +1. **Non-REST Design**: Optimized for machine-generated queries from ODMs +2. **Failure Modes**: "Fail Fast" vs "Fail Silently" for multi-document operations +3. **Optimistic Locking**: Compare-and-set for concurrent updates +4. **Document Shredding**: Custom decomposition for Cassandra storage +5. **Server-Side Vectorization**: Optional embedding generation + +## Project Structure + +- `src/main/java/io/stargate/sgv2/jsonapi/` - Core application +- `src/test/java/` - Comprehensive test suite +- `docs/` - API specifications (dataapi-spec.md, dataapi-network-spec.md) +- `docker-compose/` - Local deployment scripts +- `helm/` - Kubernetes deployment +- `lib/` - Custom Java driver repository + +## Quick Start + +### Running with Docker Compose + +```bash +cd docker-compose +./start_hcd.sh # For HCD +# or +./start_dse69.sh # For DSE 6.9 +``` + +### Running in Development Mode + +```bash +# Start backend database first +cd docker-compose +./start_hcd.sh -d + +# Then start Data API in dev mode +./mvnw compile quarkus:dev -Dstargate.jsonapi.operations.vectorize-enabled=true \ + -Dstargate.jsonapi.operations.database-config.local-datacenter=dc1 +``` + +### Building Docker Image + +```bash +./mvnw clean package -Dquarkus.container-image.build=true -DskipTests +``` + +### Running Tests + +```bash +./mvnw verify # All tests +./mvnw verify -DskipITs # Skip integration tests +./mvnw verify -DskipUnitTests # Only integration tests +``` + +## API Access + +Once running, access: +- **Swagger UI**: http://localhost:8181/swagger-ui/ +- **Health Check**: http://localhost:8181/stargate/health +- **Metrics**: http://localhost:8181/q/metrics + +## Authentication + +Token format for Cassandra backend: +``` +Token: Cassandra:Base64(username):Base64(password) +``` + +Example with default credentials (cassandra/cassandra): +``` +Token: Cassandra:Y2Fzc2FuZHJh:Y2Fzc2FuZHJh +``` + +## Additional Resources + +- [Configuration Guide](CONFIGURATION.md) +- [Data API Specification](docs/dataapi-spec.md) +- [Network Specification](docs/dataapi-network-spec.md) +- [Docker Compose README](docker-compose/README.md) + +--- + +This is a production-ready microservice bridging document-oriented applications with Cassandra's distributed capabilities, with strong support for modern AI/ML workloads through vector search and embedding integration. \ No newline at end of file diff --git a/nginx/nginx-quarkus.conf b/nginx/nginx-quarkus.conf new file mode 100644 index 0000000000..d69007419e --- /dev/null +++ b/nginx/nginx-quarkus.conf @@ -0,0 +1,23 @@ +events { + worker_connections 256; +} +http { + upstream quarkus_backend { + # This setting will use client ip to select route: it's stable + # but with localhost will always route to one of selections and + # not distribute. In production should load better + ip_hash; + server 127.0.0.1:8181; + server 127.0.0.1:8182; + } + + server { + listen 8180; + + location / { + proxy_pass http://quarkus_backend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + } +} diff --git a/nginx/start-quarkus-lb.sh b/nginx/start-quarkus-lb.sh new file mode 100755 index 0000000000..2a1c9453f5 --- /dev/null +++ b/nginx/start-quarkus-lb.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +CONFIG_FILE="$SCRIPT_DIR/nginx-quarkus.conf" + +echo "Starting Quarkus-LB with configs from '$CONFIG_FILE'..." + +nginx -c $CONFIG_FILE diff --git a/pom.xml b/pom.xml index 437723ef8e..a35c19b687 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,15 @@ pom import - + + + io.quarkiverse.mcp + quarkus-mcp-server-bom + 1.7.2 + pom + import + + software.amazon.awssdk bom @@ -149,6 +157,10 @@ io.quarkus quarkus-logging-json + + io.quarkiverse.mcp + quarkus-mcp-server-sse + jakarta.validation jakarta.validation-api diff --git a/src/main/java/io/stargate/sgv2/jsonapi/mcp/MCPPocTools.java b/src/main/java/io/stargate/sgv2/jsonapi/mcp/MCPPocTools.java new file mode 100644 index 0000000000..e5cc143baa --- /dev/null +++ b/src/main/java/io/stargate/sgv2/jsonapi/mcp/MCPPocTools.java @@ -0,0 +1,73 @@ +package io.stargate.sgv2.jsonapi.mcp; + +import io.micrometer.core.instrument.MeterRegistry; +import io.quarkiverse.mcp.server.Tool; +import io.quarkiverse.mcp.server.ToolArg; +import io.quarkiverse.mcp.server.ToolManager; +import io.quarkiverse.mcp.server.ToolResponse; +import io.stargate.sgv2.jsonapi.ConfigPreLoader; +import io.stargate.sgv2.jsonapi.api.model.command.CommandContext; +import io.stargate.sgv2.jsonapi.api.request.RequestContext; +import io.stargate.sgv2.jsonapi.metrics.JsonProcessingMetricsReporter; +import io.stargate.sgv2.jsonapi.service.cqldriver.CqlSessionCacheSupplier; +import io.stargate.sgv2.jsonapi.service.cqldriver.executor.SchemaCache; +import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor; +import jakarta.inject.Inject; +import java.util.Map; + +public class MCPPocTools { + @Inject private ToolManager toolManager; + + @Inject private RequestContext requestContext; + @Inject private SchemaCache schemaCache; + + private final CommandContext.BuilderSupplier contextBuilderSupplier; + private final MeteredCommandProcessor meteredCommandProcessor; + + @Inject + public MCPPocTools( + MeteredCommandProcessor meteredCommandProcessor, + MeterRegistry meterRegistry, + JsonProcessingMetricsReporter jsonProcessingMetricsReporter, + CqlSessionCacheSupplier sessionCacheSupplier) { + this.meteredCommandProcessor = meteredCommandProcessor; + + contextBuilderSupplier = + CommandContext.builderSupplier() + .withJsonProcessingMetricsReporter(jsonProcessingMetricsReporter) + .withCqlSessionCache(sessionCacheSupplier.get()) + .withCommandConfig(ConfigPreLoader.getPreLoadOrEmpty()) + .withMeterRegistry(meterRegistry); + } + + @Tool(description = "Simple Echo tool") + String echo( + @ToolArg(description = "Response", defaultValue = "OK", required = false) String response) { + return response; + } + + @Tool(description = "System info printer tool") + Map sysinfo() { + return Map.of( + "requestId", + requestContext.getRequestId(), + "tenantId", + requestContext.getTenantId().orElse("N/A"), + "token", + requestContext.getCassandraToken().orElse("N/A")); + } + + @Tool(description = "Add Tool tool") + String addTool(@ToolArg(description = "Name") String name) { + if (toolManager.getTool(name) != null) { + return "ALREADY_EXISTS"; + } + toolManager + .newTool(name) + .setDescription("Tool '" + name + "': lower-cases given String") + .addArgument("value", "Value to convert", true, String.class) + .setHandler(ta -> ToolResponse.success(ta.args().get("value").toString().toLowerCase())) + .register(); + return "ADDED"; + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 40a1095df8..daa56f9b1c 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -124,6 +124,15 @@ quarkus: min-level: trace + # MCP PoC + mcp: + server: + traffic-logging: + enabled: true + text-limit: 500 + sse: + root-path: /v1/mcp + # built-in micrometer properties micrometer: # exports at prometheus default path