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()));


Reply via email to