diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandContext.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandContext.java
index 77db77b143..9cc978b6b4 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandContext.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandContext.java
@@ -9,15 +9,22 @@
import io.stargate.sgv2.jsonapi.config.feature.ApiFeature;
import io.stargate.sgv2.jsonapi.config.feature.ApiFeatures;
import io.stargate.sgv2.jsonapi.config.feature.FeaturesConfig;
+import io.stargate.sgv2.jsonapi.logging.LoggingMDCContext;
import io.stargate.sgv2.jsonapi.metrics.CommandFeatures;
import io.stargate.sgv2.jsonapi.metrics.JsonProcessingMetricsReporter;
import io.stargate.sgv2.jsonapi.service.cqldriver.CQLSessionCache;
import io.stargate.sgv2.jsonapi.service.cqldriver.executor.*;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.TableSchemaObject;
import io.stargate.sgv2.jsonapi.service.embedding.operation.EmbeddingProvider;
import io.stargate.sgv2.jsonapi.service.embedding.operation.EmbeddingProviderFactory;
import io.stargate.sgv2.jsonapi.service.reranking.operation.RerankingProviderFactory;
+import io.stargate.sgv2.jsonapi.service.schema.DatabaseSchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.KeyspaceSchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObjectType;
import io.stargate.sgv2.jsonapi.service.schema.collections.CollectionSchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.tables.TableSchemaObject;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -29,13 +36,16 @@
* context for a specific request call {@link BuilderSupplier#getBuilder(SchemaObject)} to get a
* {@link BuilderSupplier.Builder} to configure the context for the request.
*
- *
+ *
NOTE: When {@link BuilderSupplier.Builder#build()} is called it will call {@link
+ * #addToMDC()} so that the context is added to the logging MDC for the duration of the request. The
+ * context must be closed via {@link #close()} to remove it from the MDC, this should be done at the
+ * last possible time in the resource handler so all log messages have the context.
*
* @param The schema object type that this context is for. There are times we need to lock
* this down to the specific type, if so use the "as" methods such as {@link
* CommandContext#asCollectionContext()}
*/
-public class CommandContext {
+public class CommandContext implements LoggingMDCContext {
// Common for all instances
private final JsonProcessingMetricsReporter jsonProcessingMetricsReporter;
@@ -47,11 +57,15 @@ public class CommandContext {
// Request specific
private final SchemaT schemaObject;
+ private final RequestTracing requestTracing;
+ private final RequestContext requestContext;
private final EmbeddingProvider
embeddingProvider; // to be removed later, this is a single provider
private final String commandName; // TODO: remove the command name, but it is used in 14 places
- private final RequestContext requestContext;
- private RequestTracing requestTracing;
+
+ // both per request list of objects that want to update the logging MDC context,
+ // add to this list in the ctor. See {@link #addToMDC()} and {@link #removeFromMDC()}
+ private final List loggingMDCContexts = new ArrayList<>();
// see accessors
private FindAndRerankCommand.HybridLimits hybridLimits;
@@ -77,19 +91,23 @@ private CommandContext(
RerankingProviderFactory rerankingProviderFactory,
MeterRegistry meterRegistry) {
- this.schemaObject = schemaObject;
- this.embeddingProvider = embeddingProvider;
- this.commandName = commandName;
- this.requestContext = requestContext;
-
- this.jsonProcessingMetricsReporter = jsonProcessingMetricsReporter;
+ // Common for all instances
this.cqlSessionCache = cqlSessionCache;
this.commandConfig = commandConfig;
this.embeddingProviderFactory = embeddingProviderFactory;
+ this.jsonProcessingMetricsReporter = jsonProcessingMetricsReporter;
+ this.meterRegistry = meterRegistry;
this.rerankingProviderFactory = rerankingProviderFactory;
+ // Request specific
+ this.embeddingProvider = embeddingProvider; // to be removed later, this is a single provider
+ this.requestContext = requestContext;
+ this.schemaObject = schemaObject;
+ this.commandName = commandName; // TODO: remove the command name, but it is used in 14 places
this.apiFeatures = apiFeatures;
- this.meterRegistry = meterRegistry;
+
+ this.loggingMDCContexts.add(this.requestContext);
+ this.loggingMDCContexts.add(this.schemaObject.identifier());
var anyTracing =
apiFeatures().isFeatureEnabled(ApiFeature.REQUEST_TRACING)
@@ -191,34 +209,34 @@ public MeterRegistry meterRegistry() {
}
public boolean isCollectionContext() {
- return schemaObject().type() == CollectionSchemaObject.TYPE;
+ return schemaObject().type() == SchemaObjectType.COLLECTION;
}
@SuppressWarnings("unchecked")
public CommandContext asCollectionContext() {
- checkSchemaObjectType(CollectionSchemaObject.TYPE);
+ checkSchemaObjectType(SchemaObjectType.COLLECTION);
return (CommandContext) this;
}
@SuppressWarnings("unchecked")
public CommandContext asTableContext() {
- checkSchemaObjectType(TableSchemaObject.TYPE);
+ checkSchemaObjectType(SchemaObjectType.TABLE);
return (CommandContext) this;
}
@SuppressWarnings("unchecked")
public CommandContext asKeyspaceContext() {
- checkSchemaObjectType(KeyspaceSchemaObject.TYPE);
+ checkSchemaObjectType(SchemaObjectType.KEYSPACE);
return (CommandContext) this;
}
@SuppressWarnings("unchecked")
public CommandContext asDatabaseContext() {
- checkSchemaObjectType(DatabaseSchemaObject.TYPE);
+ checkSchemaObjectType(SchemaObjectType.DATABASE);
return (CommandContext) this;
}
- private void checkSchemaObjectType(SchemaObject.SchemaObjectType expectedType) {
+ private void checkSchemaObjectType(SchemaObjectType expectedType) {
Preconditions.checkArgument(
schemaObject().type() == expectedType,
"SchemaObject type actual was %s expected was %s ",
@@ -226,6 +244,24 @@ private void checkSchemaObjectType(SchemaObject.SchemaObjectType expectedType) {
expectedType);
}
+ @Override
+ public void addToMDC() {
+ loggingMDCContexts.forEach(LoggingMDCContext::addToMDC);
+ }
+
+ @Override
+ public void removeFromMDC() {
+ loggingMDCContexts.forEach(LoggingMDCContext::removeFromMDC);
+ }
+
+ /**
+ * NOTE: Not using AutoCloseable because it created a lot of linting warnings, we only want to
+ * close this in the request resource handler.
+ */
+ public void close() throws Exception {
+ removeFromMDC();
+ }
+
/**
* Configure the BuilderSupplier with resources and config that will be used for all the {@link
* CommandContext} that will be created. Then called {@link
@@ -341,18 +377,21 @@ public CommandContext build() {
Objects.requireNonNull(commandName, "commandName must not be null");
Objects.requireNonNull(requestContext, "requestContext must not be null");
- return new CommandContext<>(
- schemaObject,
- embeddingProvider,
- commandName,
- requestContext,
- jsonProcessingMetricsReporter,
- cqlSessionCache,
- commandConfig,
- apiFeatures,
- embeddingProviderFactory,
- rerankingProviderFactory,
- meterRegistry);
+ var context =
+ new CommandContext<>(
+ schemaObject,
+ embeddingProvider,
+ commandName,
+ requestContext,
+ jsonProcessingMetricsReporter,
+ cqlSessionCache,
+ commandConfig,
+ apiFeatures,
+ embeddingProviderFactory,
+ rerankingProviderFactory,
+ meterRegistry);
+ context.addToMDC();
+ return context;
}
}
}
diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandTarget.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandTarget.java
index b4f7ab96bf..f3abac12ac 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandTarget.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/CommandTarget.java
@@ -4,9 +4,8 @@
* The schema object a command can be called against.
*
* Example: creteTable runs against the Keyspace , so target is the Keyspace aaron 13 - nove -
- * 2024 - not using the {@link
- * io.stargate.sgv2.jsonapi.service.cqldriver.executor.SchemaObject.SchemaObjectType} because this
- * also needs the SYSTEM value, and the schema object design prob needs improvement
+ * 2024 - not using the {@link io.stargate.sgv2.jsonapi.service.cqldriver.executor.SchemaObjectType}
+ * because this also needs the SYSTEM value, and the schema object design prob needs improvement
*/
public enum CommandTarget {
COLLECTION,
diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/FilterClauseBuilder.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/FilterClauseBuilder.java
index e66bc334c9..fc0ad670fe 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/FilterClauseBuilder.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/FilterClauseBuilder.java
@@ -7,9 +7,9 @@
import io.stargate.sgv2.jsonapi.config.OperationsConfig;
import io.stargate.sgv2.jsonapi.config.constants.DocumentConstants;
import io.stargate.sgv2.jsonapi.exception.FilterException;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.SchemaObject;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.TableSchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObject;
import io.stargate.sgv2.jsonapi.service.schema.collections.CollectionSchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.tables.TableSchemaObject;
import io.stargate.sgv2.jsonapi.service.shredding.collections.DocumentId;
import io.stargate.sgv2.jsonapi.service.shredding.collections.JsonExtensionType;
import io.stargate.sgv2.jsonapi.util.JsonUtil;
diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/SortClauseBuilder.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/SortClauseBuilder.java
index 995d76668c..a25765591d 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/SortClauseBuilder.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/SortClauseBuilder.java
@@ -6,9 +6,9 @@
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.SortDefinition;
import io.stargate.sgv2.jsonapi.api.model.command.clause.sort.SortClause;
import io.stargate.sgv2.jsonapi.exception.SortException;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.SchemaObject;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.TableSchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObject;
import io.stargate.sgv2.jsonapi.service.schema.collections.CollectionSchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.tables.TableSchemaObject;
import io.stargate.sgv2.jsonapi.util.JsonUtil;
import java.util.Map;
import java.util.Objects;
diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/TableFilterClauseBuilder.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/TableFilterClauseBuilder.java
index a4f156465e..311ad18eb3 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/TableFilterClauseBuilder.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/TableFilterClauseBuilder.java
@@ -8,9 +8,9 @@
import io.stargate.sgv2.jsonapi.api.model.command.clause.filter.*;
import io.stargate.sgv2.jsonapi.api.model.command.table.definition.datatype.MapComponentDesc;
import io.stargate.sgv2.jsonapi.exception.FilterException;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.TableSchemaObject;
import io.stargate.sgv2.jsonapi.service.operation.filters.table.MapSetListFilterComponent;
import io.stargate.sgv2.jsonapi.service.schema.tables.ApiTypeName;
+import io.stargate.sgv2.jsonapi.service.schema.tables.TableSchemaObject;
import io.stargate.sgv2.jsonapi.util.CqlIdentifierUtil;
import java.util.*;
diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/TableSortClauseBuilder.java b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/TableSortClauseBuilder.java
index a4b88b2fdc..07781c03c9 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/TableSortClauseBuilder.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/api/model/command/builders/TableSortClauseBuilder.java
@@ -13,9 +13,9 @@
import io.stargate.sgv2.jsonapi.api.model.command.clause.sort.SortClause;
import io.stargate.sgv2.jsonapi.api.model.command.clause.sort.SortExpression;
import io.stargate.sgv2.jsonapi.exception.SortException;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.TableSchemaObject;
import io.stargate.sgv2.jsonapi.service.schema.tables.ApiColumnDef;
import io.stargate.sgv2.jsonapi.service.schema.tables.ApiColumnDefContainer;
+import io.stargate.sgv2.jsonapi.service.schema.tables.TableSchemaObject;
import io.stargate.sgv2.jsonapi.util.CqlIdentifierUtil;
import io.stargate.sgv2.jsonapi.util.JsonUtil;
import java.util.ArrayList;
diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java
index b3491fea6c..8b5a5f5527 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/CollectionResource.java
@@ -1,6 +1,7 @@
package io.stargate.sgv2.jsonapi.api.v1;
import static io.stargate.sgv2.jsonapi.config.constants.DocumentConstants.Fields.VECTOR_EMBEDDING_TEXT_FIELD;
+import static io.stargate.sgv2.jsonapi.util.CqlIdentifierUtil.cqlIdentifierFromUserInput;
import io.micrometer.core.instrument.MeterRegistry;
import io.smallrye.mutiny.Uni;
@@ -31,13 +32,14 @@
import io.stargate.sgv2.jsonapi.exception.mappers.ThrowableCommandResultSupplier;
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.cqldriver.executor.SchemaObject;
import io.stargate.sgv2.jsonapi.service.cqldriver.executor.VectorColumnDefinition;
import io.stargate.sgv2.jsonapi.service.embedding.operation.EmbeddingProvider;
import io.stargate.sgv2.jsonapi.service.embedding.operation.EmbeddingProviderFactory;
import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor;
import io.stargate.sgv2.jsonapi.service.reranking.operation.RerankingProviderFactory;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObjectCacheSupplier;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObjectType;
+import io.stargate.sgv2.jsonapi.service.schema.UnscopedSchemaObjectIdentifier;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
@@ -60,6 +62,8 @@
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.resteasy.reactive.RestResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@Path(CollectionResource.BASE_PATH)
@Produces(MediaType.APPLICATION_JSON)
@@ -67,6 +71,7 @@
@SecurityRequirement(name = OpenApiConstants.SecuritySchemes.TOKEN)
@Tag(ref = "Documents")
public class CollectionResource {
+ private static final Logger LOGGER = LoggerFactory.getLogger(CollectionResource.class);
public static final String BASE_PATH = GeneralResource.BASE_PATH + "/{keyspace}/{collection}";
@@ -75,20 +80,23 @@ public class CollectionResource {
// TODO remove apiFeatureConfig as a property after cleanup for how we get schema from cache
@Inject private FeaturesConfig apiFeatureConfig;
@Inject private RequestContext requestContext;
- @Inject private SchemaCache schemaCache;
+ private final SchemaObjectCacheSupplier schemaObjectCacheSupplier;
private final CommandContext.BuilderSupplier contextBuilderSupplier;
private final EmbeddingProviderFactory embeddingProviderFactory;
private final MeteredCommandProcessor meteredCommandProcessor;
@Inject
public CollectionResource(
+ SchemaObjectCacheSupplier schemaObjectCacheSupplier,
MeteredCommandProcessor meteredCommandProcessor,
MeterRegistry meterRegistry,
JsonProcessingMetricsReporter jsonProcessingMetricsReporter,
CqlSessionCacheSupplier sessionCacheSupplier,
EmbeddingProviderFactory embeddingProviderFactory,
RerankingProviderFactory rerankingProviderFactory) {
+
+ this.schemaObjectCacheSupplier = schemaObjectCacheSupplier;
this.embeddingProviderFactory = embeddingProviderFactory;
this.meteredCommandProcessor = meteredCommandProcessor;
@@ -198,12 +206,15 @@ public Uni> postCommand(
@NotNull @Valid CollectionCommand command,
@PathParam("keyspace") @NotEmpty String keyspace,
@PathParam("collection") @NotEmpty String collection) {
- return schemaCache
- .getSchemaObject(
- requestContext,
- keyspace,
- collection,
- CommandType.DDL.equals(command.commandName().getCommandType()))
+
+ var name =
+ new UnscopedSchemaObjectIdentifier.DefaultKeyspaceScopedName(
+ cqlIdentifierFromUserInput(keyspace), cqlIdentifierFromUserInput(collection));
+ var forceRefresh = CommandType.DDL.equals(command.commandName().getCommandType());
+
+ return schemaObjectCacheSupplier
+ .get()
+ .getTableBased(requestContext, name, requestContext.userAgent(), forceRefresh)
.onItemOrFailure()
.transformToUni(
(schemaObject, throwable) -> {
@@ -219,19 +230,17 @@ public Uni> postCommand(
// otherwise use generic for now
return Uni.createFrom().item(new ThrowableCommandResultSupplier(error));
} else {
- // TODO No need for the else clause here, simplify
-
// TODO: This needs to change, currently it is only checking if there is vectorize
// for the $vector column in a collection
VectorColumnDefinition vectorColDef = null;
- if (schemaObject.type() == SchemaObject.SchemaObjectType.COLLECTION) {
+ if (schemaObject.type() == SchemaObjectType.COLLECTION) {
vectorColDef =
schemaObject
.vectorConfig()
.getColumnDefinition(VECTOR_EMBEDDING_TEXT_FIELD)
.orElse(null);
- } else if (schemaObject.type() == SchemaObject.SchemaObjectType.TABLE) {
+ } else if (schemaObject.type() == SchemaObjectType.TABLE) {
vectorColDef =
schemaObject
.vectorConfig()
@@ -262,7 +271,20 @@ public Uni> postCommand(
.withRequestContext(requestContext)
.build();
- return meteredCommandProcessor.processCommand(commandContext, command);
+ return meteredCommandProcessor
+ .processCommand(commandContext, command)
+ .onTermination()
+ .invoke(
+ () -> {
+ try {
+ commandContext.close();
+ } catch (Exception e) {
+ LOGGER.error(
+ "Error closing the command context for requestContext={}",
+ requestContext,
+ e);
+ }
+ });
}
})
.map(commandResult -> commandResult.toRestResponse());
diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java
index 033360a601..e9c5cd4335 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/GeneralResource.java
@@ -11,10 +11,11 @@
import io.stargate.sgv2.jsonapi.config.constants.OpenApiConstants;
import io.stargate.sgv2.jsonapi.metrics.JsonProcessingMetricsReporter;
import io.stargate.sgv2.jsonapi.service.cqldriver.CqlSessionCacheSupplier;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.DatabaseSchemaObject;
import io.stargate.sgv2.jsonapi.service.embedding.operation.EmbeddingProviderFactory;
import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor;
import io.stargate.sgv2.jsonapi.service.reranking.operation.RerankingProviderFactory;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObjectCacheSupplier;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObjectIdentifier;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
@@ -33,6 +34,8 @@
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.resteasy.reactive.RestResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@Path(GeneralResource.BASE_PATH)
@Produces(MediaType.APPLICATION_JSON)
@@ -40,21 +43,27 @@
@SecurityRequirement(name = OpenApiConstants.SecuritySchemes.TOKEN)
@Tag(ref = "General")
public class GeneralResource {
+ private static final Logger LOGGER = LoggerFactory.getLogger(GeneralResource.class);
+
public static final String BASE_PATH = "/v1";
@Inject private RequestContext requestContext;
+ private final SchemaObjectCacheSupplier schemaObjectCacheSupplier;
private final CommandContext.BuilderSupplier contextBuilderSupplier;
private final MeteredCommandProcessor meteredCommandProcessor;
@Inject
public GeneralResource(
+ SchemaObjectCacheSupplier schemaObjectCacheSupplier,
MeteredCommandProcessor meteredCommandProcessor,
MeterRegistry meterRegistry,
JsonProcessingMetricsReporter jsonProcessingMetricsReporter,
CqlSessionCacheSupplier sessionCacheSupplier,
EmbeddingProviderFactory embeddingProviderFactory,
RerankingProviderFactory rerankingProviderFactory) {
+
+ this.schemaObjectCacheSupplier = schemaObjectCacheSupplier;
this.meteredCommandProcessor = meteredCommandProcessor;
contextBuilderSupplier =
@@ -98,16 +107,36 @@ public GeneralResource(
@POST
public Uni> postCommand(@NotNull @Valid GeneralCommand command) {
- var commandContext =
- contextBuilderSupplier
- .getBuilder(new DatabaseSchemaObject())
- .withCommandName(command.getClass().getSimpleName())
- .withRequestContext(requestContext)
- .build();
+ var dbIdentifier = SchemaObjectIdentifier.forDatabase(requestContext.tenant());
+
+ return schemaObjectCacheSupplier
+ .get()
+ .getDatabase(requestContext, dbIdentifier, requestContext.userAgent(), false)
+ .flatMap(
+ databaseSchemaObject -> {
+ var commandContext =
+ contextBuilderSupplier
+ .getBuilder(databaseSchemaObject)
+ .withCommandName(command.getClass().getSimpleName())
+ .withRequestContext(requestContext)
+ .build();
- return meteredCommandProcessor
- .processCommand(commandContext, command)
- // map to 2xx unless overridden by error
- .map(commandResult -> commandResult.toRestResponse());
+ return meteredCommandProcessor
+ .processCommand(commandContext, command)
+ // map to 2xx unless overridden by error
+ .map(commandResult -> commandResult.toRestResponse())
+ .onTermination()
+ .invoke(
+ () -> {
+ try {
+ commandContext.close();
+ } catch (Exception e) {
+ LOGGER.error(
+ "Error closing the command context for requestContext={}",
+ requestContext,
+ e);
+ }
+ });
+ });
}
}
diff --git a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/KeyspaceResource.java b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/KeyspaceResource.java
index ff4f9dda11..fc9b1c7660 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/api/v1/KeyspaceResource.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/api/v1/KeyspaceResource.java
@@ -1,5 +1,7 @@
package io.stargate.sgv2.jsonapi.api.v1;
+import static io.stargate.sgv2.jsonapi.util.CqlIdentifierUtil.cqlIdentifierFromUserInput;
+
import io.micrometer.core.instrument.MeterRegistry;
import io.smallrye.mutiny.Uni;
import io.stargate.sgv2.jsonapi.ConfigPreLoader;
@@ -11,10 +13,11 @@
import io.stargate.sgv2.jsonapi.config.constants.OpenApiConstants;
import io.stargate.sgv2.jsonapi.metrics.JsonProcessingMetricsReporter;
import io.stargate.sgv2.jsonapi.service.cqldriver.CqlSessionCacheSupplier;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.KeyspaceSchemaObject;
import io.stargate.sgv2.jsonapi.service.embedding.operation.EmbeddingProviderFactory;
import io.stargate.sgv2.jsonapi.service.processor.MeteredCommandProcessor;
import io.stargate.sgv2.jsonapi.service.reranking.operation.RerankingProviderFactory;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObjectCacheSupplier;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObjectIdentifier;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
@@ -37,6 +40,8 @@
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.resteasy.reactive.RestResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@Path(KeyspaceResource.BASE_PATH)
@Produces(MediaType.APPLICATION_JSON)
@@ -44,22 +49,27 @@
@SecurityRequirement(name = OpenApiConstants.SecuritySchemes.TOKEN)
@Tag(ref = "Keyspaces")
public class KeyspaceResource {
+ private static final Logger LOGGER = LoggerFactory.getLogger(KeyspaceResource.class);
public static final String BASE_PATH = GeneralResource.BASE_PATH + "/{keyspace}";
@Inject private RequestContext requestContext;
+ private final SchemaObjectCacheSupplier schemaObjectCacheSupplier;
private final CommandContext.BuilderSupplier contextBuilderSupplier;
private final MeteredCommandProcessor meteredCommandProcessor;
@Inject
public KeyspaceResource(
+ SchemaObjectCacheSupplier schemaObjectCacheSupplier,
MeteredCommandProcessor meteredCommandProcessor,
MeterRegistry meterRegistry,
JsonProcessingMetricsReporter jsonProcessingMetricsReporter,
CqlSessionCacheSupplier sessionCacheSupplier,
EmbeddingProviderFactory embeddingProviderFactory,
RerankingProviderFactory rerankingProviderFactory) {
+
+ this.schemaObjectCacheSupplier = schemaObjectCacheSupplier;
this.meteredCommandProcessor = meteredCommandProcessor;
contextBuilderSupplier =
@@ -139,18 +149,41 @@ public Uni> postCommand(
// CommandContext commandContext = new CommandContext(keyspace, null);
// HACK TODO: The above did not set a command name on the command context, how did that work ?
- var commandContext =
- contextBuilderSupplier
- .getBuilder(new KeyspaceSchemaObject(keyspace))
- .withEmbeddingProvider(null)
- .withCommandName(command.getClass().getSimpleName())
- .withRequestContext(requestContext)
- .build();
+ var keyspaceIdentifier =
+ SchemaObjectIdentifier.forKeyspace(
+ requestContext.tenant(), cqlIdentifierFromUserInput(keyspace));
+
+ // Force refresh on all keyspace commands because they are all DDL commands
+ return schemaObjectCacheSupplier
+ .get()
+ .getKeyspace(requestContext, keyspaceIdentifier, requestContext.userAgent(), true)
+ .flatMap(
+ keyspaceSchemaObject -> {
+ var commandContext =
+ contextBuilderSupplier
+ .getBuilder(keyspaceSchemaObject)
+ .withEmbeddingProvider(null)
+ .withCommandName(command.getClass().getSimpleName())
+ .withRequestContext(requestContext)
+ .build();
- // call processor
- return meteredCommandProcessor
- .processCommand(commandContext, command)
- // map to 2xx unless overridden by error
- .map(commandResult -> commandResult.toRestResponse());
+ // call processor
+ return meteredCommandProcessor
+ .processCommand(commandContext, command)
+ // map to 2xx unless overridden by error
+ .map(commandResult -> commandResult.toRestResponse())
+ .onTermination()
+ .invoke(
+ () -> {
+ try {
+ commandContext.close();
+ } catch (Exception e) {
+ LOGGER.error(
+ "Error closing the command context for requestContext={}",
+ requestContext,
+ e);
+ }
+ });
+ });
}
}
diff --git a/src/main/java/io/stargate/sgv2/jsonapi/exception/ErrorFormatters.java b/src/main/java/io/stargate/sgv2/jsonapi/exception/ErrorFormatters.java
index fd4f2bc425..b4ec28b50b 100644
--- a/src/main/java/io/stargate/sgv2/jsonapi/exception/ErrorFormatters.java
+++ b/src/main/java/io/stargate/sgv2/jsonapi/exception/ErrorFormatters.java
@@ -1,12 +1,15 @@
package io.stargate.sgv2.jsonapi.exception;
+import static io.stargate.sgv2.jsonapi.util.CqlIdentifierUtil.cqlIdentifierToMessageString;
+
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.metadata.schema.ColumnMetadata;
import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
import com.datastax.oss.driver.api.core.type.DataType;
import io.stargate.sgv2.jsonapi.api.model.command.table.definition.datatype.ColumnDesc;
import io.stargate.sgv2.jsonapi.config.constants.ErrorObjectV2Constants.TemplateVars;
-import io.stargate.sgv2.jsonapi.service.cqldriver.executor.SchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.SchemaObject;
+import io.stargate.sgv2.jsonapi.service.schema.UnscopedSchemaObjectIdentifier;
import io.stargate.sgv2.jsonapi.service.schema.tables.ApiColumnDef;
import io.stargate.sgv2.jsonapi.service.schema.tables.ApiColumnDefContainer;
import io.stargate.sgv2.jsonapi.service.schema.tables.ApiDataType;
@@ -145,6 +148,26 @@ public static Map errVars(SchemaObject schemaObject, Throwable e
return errVars(schemaObject, exception, null);
}
+ public static Map errVars(UnscopedSchemaObjectIdentifier name) {
+ return errVars(name, null);
+ }
+
+ public static Map errVars(
+ UnscopedSchemaObjectIdentifier name, Consumer