From 960419387e78d09042ab5cfda59ece00a26c9546 Mon Sep 17 00:00:00 2001 From: Jesus The Hun Date: Tue, 19 Oct 2021 18:34:31 +0200 Subject: [PATCH] Fix SimpleCouchbaseRepository not taking into account the default consistency. (#1256) * Create a test repository dedicated to default consistency. * Test default consistency being used with CouchbaseRepository findAll. Related tickets #1243 * Take template default consistency into account when building the query scan consistency. Closes #1243 * Clean imports. * Query scan consistency is null unless specified. * Apply query scan consistency fix to reactive repository. * Reactive repository for default scan consistency tests. * Test reactive repository default scan consistency. * Avoid consistency default behavior duplication. * Tests request plus consistency now use deleteAll with request plus. This is to make sure other tests won't see documents from pending index mutations --- .../support/SimpleCouchbaseRepository.java | 21 ++-- .../SimpleReactiveCouchbaseRepository.java | 22 ++-- .../AirportDefaultConsistencyRepository.java | 32 ++++++ .../couchbase/domain/AirportRepository.java | 4 - ...veAirportDefaultConsistencyRepository.java | 42 +++++++ ...chbaseRepositoryQueryIntegrationTests.java | 96 +++++++++------- ...chbaseRepositoryQueryIntegrationTests.java | 103 ++++++++++++++---- 7 files changed, 229 insertions(+), 91 deletions(-) create mode 100644 src/test/java/org/springframework/data/couchbase/domain/AirportDefaultConsistencyRepository.java create mode 100644 src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportDefaultConsistencyRepository.java diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java index c91235d4..44d2d39a 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleCouchbaseRepository.java @@ -16,14 +16,7 @@ package org.springframework.data.couchbase.repository.support; -import static org.springframework.data.couchbase.repository.support.Util.hasNonZeroVersionProperty; - -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - +import com.couchbase.client.java.query.QueryScanConsistency; import org.springframework.data.couchbase.core.CouchbaseOperations; import org.springframework.data.couchbase.core.query.Query; import org.springframework.data.couchbase.repository.CouchbaseRepository; @@ -36,7 +29,13 @@ import org.springframework.data.util.StreamUtils; import org.springframework.data.util.Streamable; import org.springframework.util.Assert; -import com.couchbase.client.java.query.QueryScanConsistency; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.springframework.data.couchbase.repository.support.Util.hasNonZeroVersionProperty; /** * Repository base implementation for Couchbase. @@ -44,6 +43,7 @@ import com.couchbase.client.java.query.QueryScanConsistency; * @author Michael Nitschinger * @author Mark Paluch * @author Jens Schauder + * @author Jonathan Massuchetti */ public class SimpleCouchbaseRepository implements CouchbaseRepository { @@ -190,7 +190,8 @@ public class SimpleCouchbaseRepository implements CouchbaseRepository implements ReactiveCouchbaseRepository { @@ -206,7 +205,8 @@ public class SimpleReactiveCouchbaseRepository implements ReactiveCouchba } private QueryScanConsistency buildQueryScanConsistency() { - QueryScanConsistency scanConsistency = QueryScanConsistency.NOT_BOUNDED; + QueryScanConsistency scanConsistency = null; + if (crudMethodMetadata.getScanConsistency() != null) { scanConsistency = crudMethodMetadata.getScanConsistency().query(); } diff --git a/src/test/java/org/springframework/data/couchbase/domain/AirportDefaultConsistencyRepository.java b/src/test/java/org/springframework/data/couchbase/domain/AirportDefaultConsistencyRepository.java new file mode 100644 index 00000000..a7aa8530 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/domain/AirportDefaultConsistencyRepository.java @@ -0,0 +1,32 @@ +/* + * Copyright 2017-2021 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.data.couchbase.domain; + +import org.springframework.data.couchbase.repository.CouchbaseRepository; +import org.springframework.stereotype.Repository; + +/** + * Airport repository for testing default consistency
+ * + * @author Jonathan Massuchetti + */ +@Repository +public interface AirportDefaultConsistencyRepository extends CouchbaseRepository { + + Airport iata(String iata); + +} diff --git a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java index 0265fe7c..d849d32e 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java @@ -43,10 +43,6 @@ import com.couchbase.client.java.query.QueryScanConsistency; @Repository public interface AirportRepository extends CouchbaseRepository { - // NOT_BOUNDED to test ScanConsistency - // @ScanConsistency(query = QueryScanConsistency.NOT_BOUNDED) - Airport iata(String iata); - @Override @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) List findAll(); diff --git a/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportDefaultConsistencyRepository.java b/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportDefaultConsistencyRepository.java new file mode 100644 index 00000000..2ffbfab4 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportDefaultConsistencyRepository.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017-2021 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.data.couchbase.domain; + +import com.couchbase.client.java.json.JsonArray; +import com.couchbase.client.java.query.QueryScanConsistency; +import org.springframework.data.couchbase.repository.Query; +import org.springframework.data.couchbase.repository.ScanConsistency; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.reactive.ReactiveSortingRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.ArrayList; + +/** + * template class for Reactive Couchbase operations + * + * @author Jonathan Massuchetti + */ +@Repository +public interface ReactiveAirportDefaultConsistencyRepository extends ReactiveSortingRepository { + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java index 72ec1bbe..c9b1af66 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java @@ -16,30 +16,9 @@ package org.springframework.data.couchbase.repository; -import static com.couchbase.client.java.query.QueryScanConsistency.NOT_BOUNDED; -import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS; -import static java.util.Arrays.asList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.stream.Collectors; - +import com.couchbase.client.core.error.CouchbaseException; +import com.couchbase.client.core.error.IndexExistsException; +import com.couchbase.client.java.query.QueryScanConsistency; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -57,15 +36,7 @@ import org.springframework.data.couchbase.core.RemoveResult; import org.springframework.data.couchbase.core.query.N1QLExpression; import org.springframework.data.couchbase.core.query.Query; import org.springframework.data.couchbase.core.query.QueryCriteria; -import org.springframework.data.couchbase.domain.Address; -import org.springframework.data.couchbase.domain.Airport; -import org.springframework.data.couchbase.domain.AirportRepository; -import org.springframework.data.couchbase.domain.Iata; -import org.springframework.data.couchbase.domain.NaiveAuditorAware; -import org.springframework.data.couchbase.domain.Person; -import org.springframework.data.couchbase.domain.PersonRepository; -import org.springframework.data.couchbase.domain.User; -import org.springframework.data.couchbase.domain.UserRepository; +import org.springframework.data.couchbase.domain.*; import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider; import org.springframework.data.couchbase.repository.auditing.EnableCouchbaseAuditing; import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories; @@ -82,9 +53,24 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import com.couchbase.client.core.error.CouchbaseException; -import com.couchbase.client.core.error.IndexExistsException; -import com.couchbase.client.java.query.QueryScanConsistency; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.stream.Collectors; + +import static com.couchbase.client.java.query.QueryScanConsistency.NOT_BOUNDED; +import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE; /** * Repository tests @@ -92,6 +78,7 @@ import com.couchbase.client.java.query.QueryScanConsistency; * @author Michael Nitschinger * @author Michael Reiche * @author Jens Schauder + * @author Jonathan Massuchetti */ @SpringJUnitConfig(CouchbaseRepositoryQueryIntegrationTests.Config.class) @IgnoreWhen(missesCapabilities = Capabilities.QUERY, clusterTypes = ClusterType.MOCKED) @@ -101,6 +88,9 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr @Autowired AirportRepository airportRepository; + @Autowired + AirportDefaultConsistencyRepository airportDefaultConsistencyRepository; + @Autowired UserRepository userRepository; @Autowired CouchbaseTemplate couchbaseTemplate; @@ -212,9 +202,9 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr Airport airport2 = null; for (int i = 1; i <= 100; i++) { // set version == 0 so save() will be an upsert, not a replace - Airport saved = airportRepository.save(vie.clearVersion()); + Airport saved = airportDefaultConsistencyRepository.save(vie.clearVersion()); try { - airport2 = airportRepository.iata(saved.getIata()); + airport2 = airportDefaultConsistencyRepository.iata(saved.getIata()); if (airport2 == null) { break; } @@ -227,14 +217,14 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr assertEquals(vie.getId(), removeResult.getId()); assertTrue(removeResult.getCas() != 0); assertTrue(removeResult.getMutationToken().isPresent()); - Airport airport3 = airportRepository.iata(vie.getIata()); + Airport airport3 = airportDefaultConsistencyRepository.iata(vie.getIata()); assertNull(airport3, "should have been removed"); } } assertNull(airport2, "airport2 should have likely been null at least once"); - Airport saved = airportRepository.save(vie.clearVersion()); + Airport saved = airportDefaultConsistencyRepository.save(vie.clearVersion()); couchbaseTemplate.findByQuery(Airport.class).withConsistency(REQUEST_PLUS).all(); - airport2 = airportRepository.iata(vie.getIata()); + airport2 = airportDefaultConsistencyRepository.iata(vie.getIata()); RemoveResult removeResult = couchbaseTemplate.removeById().one(saved.getId()); assertNotNull(airport2, "airport2 should have been found"); } @@ -244,7 +234,7 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigRequestPlus.class); // the Config class has been modified, these need to be loaded again CouchbaseTemplate couchbaseTemplateRP = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE); - AirportRepository airportRepositoryRP = (AirportRepository) ac.getBean("airportRepository"); + AirportDefaultConsistencyRepository airportRepositoryRP = (AirportDefaultConsistencyRepository) ac.getBean("airportDefaultConsistencyRepository"); // save() followed by query with NOT_BOUNDED will result in not finding the document Airport vie = new Airport("airports::vie", "vie", "low9"); @@ -277,6 +267,28 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr assertFalse(!airports.isEmpty(), "airports should have been empty"); } + @Test + public void saveNotBoundedRequestPlusWithDefaultRepository() { + ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigRequestPlus.class); + // the Config class has been modified, these need to be loaded again + CouchbaseTemplate couchbaseTemplateRP = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE); + AirportDefaultConsistencyRepository airportRepositoryRP = (AirportDefaultConsistencyRepository) ac.getBean("airportDefaultConsistencyRepository"); + + List sizeBeforeTest = airportRepositoryRP.findAll(); + assertEquals(0, sizeBeforeTest.size()); + + for (int i = 1; i <= 100; i++) { + Airport vie = new Airport("airports::vie" + i, "vie" + i, "low9"); + airportRepositoryRP.save(vie); + } + + List allSaved = airportRepositoryRP.findAll(); + + airportRepository.deleteAll(); + + assertEquals(100, allSaved.size()); + } + @Test void findByTypeAlias() { Airport vie = null; diff --git a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java index ed07c651..1823f8ff 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java @@ -16,12 +16,29 @@ package org.springframework.data.couchbase.repository; -import static java.util.Arrays.asList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - +import com.couchbase.client.java.query.QueryScanConsistency; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.data.couchbase.CouchbaseClientFactory; +import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; +import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; +import org.springframework.data.couchbase.domain.Airport; +import org.springframework.data.couchbase.domain.ReactiveAirportDefaultConsistencyRepository; +import org.springframework.data.couchbase.domain.ReactiveAirportRepository; +import org.springframework.data.couchbase.domain.ReactiveUserRepository; +import org.springframework.data.couchbase.domain.User; +import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories; +import org.springframework.data.couchbase.util.Capabilities; +import org.springframework.data.couchbase.util.ClusterType; +import org.springframework.data.couchbase.util.IgnoreWhen; +import org.springframework.data.couchbase.util.JavaIntegrationTests; +import org.springframework.data.domain.PageRequest; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -37,30 +54,17 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.stream.Collectors; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.DataRetrievalFailureException; -import org.springframework.data.couchbase.CouchbaseClientFactory; -import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration; -import org.springframework.data.couchbase.domain.Airport; -import org.springframework.data.couchbase.domain.ReactiveAirportRepository; -import org.springframework.data.couchbase.domain.ReactiveUserRepository; -import org.springframework.data.couchbase.domain.User; -import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories; -import org.springframework.data.couchbase.util.Capabilities; -import org.springframework.data.couchbase.util.ClusterType; -import org.springframework.data.couchbase.util.IgnoreWhen; -import org.springframework.data.couchbase.util.JavaIntegrationTests; -import org.springframework.data.domain.PageRequest; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE; /** * template class for Reactive Couchbase operations * * @author Michael Nitschinger * @author Michael Reiche + * @author Jonathan Massuchetti */ @SpringJUnitConfig(ReactiveCouchbaseRepositoryQueryIntegrationTests.Config.class) @IgnoreWhen(missesCapabilities = Capabilities.QUERY, clusterTypes = ClusterType.MOCKED) @@ -249,6 +253,28 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr } } + @Test + public void saveNotBoundedRequestPlusWithDefaultRepository() { + ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigRequestPlus.class); + // the Config class has been modified, these need to be loaded again + ReactiveCouchbaseTemplate couchbaseTemplateRP = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE); + ReactiveAirportDefaultConsistencyRepository airportRepositoryRP = (ReactiveAirportDefaultConsistencyRepository) ac.getBean("reactiveAirportDefaultConsistencyRepository"); + + List sizeBeforeTest = airportRepositoryRP.findAll().collectList().block(); + assertEquals(0, sizeBeforeTest.size()); + + for (int i = 1; i <= 100; i++) { + Airport vie = new Airport("airports::vie" + i, "vie" + i, "low9"); + airportRepositoryRP.save(vie).block(); + } + + List allSaved = airportRepositoryRP.findAll().collectList().block(); + + airportRepository.deleteAll().block(); + + assertEquals(100, allSaved.size()); + } + @Configuration @EnableReactiveCouchbaseRepositories("org.springframework.data.couchbase") static class Config extends AbstractCouchbaseConfiguration { @@ -275,4 +301,33 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr } + @Configuration + @EnableReactiveCouchbaseRepositories("org.springframework.data.couchbase") + static class ConfigRequestPlus extends AbstractCouchbaseConfiguration { + + @Override + public String getConnectionString() { + return connectionString(); + } + + @Override + public String getUserName() { + return config().adminUsername(); + } + + @Override + public String getPassword() { + return config().adminPassword(); + } + + @Override + public String getBucketName() { + return bucketName(); + } + + @Override + public QueryScanConsistency getDefaultConsistency() { + return REQUEST_PLUS; + } + } }