diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts index 7bdc40545c..45267c888b 100644 --- a/bom/build.gradle.kts +++ b/bom/build.gradle.kts @@ -55,6 +55,7 @@ dependencies { api(project(":polaris-persistence-nosql-api")) api(project(":polaris-persistence-nosql-impl")) api(project(":polaris-persistence-nosql-benchmark")) + api(project(":polaris-persistence-nosql-correctness")) api(project(":polaris-persistence-nosql-standalone")) api(project(":polaris-persistence-nosql-testextension")) diff --git a/gradle/projects.main.properties b/gradle/projects.main.properties index 1f7bd19cc5..52f5f72a87 100644 --- a/gradle/projects.main.properties +++ b/gradle/projects.main.properties @@ -70,6 +70,7 @@ polaris-persistence-nosql-realms-spi=persistence/nosql/realms/spi polaris-persistence-nosql-api=persistence/nosql/persistence/api 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-standalone=persistence/nosql/persistence/standalone polaris-persistence-nosql-testextension=persistence/nosql/persistence/testextension polaris-persistence-nosql-varint=persistence/nosql/persistence/varint diff --git a/persistence/nosql/persistence/correctness/README.md b/persistence/nosql/persistence/correctness/README.md new file mode 100644 index 0000000000..382164c657 --- /dev/null +++ b/persistence/nosql/persistence/correctness/README.md @@ -0,0 +1,36 @@ + + +# Polaris NoSQL Persistence Correctness Tests + +Collection of tests that use JUnit to verify the correctness of the persistence implementation. + +`:polaris-persistence-nosql-correctness` contains a couple of test suites with test tasks named `correctnessTest-XYZ`, +where `XYZ` determines the database kind. Those tests use backends on the Java heap or using testcontainers and +are dependents of Gradle's `check` task. + +The `correctnessManualTest` task however is meant to be run _manually_ against a separate database/cluster setup, +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 +``` +See also the Docker Compose example in the [`docker`](../docker) directory. diff --git a/persistence/nosql/persistence/correctness/build.gradle.kts b/persistence/nosql/persistence/correctness/build.gradle.kts new file mode 100644 index 0000000000..a5bef2fc66 --- /dev/null +++ b/persistence/nosql/persistence/correctness/build.gradle.kts @@ -0,0 +1,86 @@ +/* + * 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 correctness tests" + +dependencies { + testFixturesApi(project(":polaris-persistence-nosql-api")) + testFixturesApi(project(":polaris-persistence-nosql-impl")) + testFixturesApi(project(":polaris-persistence-nosql-testextension")) + + testFixturesImplementation(project(":polaris-idgen-api")) + testFixturesImplementation(project(":polaris-idgen-impl")) + testFixturesImplementation(project(":polaris-idgen-spi")) + + testFixturesApi(platform(libs.jackson.bom)) + testFixturesApi("com.fasterxml.jackson.core:jackson-annotations") + testFixturesApi("com.fasterxml.jackson.core:jackson-databind") + + testFixturesImplementation(libs.jakarta.annotation.api) + testFixturesImplementation(libs.jakarta.validation.api) + + testFixturesCompileOnly(project(":polaris-immutables")) + testFixturesAnnotationProcessor(project(":polaris-immutables", configuration = "processor")) +} + +// Map of `:polaris-persistence-*` projects implementing the database specific parts to the list of +// test-backend names to be exercised. +var dbs = mapOf("inmemory" to listOf("InMemory"), "mongodb" to listOf("MongoDb")) + +testing { + suites { + dbs.forEach { prjDbs -> + val prj = prjDbs.key + prjDbs.value.forEach { db -> + val dbTaskName = db.replace("-", "") + register("correctness${dbTaskName}Test") { + dependencies { + runtimeOnly(project(":polaris-persistence-nosql-$prj")) + implementation(testFixtures(project(":polaris-persistence-nosql-$prj"))) + } + + targets.all { testTask.configure { systemProperty("polaris.testBackend.name", db) } } + } + } + } + + // Test suite to manually run the correctness tests against an externally configured database + register("correctnessManualTest") { + dependencies { + implementation(project(":polaris-persistence-nosql-standalone")) + + // Pass system properties starting with `polaris.` down to the manually executed test(s) so + // they can setup the backend via + // `o.a.p.persistence.api.BackendConfigurer.defaultBackendConfigurer` using smallrye-config. + targets.all { + testTask.configure { + System.getProperties() + .filter { p -> p.key.toString().startsWith("polaris.") } + .forEach { p -> systemProperty(p.key.toString(), p.value) } + } + } + } + } + } +} diff --git a/persistence/nosql/persistence/correctness/src/correctnessInMemoryTest/java/org/apache/polaris/persistence/nosql/correctness/TestInMemoryCorrectness.java b/persistence/nosql/persistence/correctness/src/correctnessInMemoryTest/java/org/apache/polaris/persistence/nosql/correctness/TestInMemoryCorrectness.java new file mode 100644 index 0000000000..ba1aa12ab9 --- /dev/null +++ b/persistence/nosql/persistence/correctness/src/correctnessInMemoryTest/java/org/apache/polaris/persistence/nosql/correctness/TestInMemoryCorrectness.java @@ -0,0 +1,21 @@ +/* + * 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.correctness; + +public class TestInMemoryCorrectness extends BaseCorrectness {} diff --git a/persistence/nosql/persistence/correctness/src/correctnessManualTest/java/org/apache/polaris/persistence/nosql/correctness/TestCorrectness.java b/persistence/nosql/persistence/correctness/src/correctnessManualTest/java/org/apache/polaris/persistence/nosql/correctness/TestCorrectness.java new file mode 100644 index 0000000000..cd8a32a231 --- /dev/null +++ b/persistence/nosql/persistence/correctness/src/correctnessManualTest/java/org/apache/polaris/persistence/nosql/correctness/TestCorrectness.java @@ -0,0 +1,31 @@ +/* + * 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.correctness; + +import org.apache.polaris.persistence.nosql.api.backend.Backend; +import org.apache.polaris.persistence.nosql.standalone.PersistenceConfigurer; + +public class TestCorrectness extends BaseCorrectness { + @Override + protected Backend setupBackend() { + var configurer = PersistenceConfigurer.defaultBackendConfigurer(); + var factory = configurer.buildBackendFactory(); + return configurer.buildBackendFromConfiguration(factory); + } +} diff --git a/persistence/nosql/persistence/correctness/src/correctnessMongoDbTest/java/org/apache/polaris/persistence/nosql/correctness/TestMongoDbCorrectness.java b/persistence/nosql/persistence/correctness/src/correctnessMongoDbTest/java/org/apache/polaris/persistence/nosql/correctness/TestMongoDbCorrectness.java new file mode 100644 index 0000000000..4d17f4a18f --- /dev/null +++ b/persistence/nosql/persistence/correctness/src/correctnessMongoDbTest/java/org/apache/polaris/persistence/nosql/correctness/TestMongoDbCorrectness.java @@ -0,0 +1,21 @@ +/* + * 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.correctness; + +public class TestMongoDbCorrectness extends BaseCorrectness {} diff --git a/persistence/nosql/persistence/correctness/src/testFixtures/java/org/apache/polaris/persistence/nosql/correctness/BaseCorrectness.java b/persistence/nosql/persistence/correctness/src/testFixtures/java/org/apache/polaris/persistence/nosql/correctness/BaseCorrectness.java new file mode 100644 index 0000000000..9a75c65c1c --- /dev/null +++ b/persistence/nosql/persistence/correctness/src/testFixtures/java/org/apache/polaris/persistence/nosql/correctness/BaseCorrectness.java @@ -0,0 +1,343 @@ +/* + * 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.correctness; + +import static java.util.Objects.requireNonNull; +import static java.util.function.Function.identity; +import static org.apache.polaris.persistence.nosql.api.obj.ObjRef.OBJ_REF_SERIALIZER; +import static org.apache.polaris.persistence.nosql.api.obj.ObjRef.objRef; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.fail; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalLong; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.IntFunction; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import org.apache.polaris.ids.api.MonotonicClock; +import org.apache.polaris.ids.impl.MonotonicClockImpl; +import org.apache.polaris.ids.impl.SnowflakeIdGeneratorFactory; +import org.apache.polaris.ids.spi.IdGeneratorSource; +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.commit.CommitException; +import org.apache.polaris.persistence.nosql.api.commit.RetryTimeoutException; +import org.apache.polaris.persistence.nosql.api.index.IndexContainer; +import org.apache.polaris.persistence.nosql.api.index.IndexKey; +import org.apache.polaris.persistence.nosql.api.obj.ObjRef; +import org.apache.polaris.persistence.nosql.impl.commits.CommitterWithStats; +import org.apache.polaris.persistence.nosql.testextension.BackendTestFactory; +import org.apache.polaris.persistence.nosql.testextension.BackendTestFactoryLoader; +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.junit.jupiter.InjectSoftAssertions; +import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; +import org.assertj.core.util.Streams; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +@ExtendWith(SoftAssertionsExtension.class) +public abstract class BaseCorrectness { + @InjectSoftAssertions protected SoftAssertions soft; + + private static BackendTestFactory backendTestFactory; + protected Backend backend; + protected Persistence persistence; + private MonotonicClock clock; + + @BeforeEach + protected void init() throws Exception { + this.backend = setupBackend(); + + var info = backend.setupSchema().orElse(""); + System.out.printf("Opened new persistence backend '%s' %s%n", backend.type(), info); + + clock = MonotonicClockImpl.newDefaultInstance(); + + var idGenerator = + new SnowflakeIdGeneratorFactory() + .buildIdGenerator( + Map.of(), + new IdGeneratorSource() { + @Override + public int nodeId() { + return 42; + } + + @Override + public long currentTimeMillis() { + return clock.currentTimeMillis(); + } + }); + this.persistence = + backend.newPersistence( + identity(), + PersistenceParams.BuildablePersistenceParams.builder().build(), + "42", + clock, + idGenerator); + } + + protected Backend setupBackend() throws Exception { + var backendName = System.getProperty("polaris.testBackend.name"); + + if (backendTestFactory == null) { + backendTestFactory = BackendTestFactoryLoader.findFactoryByName(backendName); + backendTestFactory.start(); + } + + return backendTestFactory.createNewBackend(); + } + + @AfterEach + protected void cleanup() throws Exception { + backend.close(); + clock.close(); + } + + @AfterAll + static void tearDownBackendTestFactory() throws Exception { + if (backendTestFactory != null) { + backendTestFactory.stop(); + } + } + + @ParameterizedTest + @CsvSource(value = {"1,3000", "3,2000"}) + public void correctness(int numThreads, int numCommitsPerThread) { + verifyCommitsCorrectness(numThreads, numCommitsPerThread); + } + + protected void verifyCommitsCorrectness(int numThreads, int numCommitsPerThread) { + var refName = "ref-" + UUID.randomUUID(); + persistence.createReference(refName, Optional.empty()); + var committer = + (CommitterWithStats) + persistence.createCommitter(refName, SimpleCommitObj.class, String.class); + + var totalRetries = new AtomicInteger(0); + var totalTimeouts = new AtomicInteger(0); + var totalSleepTimeMillis = new AtomicLong(0); + var totalDurationNanos = new AtomicLong(0); + + try (var executor = Executors.newFixedThreadPool(numThreads)) { + var futures = new ArrayList>(); + for (int t = 0; t < numThreads; t++) { + var thread = t; + futures.add( + CompletableFuture.runAsync( + () -> { + var retryTimeouts = 0; + for (int c = 0; c < numCommitsPerThread; c++) { + try { + committer.commit( + (state, refObjSupplier) -> { + var refObj = refObjSupplier.get(); + var commitSeqPerThread = + refObj + .map(SimpleCommitObj::commitSeqPerThread) + .map(ArrayList::new) + .orElseGet( + () -> { + var initial = new ArrayList(numThreads); + for (int i = 0; i < numThreads; i++) { + initial.add(-1); + } + return initial; + }); + + commitSeqPerThread.set(thread, commitSeqPerThread.get(thread) + 1); + + return state.commitResult( + "foo", + ImmutableSimpleCommitObj.builder() + .commitSeqPerThread(commitSeqPerThread) + .thread(thread), + refObj); + }, + (result, retries, sleepTimeMillis, durationNanos) -> { + totalRetries.addAndGet(retries); + totalSleepTimeMillis.addAndGet(sleepTimeMillis); + totalDurationNanos.addAndGet(durationNanos); + switch (result) { + case SUCCESS, CONFLICT, ERROR -> {} + case TIMEOUT -> totalTimeouts.incrementAndGet(); + default -> fail("Unexpected/invalid result %s", result); + } + }); + } catch (RetryTimeoutException e) { + if (retryTimeouts++ >= 10) { + throw new RuntimeException(e); + } + } catch (CommitException e) { + throw new RuntimeException(e); + } + } + }, + executor)); + } + + soft.assertThatCode( + () -> + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .get(5, TimeUnit.MINUTES)) + .doesNotThrowAnyException(); + } + + System.out.printf( + """ + Threads: %d + Commits per thread: %d + Retry timeouts: %d + Total retries: %d + Total sleep time: %s + Total work duration: %s + """, + numThreads, + numCommitsPerThread, + totalTimeouts.get(), + totalRetries.get(), + Duration.ofMillis(totalSleepTimeMillis.get()), + Duration.ofSeconds( + TimeUnit.NANOSECONDS.toSeconds(totalDurationNanos.get()), + totalDurationNanos.get() % TimeUnit.SECONDS.toNanos(1))); + + soft.assertThat(totalTimeouts) + .describedAs( + "Retry timeouts: %d, retries total: %d", totalTimeouts.get(), totalRetries.get()) + .hasValue(0); + + var commitThreadSeqDesc = + IntStream.range(0, numThreads) + .boxed() + .collect(Collectors.toMap(t -> t, t -> new AtomicInteger(numCommitsPerThread - 1))); + + Streams.stream( + persistence.commits().commitLog(refName, OptionalLong.empty(), SimpleCommitObj.class)) + .forEach( + co -> { + var expected = commitThreadSeqDesc.get(co.thread()); + var expectedSeq = expected.get(); + var commitSeq = co.commitSeqPerThread().get(co.thread()); + soft.assertThat(commitSeq) + .describedAs( + "Descending (normal order) - thread %d, commit %d", co.thread(), commitSeq) + .isEqualTo(expectedSeq); + expected.set(commitSeq - 1); + }); + + soft.assertThat(commitThreadSeqDesc.values().stream().map(AtomicInteger::get)) + .describedAs("Descending (normal order)") + .containsExactlyElementsOf(IntStream.range(0, numThreads).mapToObj(x -> -1).toList()); + + var commitThreadSeq = + IntStream.range(0, numThreads) + .boxed() + .collect(Collectors.toMap(t -> t, t -> new AtomicInteger(0))); + + Streams.stream(persistence.commits().commitLogReversed(refName, -1L, SimpleCommitObj.class)) + .forEach( + co -> { + var expected = commitThreadSeq.get(co.thread()); + var expectedSeq = expected.get(); + var commitSeq = co.commitSeqPerThread().get(co.thread()); + soft.assertThat(commitSeq) + .describedAs( + "Ascending (reverse order) - thread %d, commit %d", co.thread(), commitSeq) + .isEqualTo(expectedSeq); + expected.set(commitSeq + 1); + }); + + soft.assertThat(commitThreadSeq.values().stream().map(AtomicInteger::get)) + .describedAs("Ascending (reverse order)") + .containsExactlyElementsOf( + IntStream.range(0, numThreads).mapToObj(x -> numCommitsPerThread).toList()); + } + + @ParameterizedTest + @MethodSource + public void bigIndex(int numCommits, int additionsPerCommit) throws Exception { + var refName = "ref-" + UUID.randomUUID(); + persistence.createReference(refName, Optional.empty()); + var committer = + (CommitterWithStats) + persistence.createCommitter(refName, SimpleCommitObj.class, String.class); + + var objIdGen = (IntFunction) i -> objRef("foo", i, 1); + var keyGen = (IntFunction) i -> IndexKey.key("my-table." + i + ".suffix"); + + var table = 0; + for (var i = 0; i < numCommits; i++, table += additionsPerCommit) { + + var off = table; + + committer.commit( + (state, refObjSupplier) -> { + var refObj = refObjSupplier.get(); + + var index = + refObj + .map( + r -> + requireNonNull(r.index()) + .asUpdatableIndex(persistence, OBJ_REF_SERIALIZER)) + .orElseGet( + () -> IndexContainer.newUpdatableIndex(persistence, OBJ_REF_SERIALIZER)); + + for (var t = off; t < off + additionsPerCommit; t++) { + index.put(keyGen.apply(t), objIdGen.apply(t)); + } + + var newRefObj = + ImmutableSimpleCommitObj.builder() + .index(index.toIndexed("idx-", state::writeOrReplace)) + .thread(0); + + return state.commitResult("result", newRefObj, refObj); + }); + + var head = persistence.fetchReferenceHead(refName, SimpleCommitObj.class); + assertThat(head).isNotEmpty(); + var idx = requireNonNull(head.get().index()).indexForRead(persistence, OBJ_REF_SERIALIZER); + soft.assertThat(IntStream.range(0, table + additionsPerCommit)) + .allMatch(t -> objIdGen.apply(t).equals(idx.get(keyGen.apply(t)))); + } + } + + static Stream bigIndex() { + return Stream.of(arguments(3, 10), arguments(50, 1000)); + } +} diff --git a/persistence/nosql/persistence/correctness/src/testFixtures/java/org/apache/polaris/persistence/nosql/correctness/SimpleCommitObj.java b/persistence/nosql/persistence/correctness/src/testFixtures/java/org/apache/polaris/persistence/nosql/correctness/SimpleCommitObj.java new file mode 100644 index 0000000000..ade14289b3 --- /dev/null +++ b/persistence/nosql/persistence/correctness/src/testFixtures/java/org/apache/polaris/persistence/nosql/correctness/SimpleCommitObj.java @@ -0,0 +1,65 @@ +/* + * 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.correctness; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import jakarta.annotation.Nullable; +import java.util.List; +import org.apache.polaris.immutables.PolarisImmutable; +import org.apache.polaris.persistence.nosql.api.exceptions.UnknownOperationResultException; +import org.apache.polaris.persistence.nosql.api.index.IndexContainer; +import org.apache.polaris.persistence.nosql.api.obj.AbstractObjType; +import org.apache.polaris.persistence.nosql.api.obj.BaseCommitObj; +import org.apache.polaris.persistence.nosql.api.obj.ObjRef; +import org.apache.polaris.persistence.nosql.api.obj.ObjType; + +@PolarisImmutable +@JsonSerialize(as = ImmutableSimpleCommitObj.class) +@JsonDeserialize(as = ImmutableSimpleCommitObj.class) +public interface SimpleCommitObj extends BaseCommitObj { + ObjType TYPE = new SimpleCommitObjType(); + + @Override + default ObjType type() { + return TYPE; + } + + /** + * Record the commit-numbers of all threads in a list, indexed by thread-number. Using a list here + * allows the correctness checks to work even if the database driver reported an {@linkplain + * UnknownOperationResultException unknown operation result}. + */ + List commitSeqPerThread(); + + int thread(); + + @JsonInclude(JsonInclude.Include.NON_NULL) + @Nullable + IndexContainer index(); + + final class SimpleCommitObjType extends AbstractObjType { + public SimpleCommitObjType() { + super("s-c", "simple commit", SimpleCommitObj.class); + } + } + + interface Builder extends BaseCommitObj.Builder {} +} diff --git a/persistence/nosql/persistence/correctness/src/testFixtures/resources/META-INF/services/org.apache.polaris.persistence.nosql.api.obj.ObjType b/persistence/nosql/persistence/correctness/src/testFixtures/resources/META-INF/services/org.apache.polaris.persistence.nosql.api.obj.ObjType new file mode 100644 index 0000000000..c8cb74f4bc --- /dev/null +++ b/persistence/nosql/persistence/correctness/src/testFixtures/resources/META-INF/services/org.apache.polaris.persistence.nosql.api.obj.ObjType @@ -0,0 +1,20 @@ +# +# 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. +# + +org.apache.polaris.persistence.nosql.correctness.SimpleCommitObj$SimpleCommitObjType diff --git a/persistence/nosql/persistence/correctness/src/testFixtures/resources/logback-test.xml b/persistence/nosql/persistence/correctness/src/testFixtures/resources/logback-test.xml new file mode 100644 index 0000000000..aafa701dc4 --- /dev/null +++ b/persistence/nosql/persistence/correctness/src/testFixtures/resources/logback-test.xml @@ -0,0 +1,30 @@ + + + + + + + %date{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + +