This is an automated email from the ASF dual-hosted git repository.
snazy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git
The following commit(s) were added to refs/heads/main by this push:
new f5224fe92 NoSQL: CDI / Quarkus (#3135)
f5224fe92 is described below
commit f5224fe92c4cf26c74795a408dfc58e964c30d6b
Author: Robert Stupp <[email protected]>
AuthorDate: Thu Dec 4 06:56:08 2025 +0100
NoSQL: CDI / Quarkus (#3135)
---
bom/build.gradle.kts | 1 +
gradle/projects.main.properties | 1 +
.../nosql/api/backend/BackendConfiguration.java | 6 +-
persistence/nosql/persistence/benchmark/NOTES.md | 8 +-
.../nosql/persistence/cdi/quarkus/build.gradle.kts | 66 ++++++
.../nosql/quarkus/backend/BackendBuilder.java} | 9 +-
.../nosql/quarkus/backend/BackendProvider.java | 105 ++++++++
.../nosql/quarkus/backend/BackendType.java | 59 +++++
.../quarkus/backend/InMemoryBackendBuilder.java} | 29 +--
.../quarkus/backend/MongoDbBackendBuilder.java | 49 ++++
.../nosql/quarkus/backend/NotObserved.java} | 31 ++-
.../nosql/quarkus/backend/ObservingBackend.java | 263 +++++++++++++++++++++
.../persistence/nosql/weld/BackendProvider.java | 4 +-
.../nosql/weld/CdiTestingProviders.java | 2 +-
.../nosql/persistence/correctness/README.md | 6 +-
.../nosql/inmemory/InMemoryConfiguration.java | 2 +-
.../nosql/mongodb/MongoDbConfiguration.java | 2 +-
.../docker/mongodb-3-nodes/docker-compose.yml | 6 +-
.../nosql/standalone/PersistenceConfigurer.java | 4 +-
19 files changed, 597 insertions(+), 56 deletions(-)
diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts
index bab80f538..8daafeeae 100644
--- a/bom/build.gradle.kts
+++ b/bom/build.gradle.kts
@@ -64,6 +64,7 @@ dependencies {
api(project(":polaris-persistence-nosql-benchmark"))
api(project(":polaris-persistence-nosql-correctness"))
api(project(":polaris-persistence-nosql-cdi-common"))
+ api(project(":polaris-persistence-nosql-cdi-quarkus"))
api(project(":polaris-persistence-nosql-cdi-quarkus-distcache"))
api(project(":polaris-persistence-nosql-cdi-weld"))
api(project(":polaris-persistence-nosql-standalone"))
diff --git a/gradle/projects.main.properties b/gradle/projects.main.properties
index 488394b49..454c704de 100644
--- a/gradle/projects.main.properties
+++ b/gradle/projects.main.properties
@@ -79,6 +79,7 @@
polaris-persistence-nosql-impl=persistence/nosql/persistence/impl
polaris-persistence-nosql-benchmark=persistence/nosql/persistence/benchmark
polaris-persistence-nosql-correctness=persistence/nosql/persistence/correctness
polaris-persistence-nosql-cdi-common=persistence/nosql/persistence/cdi/common
+polaris-persistence-nosql-cdi-quarkus=persistence/nosql/persistence/cdi/quarkus
polaris-persistence-nosql-cdi-quarkus-distcache=persistence/nosql/persistence/cdi/quarkus-distcache
polaris-persistence-nosql-cdi-weld=persistence/nosql/persistence/cdi/weld
polaris-persistence-nosql-standalone=persistence/nosql/persistence/standalone
diff --git
a/persistence/nosql/persistence/api/src/main/java/org/apache/polaris/persistence/nosql/api/backend/BackendConfiguration.java
b/persistence/nosql/persistence/api/src/main/java/org/apache/polaris/persistence/nosql/api/backend/BackendConfiguration.java
index 5e178d23b..9b0714326 100644
---
a/persistence/nosql/persistence/api/src/main/java/org/apache/polaris/persistence/nosql/api/backend/BackendConfiguration.java
+++
b/persistence/nosql/persistence/api/src/main/java/org/apache/polaris/persistence/nosql/api/backend/BackendConfiguration.java
@@ -25,12 +25,12 @@ import java.util.Optional;
import org.apache.polaris.immutables.PolarisImmutable;
/** Polaris persistence backend configuration. */
-@ConfigMapping(prefix = "polaris.persistence.backend")
+@ConfigMapping(prefix = "polaris.persistence.nosql")
@JsonSerialize(as = ImmutableBuildableBackendConfiguration.class)
@JsonDeserialize(as = ImmutableBuildableBackendConfiguration.class)
public interface BackendConfiguration {
/** Name of the persistence backend to use. */
- Optional<String> type();
+ Optional<String> backend();
@PolarisImmutable
interface BuildableBackendConfiguration extends BackendConfiguration {
@@ -39,6 +39,6 @@ public interface BackendConfiguration {
}
@Override
- Optional<String> type();
+ Optional<String> backend();
}
}
diff --git a/persistence/nosql/persistence/benchmark/NOTES.md
b/persistence/nosql/persistence/benchmark/NOTES.md
index ef8bc95fe..05928e43d 100644
--- a/persistence/nosql/persistence/benchmark/NOTES.md
+++ b/persistence/nosql/persistence/benchmark/NOTES.md
@@ -28,14 +28,14 @@ podman run --rm -ti \
```bash
./gradlew :polaris-persistence-nosql-benchmark:jmhJar && java \
- -Dpolaris.persistence.backend.type=InMemory \
+ -Dpolaris.persistence.nosql.backend=InMemory \
-jar
persistence/benchmark/build/libs/polaris-persistence-nosql-benchmark-1.0.0-incubating-SNAPSHOT-jmh.jar
```
```bash
./gradlew :polaris-persistence-nosql-benchmark:jmhJar && java \
- -Dpolaris.persistence.backend.type=MongoDb \
-
-Dpolaris.persistence.backend.mongodb.connection-string=mongodb://localhost:27017/
\
- -Dpolaris.persistence.backend.mongodb.database-name=test \
+ -Dpolaris.persistence.nosql.backend=MongoDb \
+
-Dpolaris.persistence.nosql.mongodb.connection-string=mongodb://localhost:27017/
\
+ -Dpolaris.persistence.nosql.mongodb.database-name=test \
-jar
persistence/benchmark/build/libs/polaris-persistence-nosql-benchmark-1.0.0-incubating-SNAPSHOT-jmh.jar
```
diff --git a/persistence/nosql/persistence/cdi/quarkus/build.gradle.kts
b/persistence/nosql/persistence/cdi/quarkus/build.gradle.kts
new file mode 100644
index 000000000..bc6138b10
--- /dev/null
+++ b/persistence/nosql/persistence/cdi/quarkus/build.gradle.kts
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+plugins {
+ id("org.kordamp.gradle.jandex")
+ id("polaris-server")
+}
+
+description = "Polaris NoSQL persistence, providers for Quarkus."
+
+dependencies {
+ implementation(project(":polaris-persistence-nosql-cdi-common"))
+ implementation(project(":polaris-persistence-nosql-api"))
+ implementation(project(":polaris-persistence-nosql-inmemory"))
+ implementation(project(":polaris-persistence-nosql-mongodb"))
+ implementation(project(":polaris-idgen-api"))
+ runtimeOnly(project(":polaris-nodes-impl"))
+ runtimeOnly(project(":polaris-nodes-store-nosql"))
+ runtimeOnly(project(":polaris-persistence-nosql-realms-impl"))
+ runtimeOnly(project(":polaris-persistence-nosql-realms-store-nosql"))
+ runtimeOnly(project(":polaris-async-vertx"))
+ runtimeOnly(project(":polaris-idgen-impl"))
+ runtimeOnly(project(":polaris-persistence-nosql-authz-impl"))
+ runtimeOnly(project(":polaris-persistence-nosql-authz-store-nosql"))
+
+ compileOnly(platform(libs.micrometer.bom))
+ compileOnly("io.micrometer:micrometer-core")
+ compileOnly(platform(libs.opentelemetry.instrumentation.bom.alpha))
+
compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations")
+
+ compileOnly(libs.smallrye.config.core)
+
+ compileOnly(project(":polaris-immutables"))
+ annotationProcessor(project(":polaris-immutables", configuration =
"processor"))
+
+ implementation(platform(libs.quarkus.bom))
+ implementation("io.quarkus:quarkus-core")
+ implementation("io.quarkus:quarkus-mongodb-client")
+ runtimeOnly("io.quarkus:quarkus-micrometer")
+
+ implementation(libs.jakarta.ws.rs.api)
+
+ implementation(libs.guava)
+ implementation(libs.slf4j.api)
+
+ compileOnly(libs.jakarta.annotation.api)
+ compileOnly(libs.jakarta.validation.api)
+ compileOnly(libs.jakarta.inject.api)
+ compileOnly(libs.jakarta.enterprise.cdi.api)
+}
diff --git
a/persistence/nosql/persistence/db/inmemory/src/main/java/org/apache/polaris/persistence/nosql/inmemory/InMemoryConfiguration.java
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/BackendBuilder.java
similarity index 80%
copy from
persistence/nosql/persistence/db/inmemory/src/main/java/org/apache/polaris/persistence/nosql/inmemory/InMemoryConfiguration.java
copy to
persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/BackendBuilder.java
index 5131f9c90..74b0af36f 100644
---
a/persistence/nosql/persistence/db/inmemory/src/main/java/org/apache/polaris/persistence/nosql/inmemory/InMemoryConfiguration.java
+++
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/BackendBuilder.java
@@ -16,9 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.polaris.persistence.nosql.inmemory;
+package org.apache.polaris.persistence.nosql.quarkus.backend;
-import io.smallrye.config.ConfigMapping;
+import org.apache.polaris.persistence.nosql.api.backend.Backend;
-@ConfigMapping(prefix = "polaris.persistence.backend.inmemory")
-public interface InMemoryConfiguration {}
+interface BackendBuilder {
+ Backend buildBackend();
+}
diff --git
a/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/BackendProvider.java
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/BackendProvider.java
new file mode 100644
index 000000000..0787c40ba
--- /dev/null
+++
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/BackendProvider.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.polaris.persistence.nosql.quarkus.backend;
+
+import static java.lang.String.format;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Any;
+import jakarta.enterprise.inject.Instance;
+import jakarta.enterprise.inject.Produces;
+import java.util.Optional;
+import org.apache.polaris.persistence.nosql.api.backend.Backend;
+import org.apache.polaris.persistence.nosql.api.backend.BackendConfiguration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@ApplicationScoped
+class BackendProvider {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(BackendProvider.class);
+
+ @Produces
+ @ApplicationScoped
+ @NotObserved
+ Backend backend(
+ BackendConfiguration backendConfiguration, @Any Instance<BackendBuilder>
backendBuilders) {
+
+ var backendName =
+ backendConfiguration
+ .backend()
+ .orElseThrow(
+ () ->
+ new IllegalStateException(
+ "Mandatory configuration option
polaris.persistence.nosql.backend is missing!"));
+
+ var backendBuilder =
backendBuilders.select(BackendType.Literal.of(backendName));
+ if (!backendBuilder.isResolvable()) {
+ throw new IllegalStateException(
+ format(
+ "Backend '%s' provided in configuration
polaris.persistence.nosql.backend is not available. Available backends: %s",
+ backendName,
+ backendBuilders
+ .handlesStream()
+ .map(
+ h ->
+ h.getBean().getQualifiers().stream()
+ .filter(q -> q instanceof BackendType)
+ .map(BackendType.class::cast)
+ .findFirst()
+ .map(BackendType::value))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .toList()));
+ }
+ if (backendBuilder.isAmbiguous()) {
+ throw new IllegalStateException(
+ format(
+ "Multiple implementations match the backend name '%s' provided
in configuration polaris.persistence.nosql.backend is not available. All
available backends: %s",
+ backendName,
+ backendBuilders
+ .handlesStream()
+ .map(
+ h ->
+ h.getBean().getQualifiers().stream()
+ .filter(q -> q instanceof BackendType)
+ .map(BackendType.class::cast)
+ .findFirst()
+ .map(BackendType::value))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .toList()));
+ }
+ var builder = backendBuilder.get();
+
+ var backend = builder.buildBackend();
+ try {
+ var setupSchemaResult = backend.setupSchema().orElse("");
+ LOGGER.info("Opened new persistence backend '{}' {}", backend.type(),
setupSchemaResult);
+
+ return builder.buildBackend();
+ } catch (Exception e) {
+ try {
+ backend.close();
+ } catch (Exception e2) {
+ e.addSuppressed(e2);
+ }
+ throw e;
+ }
+ }
+}
diff --git
a/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/BackendType.java
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/BackendType.java
new file mode 100644
index 000000000..1a675cdda
--- /dev/null
+++
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/BackendType.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.polaris.persistence.nosql.quarkus.backend;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import jakarta.enterprise.util.AnnotationLiteral;
+import jakarta.inject.Qualifier;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Target({TYPE, METHOD, PARAMETER, FIELD})
+@Retention(RUNTIME)
+@Documented
+@Qualifier
+public @interface BackendType {
+ /** Gets the store type. */
+ String value();
+
+ /** Supports inline instantiation of the {@link BackendType} qualifier. */
+ final class Literal extends AnnotationLiteral<BackendType> implements
BackendType {
+
+ private final String value;
+
+ private Literal(String value) {
+ this.value = value;
+ }
+
+ public static Literal of(String value) {
+ return new Literal(value);
+ }
+
+ @Override
+ public String value() {
+ return value;
+ }
+ }
+}
diff --git
a/persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/InMemoryBackendBuilder.java
similarity index 55%
copy from
persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
copy to
persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/InMemoryBackendBuilder.java
index 5ff744822..5a3ea9626 100644
---
a/persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
+++
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/InMemoryBackendBuilder.java
@@ -16,22 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.polaris.persistence.nosql.mongodb;
+package org.apache.polaris.persistence.nosql.quarkus.backend;
-import io.smallrye.config.ConfigMapping;
-import java.util.Optional;
+import jakarta.enterprise.context.ApplicationScoped;
+import org.apache.polaris.persistence.nosql.api.backend.Backend;
+import org.apache.polaris.persistence.nosql.inmemory.InMemoryBackendFactory;
+import org.apache.polaris.persistence.nosql.inmemory.InMemoryConfiguration;
-/** Polaris persistence, MongoDB backend specific configuration. */
-@ConfigMapping(prefix = "polaris.persistence.backend.mongodb")
-public interface MongoDbConfiguration {
- Optional<String> connectionString();
-
- Optional<String> databaseName();
-
- /**
- * Optionally enable realm-deletion using a prefix-delete.
- *
- * <p>Prefix-deletion is disabled by default.
- */
- Optional<Boolean> allowPrefixDeletion();
+@BackendType(InMemoryBackendFactory.NAME)
+@ApplicationScoped
+class InMemoryBackendBuilder implements BackendBuilder {
+ @Override
+ public Backend buildBackend() {
+ var factory = new InMemoryBackendFactory();
+ return factory.buildBackend(factory.buildConfiguration(new
InMemoryConfiguration() {}));
+ }
}
diff --git
a/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/MongoDbBackendBuilder.java
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/MongoDbBackendBuilder.java
new file mode 100644
index 000000000..9a3b7615b
--- /dev/null
+++
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/MongoDbBackendBuilder.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.polaris.persistence.nosql.quarkus.backend;
+
+import com.mongodb.client.MongoClient;
+import io.quarkus.arc.Arc;
+import io.quarkus.mongodb.runtime.MongoClientBeanUtil;
+import io.quarkus.mongodb.runtime.MongoClients;
+import jakarta.enterprise.context.Dependent;
+import jakarta.inject.Inject;
+import org.apache.polaris.persistence.nosql.api.backend.Backend;
+import org.apache.polaris.persistence.nosql.mongodb.MongoDbBackendConfig;
+import org.apache.polaris.persistence.nosql.mongodb.MongoDbBackendFactory;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+@BackendType(MongoDbBackendFactory.NAME)
+@Dependent
+class MongoDbBackendBuilder implements BackendBuilder {
+ @Inject
+ @ConfigProperty(name = "quarkus.mongodb.database", defaultValue = "polaris")
+ String databaseName;
+
+ @Override
+ public Backend buildBackend() {
+ MongoClients mongoClients =
Arc.container().instance(MongoClients.class).get();
+ MongoClient client =
+
mongoClients.createMongoClient(MongoClientBeanUtil.DEFAULT_MONGOCLIENT_NAME);
+
+ var config = new MongoDbBackendConfig(databaseName, client, true, false);
+
+ return new MongoDbBackendFactory().buildBackend(config);
+ }
+}
diff --git
a/persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/NotObserved.java
similarity index 57%
copy from
persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
copy to
persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/NotObserved.java
index 5ff744822..adcc92b52 100644
---
a/persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
+++
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/NotObserved.java
@@ -16,22 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.polaris.persistence.nosql.mongodb;
+package org.apache.polaris.persistence.nosql.quarkus.backend;
-import io.smallrye.config.ConfigMapping;
-import java.util.Optional;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
-/** Polaris persistence, MongoDB backend specific configuration. */
-@ConfigMapping(prefix = "polaris.persistence.backend.mongodb")
-public interface MongoDbConfiguration {
- Optional<String> connectionString();
+import jakarta.inject.Qualifier;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
- Optional<String> databaseName();
-
- /**
- * Optionally enable realm-deletion using a prefix-delete.
- *
- * <p>Prefix-deletion is disabled by default.
- */
- Optional<Boolean> allowPrefixDeletion();
-}
+@Target({TYPE, METHOD, PARAMETER, FIELD})
+@Retention(RUNTIME)
+@Documented
+@Qualifier
+public @interface NotObserved {}
diff --git
a/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/ObservingBackend.java
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/ObservingBackend.java
new file mode 100644
index 000000000..f6c053cda
--- /dev/null
+++
b/persistence/nosql/persistence/cdi/quarkus/src/main/java/org/apache/polaris/persistence/nosql/quarkus/backend/ObservingBackend.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.polaris.persistence.nosql.quarkus.backend;
+
+import io.micrometer.core.annotation.Counted;
+import io.micrometer.core.annotation.Timed;
+import io.micrometer.core.instrument.Meter;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.config.MeterFilter;
+import io.opentelemetry.instrumentation.annotations.SpanAttribute;
+import io.opentelemetry.instrumentation.annotations.WithSpan;
+import jakarta.annotation.Nonnull;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.inject.Default;
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Singleton;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import org.apache.polaris.ids.api.IdGenerator;
+import org.apache.polaris.ids.api.MonotonicClock;
+import org.apache.polaris.persistence.nosql.api.Persistence;
+import org.apache.polaris.persistence.nosql.api.PersistenceParams;
+import org.apache.polaris.persistence.nosql.api.backend.Backend;
+import org.apache.polaris.persistence.nosql.api.backend.FetchedObj;
+import org.apache.polaris.persistence.nosql.api.backend.PersistId;
+import org.apache.polaris.persistence.nosql.api.backend.WriteObj;
+import org.apache.polaris.persistence.nosql.api.obj.ObjRef;
+import org.apache.polaris.persistence.nosql.api.ref.Reference;
+
+/** Provides telemetry and tracing for all persistence backend operations. */
+@ApplicationScoped
+@Default
+public class ObservingBackend implements Backend {
+ public static final String TELEMETRY_PREFIX = "polaris.persistence.nosql";
+
+ private final Backend backend;
+
+ public ObservingBackend(@NotObserved Backend backend) {
+ this.backend = backend;
+ }
+
+ @Nonnull
+ @Override
+ public String type() {
+ return backend.type();
+ }
+
+ @Nonnull
+ @Override
+ public Persistence newPersistence(
+ Function<Backend, Backend> backendWrapper,
+ @Nonnull PersistenceParams persistenceParams,
+ String realmId,
+ MonotonicClock monotonicClock,
+ IdGenerator idGenerator) {
+ return backend.newPersistence(
+ backendWrapper, persistenceParams, realmId, monotonicClock,
idGenerator);
+ }
+
+ @Override
+ public boolean supportsRealmDeletion() {
+ return backend.supportsRealmDeletion();
+ }
+
+ @Override
+ public void close() throws Exception {
+ backend.close();
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".setupSchema")
+ @Counted(TELEMETRY_PREFIX + ".setupSchema")
+ @Timed(value = TELEMETRY_PREFIX + ".setupSchema", histogram = true)
+ @Override
+ public Optional<String> setupSchema() {
+ return backend.setupSchema();
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".deleteRealms")
+ @Counted(TELEMETRY_PREFIX + ".deleteRealms")
+ @Timed(value = TELEMETRY_PREFIX + ".deleteRealms", histogram = true)
+ @Override
+ public void deleteRealms(@SpanAttribute("realms") Set<String> realmIds) {
+ backend.deleteRealms(realmIds);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".batchDeleteRefs")
+ @Counted(TELEMETRY_PREFIX + ".batchDeleteRefs")
+ @Timed(value = TELEMETRY_PREFIX + ".batchDeleteRefs", histogram = true)
+ @Override
+ public void batchDeleteRefs(Map<String, Set<String>> realmRefs) {
+ backend.batchDeleteRefs(realmRefs);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".batchDeleteObjs")
+ @Counted(TELEMETRY_PREFIX + ".batchDeleteObjs")
+ @Timed(value = TELEMETRY_PREFIX + ".batchDeleteObjs", histogram = true)
+ @Override
+ public void batchDeleteObjs(Map<String, Set<PersistId>> realmObjs) {
+ backend.batchDeleteObjs(realmObjs);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".scanBackend")
+ @Counted(TELEMETRY_PREFIX + ".scanBackend")
+ @Timed(value = TELEMETRY_PREFIX + ".scanBackend", histogram = true, longTask
= true)
+ @Override
+ public void scanBackend(
+ @Nonnull ReferenceScanCallback referenceConsumer, @Nonnull
ObjScanCallback objConsumer) {
+ backend.scanBackend(referenceConsumer, objConsumer);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".createReference")
+ @Counted(TELEMETRY_PREFIX + ".createReference")
+ @Timed(value = TELEMETRY_PREFIX + ".createReference", histogram = true)
+ @Override
+ public boolean createReference(
+ @SpanAttribute("realm-id") @Nonnull String realmId, @Nonnull Reference
newRef) {
+ return backend.createReference(realmId, newRef);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".createReferencesSilent")
+ @Counted(TELEMETRY_PREFIX + ".createReferencesSilent")
+ @Timed(value = TELEMETRY_PREFIX + ".createReferencesSilent", histogram =
true)
+ @Override
+ public void createReferences(
+ @SpanAttribute("realm-id") @Nonnull String realmId, @Nonnull
List<Reference> newRefs) {
+ backend.createReferences(realmId, newRefs);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".updateReference")
+ @Counted(TELEMETRY_PREFIX + ".updateReference")
+ @Timed(value = TELEMETRY_PREFIX + ".updateReference", histogram = true)
+ @Override
+ public boolean updateReference(
+ @SpanAttribute("realm-id") @Nonnull String realmId,
+ @Nonnull Reference updatedRef,
+ @Nonnull Optional<ObjRef> expectedPointer) {
+ return backend.updateReference(realmId, updatedRef, expectedPointer);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".fetchReference")
+ @Counted(TELEMETRY_PREFIX + ".fetchReference")
+ @Timed(value = TELEMETRY_PREFIX + ".fetchReference", histogram = true)
+ @Nonnull
+ @Override
+ public Reference fetchReference(
+ @SpanAttribute("realm-id") @Nonnull String realmId, @Nonnull String
name) {
+ return backend.fetchReference(realmId, name);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".fetch")
+ @Counted(TELEMETRY_PREFIX + ".fetch")
+ @Timed(value = TELEMETRY_PREFIX + ".fetch", histogram = true)
+ @Nonnull
+ @Override
+ public Map<PersistId, FetchedObj> fetch(
+ @SpanAttribute("realm-id") @Nonnull String realmId, @Nonnull
Set<PersistId> ids) {
+ return backend.fetch(realmId, ids);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".write")
+ @Counted(TELEMETRY_PREFIX + ".write")
+ @Timed(value = TELEMETRY_PREFIX + ".write", histogram = true)
+ @Override
+ public void write(
+ @SpanAttribute("realm-id") @Nonnull String realmId, @Nonnull
List<WriteObj> writes) {
+ backend.write(realmId, writes);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".delete")
+ @Counted(TELEMETRY_PREFIX + ".delete")
+ @Timed(value = TELEMETRY_PREFIX + ".delete", histogram = true)
+ @Override
+ public void delete(
+ @SpanAttribute("realm-id") @Nonnull String realmId, @Nonnull
Set<PersistId> ids) {
+ backend.delete(realmId, ids);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".conditionalInsert")
+ @Counted(TELEMETRY_PREFIX + ".conditionalInsert")
+ @Timed(value = TELEMETRY_PREFIX + ".conditionalInsert", histogram = true)
+ @Override
+ public boolean conditionalInsert(
+ @SpanAttribute("realm-id") @Nonnull String realmId,
+ String objTypeId,
+ @Nonnull PersistId persistId,
+ long createdAtMicros,
+ @Nonnull String versionToken,
+ @Nonnull byte[] serializedValue) {
+ return backend.conditionalInsert(
+ realmId, objTypeId, persistId, createdAtMicros, versionToken,
serializedValue);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".conditionalUpdate")
+ @Counted(TELEMETRY_PREFIX + ".conditionalUpdate")
+ @Timed(value = TELEMETRY_PREFIX + ".conditionalUpdate", histogram = true)
+ @Override
+ public boolean conditionalUpdate(
+ @SpanAttribute("realm-id") @Nonnull String realmId,
+ String objTypeId,
+ @Nonnull PersistId persistId,
+ long createdAtMicros,
+ @Nonnull String updateToken,
+ @Nonnull String expectedToken,
+ @Nonnull byte[] serializedValue) {
+ return backend.conditionalUpdate(
+ realmId,
+ objTypeId,
+ persistId,
+ createdAtMicros,
+ updateToken,
+ expectedToken,
+ serializedValue);
+ }
+
+ @WithSpan(TELEMETRY_PREFIX + ".conditionalDelete")
+ @Counted(TELEMETRY_PREFIX + ".conditionalDelete")
+ @Timed(value = TELEMETRY_PREFIX + ".conditionalDelete", histogram = true)
+ @Override
+ public boolean conditionalDelete(
+ @SpanAttribute("realm-id") @Nonnull String realmId,
+ @Nonnull PersistId persistId,
+ @Nonnull String expectedToken) {
+ return backend.conditionalDelete(realmId, persistId, expectedToken);
+ }
+
+ @Produces
+ @Singleton
+ public MeterFilter renameApplicationMeters() {
+ return new MeterFilter() {
+ @Override
+ @Nonnull
+ public Meter.Id map(@Nonnull Meter.Id id) {
+ var tags = id.getTags();
+ var tag = Tag.of("class", ObservingBackend.class.getName());
+ if (tags.contains(tag)) {
+ // drop the 'class' tag, but leave the 'method' tag
+ tags = tags.stream().filter(t ->
!"class".equals(t.getKey())).toList();
+ return id.replaceTags(tags);
+ }
+ return id;
+ }
+ };
+ }
+}
diff --git
a/persistence/nosql/persistence/cdi/weld/src/main/java/org/apache/polaris/persistence/nosql/weld/BackendProvider.java
b/persistence/nosql/persistence/cdi/weld/src/main/java/org/apache/polaris/persistence/nosql/weld/BackendProvider.java
index 7e3d62792..97c7a0bd2 100644
---
a/persistence/nosql/persistence/cdi/weld/src/main/java/org/apache/polaris/persistence/nosql/weld/BackendProvider.java
+++
b/persistence/nosql/persistence/cdi/weld/src/main/java/org/apache/polaris/persistence/nosql/weld/BackendProvider.java
@@ -40,7 +40,7 @@ class BackendProvider {
var factory =
backendConfiguration
- .type()
+ .backend()
.map(BackendLoader::findFactoryByName)
.map(
f -> {
@@ -56,7 +56,7 @@ class BackendProvider {
return r;
} catch (IllegalStateException e) {
throw new RuntimeException(
- "Backend factory type is configured using the
configuration option polaris.persistence.backend.type - available are: "
+ "Backend factory type is configured using the
configuration option polaris.persistence.nosql.backend - available are: "
+ BackendLoader.availableFactories()
.map(BackendFactory::name)
.collect(Collectors.joining(", ")),
diff --git
a/persistence/nosql/persistence/cdi/weld/src/testFixtures/java/org/apache/polaris/persistence/nosql/weld/CdiTestingProviders.java
b/persistence/nosql/persistence/cdi/weld/src/testFixtures/java/org/apache/polaris/persistence/nosql/weld/CdiTestingProviders.java
index 28511a552..132737e0f 100644
---
a/persistence/nosql/persistence/cdi/weld/src/testFixtures/java/org/apache/polaris/persistence/nosql/weld/CdiTestingProviders.java
+++
b/persistence/nosql/persistence/cdi/weld/src/testFixtures/java/org/apache/polaris/persistence/nosql/weld/CdiTestingProviders.java
@@ -47,7 +47,7 @@ public class CdiTestingProviders {
@Produces
@ApplicationScoped
BackendConfiguration backendConfiguration() {
- return
BackendConfiguration.BuildableBackendConfiguration.builder().type("InMemory").build();
+ return
BackendConfiguration.BuildableBackendConfiguration.builder().backend("InMemory").build();
}
@Produces
diff --git a/persistence/nosql/persistence/correctness/README.md
b/persistence/nosql/persistence/correctness/README.md
index 382164c65..b68947622 100644
--- a/persistence/nosql/persistence/correctness/README.md
+++ b/persistence/nosql/persistence/correctness/README.md
@@ -29,8 +29,8 @@ The `correctnessManualTest` task however is meant to be run
_manually_ against a
providing the necessary backend configuration via system properties. For
example:
```bash
./gradlew :polaris-persistence-nosql-correctness:correctnessManualTest \
- -Dpolaris.persistence.backend=MongoDb \
- -Dpolaris.persistence.backend.mongodb.uri=mongodb://localhost:27017/test
- -Dpolaris.persistence.backend.mongodb.databaseName=polaris_mongo_test
+ -Dpolaris.persistence.nosql=MongoDb \
+ -Dpolaris.persistence.nosql.mongodb.uri=mongodb://localhost:27017/test
+ -Dpolaris.persistence.nosql.mongodb.databaseName=polaris_mongo_test
```
See also the Docker Compose example in the [`docker`](../docker) directory.
diff --git
a/persistence/nosql/persistence/db/inmemory/src/main/java/org/apache/polaris/persistence/nosql/inmemory/InMemoryConfiguration.java
b/persistence/nosql/persistence/db/inmemory/src/main/java/org/apache/polaris/persistence/nosql/inmemory/InMemoryConfiguration.java
index 5131f9c90..db9317f5a 100644
---
a/persistence/nosql/persistence/db/inmemory/src/main/java/org/apache/polaris/persistence/nosql/inmemory/InMemoryConfiguration.java
+++
b/persistence/nosql/persistence/db/inmemory/src/main/java/org/apache/polaris/persistence/nosql/inmemory/InMemoryConfiguration.java
@@ -20,5 +20,5 @@ package org.apache.polaris.persistence.nosql.inmemory;
import io.smallrye.config.ConfigMapping;
-@ConfigMapping(prefix = "polaris.persistence.backend.inmemory")
+@ConfigMapping(prefix = "polaris.persistence.nosql.inmemory")
public interface InMemoryConfiguration {}
diff --git
a/persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
b/persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
index 5ff744822..4f2e9535e 100644
---
a/persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
+++
b/persistence/nosql/persistence/db/mongodb/src/main/java/org/apache/polaris/persistence/nosql/mongodb/MongoDbConfiguration.java
@@ -22,7 +22,7 @@ import io.smallrye.config.ConfigMapping;
import java.util.Optional;
/** Polaris persistence, MongoDB backend specific configuration. */
-@ConfigMapping(prefix = "polaris.persistence.backend.mongodb")
+@ConfigMapping(prefix = "polaris.persistence.nosql.mongodb")
public interface MongoDbConfiguration {
Optional<String> connectionString();
diff --git
a/persistence/nosql/persistence/docker/mongodb-3-nodes/docker-compose.yml
b/persistence/nosql/persistence/docker/mongodb-3-nodes/docker-compose.yml
index 723cd17d4..f392644da 100644
--- a/persistence/nosql/persistence/docker/mongodb-3-nodes/docker-compose.yml
+++ b/persistence/nosql/persistence/docker/mongodb-3-nodes/docker-compose.yml
@@ -24,9 +24,9 @@
#
# ./gradlew :polaris-persistence-nosql-benchmark:jmhJar && \
# java \
-# -Dpolaris.persistence.backend.type=MongoDb \
-#
-Dpolaris.persistence.backend.mongodb.connection-string=mongodb://127.0.0.1:27017,127.0.0.1:27018,127.0.0.1:27019/?replicaSet=rs0
\
-# -Dpolaris.persistence.backend.mongodb.database-name=test \
+# -Dpolaris.persistence.nosql.backend=MongoDb \
+#
-Dpolaris.persistence.nosql.mongodb.connection-string=mongodb://127.0.0.1:27017,127.0.0.1:27018,127.0.0.1:27019/?replicaSet=rs0
\
+# -Dpolaris.persistence.nosql.mongodb.database-name=test \
# -jar
persistence/benchmark/build/libs/polaris-persistence-nosql-benchmark-1.0.0-incubating-SNAPSHOT-jmh.jar
#
# MAKE SURE TO ADD
diff --git
a/persistence/nosql/persistence/standalone/src/main/java/org/apache/polaris/persistence/nosql/standalone/PersistenceConfigurer.java
b/persistence/nosql/persistence/standalone/src/main/java/org/apache/polaris/persistence/nosql/standalone/PersistenceConfigurer.java
index 733b5018f..de2ce42fe 100644
---
a/persistence/nosql/persistence/standalone/src/main/java/org/apache/polaris/persistence/nosql/standalone/PersistenceConfigurer.java
+++
b/persistence/nosql/persistence/standalone/src/main/java/org/apache/polaris/persistence/nosql/standalone/PersistenceConfigurer.java
@@ -53,11 +53,11 @@ public class PersistenceConfigurer {
this.name =
backendConfiguration
- .type()
+ .backend()
.orElseThrow(
() ->
new IllegalArgumentException(
- "No backend name provided, for example via the system
property 'polaris.persistence.backend.type', available backend names: "
+ "No backend name provided, for example via the system
property 'polaris.persistence.nosql.backend', available backend names: "
+ BackendLoader.availableFactories()
.map(BackendFactory::name)
.toList()));