diff --git a/pom.xml b/pom.xml index d87478c95..10c44c1c3 100644 --- a/pom.xml +++ b/pom.xml @@ -381,6 +381,10 @@ org.apache.maven.plugins maven-surefire-plugin + + **/*Test.java + **/*Tests.java + --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED diff --git a/src/main/asciidoc/reference/configure-data-settings.adoc b/src/main/asciidoc/reference/configure-data-settings.adoc index 4fdf6323f..0122b2e2b 100644 --- a/src/main/asciidoc/reference/configure-data-settings.adoc +++ b/src/main/asciidoc/reference/configure-data-settings.adoc @@ -23,7 +23,7 @@ class ApplicationConfig extends AbstractAerospikeDataConfiguration { @Override protected void configureDataSettings(AerospikeDataSettings.AerospikeDataSettingsBuilder builder) { builder.createIndexesOnStartup(true); - builder.indexCacheRefreshFrequencySeconds(3600); + builder.indexCacheRefreshSeconds(3600); builder.queryMaxRecords(10000L); } } @@ -58,12 +58,19 @@ Create secondary indexes specified using `@Indexed` annotation on startup. *Default*: `true`. [[configure-data-settings.index-cache-refresh-frequency-seconds]] -=== indexCacheRefreshFrequencySeconds +=== indexCacheRefreshSeconds Automatically refresh indexes cache every seconds. *Default*: `3600`. +[[configure-data-settings.server-version-refresh-frequency-seconds]] +=== serverVersionRefreshSeconds + +Automatically refresh cached server version every seconds. + +*Default*: `3600`. + [[configure-data-settings.query-max-records]] === queryMaxRecords @@ -71,6 +78,13 @@ Limit amount of results returned by server. Non-positive value means no limit. *Default*: `10 000`. +[[configure-data-settings.batch-write-size]] +=== batchWriteSize + +Maximum batch size for batch write operations. Non-positive value means no limit. + +*Default*: `100`. + [[configure-data-settings.keep-original-key-types]] === keepOriginalKeyTypes diff --git a/src/main/java/org/springframework/data/aerospike/config/AbstractAerospikeDataConfiguration.java b/src/main/java/org/springframework/data/aerospike/config/AbstractAerospikeDataConfiguration.java index 437743342..f16b35393 100644 --- a/src/main/java/org/springframework/data/aerospike/config/AbstractAerospikeDataConfiguration.java +++ b/src/main/java/org/springframework/data/aerospike/config/AbstractAerospikeDataConfiguration.java @@ -33,6 +33,7 @@ import org.springframework.data.aerospike.query.cache.IndexRefresher; import org.springframework.data.aerospike.query.cache.IndexesCacheUpdater; import org.springframework.data.aerospike.query.cache.InternalIndexOperations; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; @Slf4j @Configuration @@ -43,9 +44,10 @@ public AerospikeTemplate aerospikeTemplate(IAerospikeClient aerospikeClient, MappingAerospikeConverter mappingAerospikeConverter, AerospikeMappingContext aerospikeMappingContext, AerospikeExceptionTranslator aerospikeExceptionTranslator, - QueryEngine queryEngine, IndexRefresher indexRefresher) { + QueryEngine queryEngine, IndexRefresher indexRefresher, + ServerVersionSupport serverVersionSupport) { return new AerospikeTemplate(aerospikeClient, nameSpace(), mappingAerospikeConverter, - aerospikeMappingContext, aerospikeExceptionTranslator, queryEngine, indexRefresher); + aerospikeMappingContext, aerospikeExceptionTranslator, queryEngine, indexRefresher, serverVersionSupport); } @Bean(name = "aerospikeQueryEngine") @@ -75,22 +77,23 @@ public AerospikePersistenceEntityIndexCreator aerospikePersistenceEntityIndexCre } @Bean(name = "aerospikeIndexRefresher") - public IndexRefresher indexRefresher(IAerospikeClient aerospikeClient, IndexesCacheUpdater indexesCacheUpdater) { + public IndexRefresher indexRefresher(IAerospikeClient aerospikeClient, IndexesCacheUpdater indexesCacheUpdater, + ServerVersionSupport serverVersionSupport) { IndexRefresher refresher = new IndexRefresher(aerospikeClient, aerospikeClient.getInfoPolicyDefault(), - new InternalIndexOperations(new IndexInfoParser()), indexesCacheUpdater); + new InternalIndexOperations(new IndexInfoParser()), indexesCacheUpdater, serverVersionSupport); refresher.refreshIndexes(); - int refreshFrequency = aerospikeDataSettings().getIndexCacheRefreshFrequencySeconds(); + int refreshFrequency = aerospikeDataSettings().getIndexCacheRefreshSeconds(); processCacheRefreshFrequency(refreshFrequency, refresher); - log.debug("AerospikeDataSettings.indexCacheRefreshFrequencySeconds: {}", refreshFrequency); + log.debug("AerospikeDataSettings.indexCacheRefreshSeconds: {}", refreshFrequency); return refresher; } - private void processCacheRefreshFrequency(int indexCacheRefreshFrequencySeconds, IndexRefresher indexRefresher) { - if (indexCacheRefreshFrequencySeconds <= 0) { + private void processCacheRefreshFrequency(int indexCacheRefreshSeconds, IndexRefresher indexRefresher) { + if (indexCacheRefreshSeconds <= 0) { log.info("Periodic index cache refreshing is not scheduled, interval ({}) is <= 0", - indexCacheRefreshFrequencySeconds); + indexCacheRefreshSeconds); } else { - indexRefresher.scheduleRefreshIndexes(indexCacheRefreshFrequencySeconds); + indexRefresher.scheduleRefreshIndexes(indexCacheRefreshSeconds); } } } diff --git a/src/main/java/org/springframework/data/aerospike/config/AbstractReactiveAerospikeDataConfiguration.java b/src/main/java/org/springframework/data/aerospike/config/AbstractReactiveAerospikeDataConfiguration.java index d0ed25c58..2a0a85df9 100644 --- a/src/main/java/org/springframework/data/aerospike/config/AbstractReactiveAerospikeDataConfiguration.java +++ b/src/main/java/org/springframework/data/aerospike/config/AbstractReactiveAerospikeDataConfiguration.java @@ -37,6 +37,7 @@ import org.springframework.data.aerospike.query.cache.IndexesCacheUpdater; import org.springframework.data.aerospike.query.cache.InternalIndexOperations; import org.springframework.data.aerospike.query.cache.ReactorIndexRefresher; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; /** * Configuration with beans needed for reactive stuff @@ -53,10 +54,11 @@ public ReactiveAerospikeTemplate reactiveAerospikeTemplate(MappingAerospikeConve AerospikeExceptionTranslator aerospikeExceptionTranslator, IAerospikeReactorClient aerospikeReactorClient, ReactorQueryEngine reactorQueryEngine, - ReactorIndexRefresher reactorIndexRefresher) { + ReactorIndexRefresher reactorIndexRefresher, + ServerVersionSupport serverVersionSupport) { return new ReactiveAerospikeTemplate(aerospikeReactorClient, nameSpace(), mappingAerospikeConverter, - aerospikeMappingContext, - aerospikeExceptionTranslator, reactorQueryEngine, reactorIndexRefresher); + aerospikeMappingContext, aerospikeExceptionTranslator, reactorQueryEngine, reactorIndexRefresher, + serverVersionSupport); } @Bean(name = "reactiveAerospikeQueryEngine") @@ -76,10 +78,11 @@ public ReactorQueryEngine reactorQueryEngine(IAerospikeReactorClient aerospikeRe @Bean(name = "reactiveAerospikeIndexRefresher") public ReactorIndexRefresher reactorIndexRefresher(IAerospikeReactorClient aerospikeReactorClient, - IndexesCacheUpdater indexesCacheUpdater) { + IndexesCacheUpdater indexesCacheUpdater, + ServerVersionSupport serverVersionSupport) { ReactorIndexRefresher refresher = new ReactorIndexRefresher(aerospikeReactorClient, aerospikeReactorClient.getInfoPolicyDefault(), - new InternalIndexOperations(new IndexInfoParser()), indexesCacheUpdater); + new InternalIndexOperations(new IndexInfoParser()), indexesCacheUpdater, serverVersionSupport); refresher.refreshIndexes().block(); return refresher; } @@ -103,8 +106,7 @@ protected ClientPolicy getClientPolicy() { public ReactiveAerospikePersistenceEntityIndexCreator aerospikePersistenceEntityIndexCreator( ObjectProvider aerospikeMappingContext, AerospikeIndexResolver aerospikeIndexResolver, - ObjectProvider template) - { + ObjectProvider template) { boolean indexesOnStartup = aerospikeDataSettings().isCreateIndexesOnStartup(); log.debug("AerospikeDataSettings.indexesOnStartup: {}", indexesOnStartup); return new ReactiveAerospikePersistenceEntityIndexCreator(aerospikeMappingContext, diff --git a/src/main/java/org/springframework/data/aerospike/config/AerospikeDataConfigurationSupport.java b/src/main/java/org/springframework/data/aerospike/config/AerospikeDataConfigurationSupport.java index e0c5b787a..c2709a1ca 100644 --- a/src/main/java/org/springframework/data/aerospike/config/AerospikeDataConfigurationSupport.java +++ b/src/main/java/org/springframework/data/aerospike/config/AerospikeDataConfigurationSupport.java @@ -17,6 +17,7 @@ import com.aerospike.client.AerospikeClient; import com.aerospike.client.Host; +import com.aerospike.client.IAerospikeClient; import com.aerospike.client.policy.ClientPolicy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.config.BeanDefinition; @@ -37,6 +38,7 @@ import org.springframework.data.aerospike.query.StatementBuilder; import org.springframework.data.aerospike.query.cache.IndexesCache; import org.springframework.data.aerospike.query.cache.IndexesCacheHolder; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.annotation.Persistent; import org.springframework.data.mapping.model.FieldNamingStrategy; import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy; @@ -119,6 +121,24 @@ public AerospikeIndexResolver aerospikeIndexResolver() { return new AerospikeIndexResolver(); } + @Bean(name = "aerospikeServerVersionSupport") + public ServerVersionSupport serverVersionSupport(IAerospikeClient aerospikeClient) { + ServerVersionSupport serverVersionSupport = new ServerVersionSupport(aerospikeClient); + int serverVersionRefreshFrequency = aerospikeDataSettings().getServerVersionRefreshSeconds(); + processServerVersionRefreshFrequency(serverVersionRefreshFrequency, serverVersionSupport); + return serverVersionSupport; + } + + private void processServerVersionRefreshFrequency(int serverVersionRefreshSeconds, + ServerVersionSupport serverVersionSupport) { + if (serverVersionRefreshSeconds <= 0) { + log.info("Periodic server version refreshing is not scheduled, interval ({}) is <= 0", + serverVersionRefreshSeconds); + } else { + serverVersionSupport.scheduleServerVersionRefresh(serverVersionRefreshSeconds); + } + } + protected Set> getInitialEntitySet() throws ClassNotFoundException { String basePackage = getMappingBasePackage(); Set> initialEntitySet = new HashSet<>(); diff --git a/src/main/java/org/springframework/data/aerospike/config/AerospikeDataSettings.java b/src/main/java/org/springframework/data/aerospike/config/AerospikeDataSettings.java index 56f183e02..2fa76f6a7 100644 --- a/src/main/java/org/springframework/data/aerospike/config/AerospikeDataSettings.java +++ b/src/main/java/org/springframework/data/aerospike/config/AerospikeDataSettings.java @@ -33,10 +33,16 @@ public class AerospikeDataSettings { boolean createIndexesOnStartup = true; @Builder.Default // Automatically refresh indexes cache every seconds - int indexCacheRefreshFrequencySeconds = 3600; + int indexCacheRefreshSeconds = 3600; + @Builder.Default + // Automatically refresh cached server version every seconds + int serverVersionRefreshSeconds = 3600; @Builder.Default // Limit amount of results returned by server. Non-positive value means no limit long queryMaxRecords = 10_000L; + @Builder.Default + // Maximum batch size for batch write operations + int batchWriteSize = 100; // Define how @Id fields (primary keys) and Map keys are stored: false - always as String, // true - preserve original type if supported @Builder.Default diff --git a/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java b/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java index 7346f085b..06a45c5ba 100644 --- a/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java +++ b/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java @@ -50,8 +50,7 @@ public interface AerospikeOperations { /** - * Return set name used for the given entityClass in the namespace configured for the AerospikeTemplate in - * use. + * Return set name used for the given entityClass in the namespace configured for the AerospikeTemplate in use. * * @param entityClass The class to get the set name for. * @return The set name used for the given entityClass. @@ -143,8 +142,8 @@ public interface AerospikeOperations { void saveAll(Iterable documents); /** - * Save multiple documents within the given set (overrides the default set associated with the documents) in one batch request. - * The policies are analogous to {@link #save(Object)}. + * Save multiple documents within the given set (overrides the default set associated with the documents) in one + * batch request. The policies are analogous to {@link #save(Object)}. *

* The order of returned results is preserved. The execution order is NOT preserved. *

@@ -221,7 +220,8 @@ public interface AerospikeOperations { void persist(T document, WritePolicy writePolicy); /** - * Persist a document within the given set (overrides the default set associated with the document) using specified WritePolicy. + * Persist a document within the given set (overrides the default set associated with the document) using specified + * WritePolicy. * * @param document The document to be persisted. Must not be {@literal null}. * @param writePolicy The Aerospike write policy for the inner Aerospike put operation. Must not be @@ -297,8 +297,8 @@ public interface AerospikeOperations { void updateAll(Iterable documents); /** - * Update multiple records within the given set (overrides the default set associated with the - * documents) in one batch request. The policies are analogous to {@link #update(Object)}. + * Update multiple records within the given set (overrides the default set associated with the documents) in one + * batch request. The policies are analogous to {@link #update(Object)}. *

* The order of returned results is preserved. The execution order is NOT preserved. *

@@ -317,7 +317,7 @@ public interface AerospikeOperations { * Truncate/Delete all records in the set determined by the given entityClass. * * @param entityClass The class to extract set name from. Must not be {@literal null}. - * @deprecated since 4.6.0, use deleteAll(Class entityClass) instead. + * @deprecated since 4.6.0, use {@link AerospikeOperations#deleteAll(Class)} instead. */ void delete(Class entityClass); @@ -327,7 +327,7 @@ public interface AerospikeOperations { * @param id The id of the record to delete. Must not be {@literal null}. * @param entityClass The class to extract set name from. Must not be {@literal null}. * @return whether the document existed on server before deletion. - * @deprecated since 4.6.0, use deleteById(Object id, Class entityClass) instead. + * @deprecated since 4.6.0, use {@link AerospikeOperations#deleteById(Object, Class)} instead. */ boolean delete(Object id, Class entityClass); @@ -367,14 +367,13 @@ public interface AerospikeOperations { boolean deleteById(Object id, String setName); /** - * Delete records by ids using a single batch delete operation, set name will be determined by - * the given entityClass. + * Delete records by ids using a single batch delete operation, set name will be determined by the given + * entityClass. *

* This operation requires Server version 6.0+. * * @param ids The ids of the records to be deleted. Must not be {@literal null}. - * @param entityClass The class to extract set name from. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from. Must not be {@literal null}. * @throws AerospikeException.BatchRecordArray if batch delete results contain errors * @throws org.springframework.dao.DataAccessException if batch operation failed (see * {@link DefaultAerospikeExceptionTranslator} for details) @@ -394,34 +393,13 @@ public interface AerospikeOperations { */ void deleteByIds(Iterable ids, String setName); - /** - * Batch delete records by ids. Set name will be determined by the given entityClass. - *

- * This operation requires Server version 6.0+. - * - * @param ids The ids of the records to be deleted. Must not be {@literal null}. - * @param entityClass The class to extract set name from. Must not be {@literal null}. - * @throws AerospikeException.BatchRecordArray if batch delete results contain errors or null records - */ - void deleteByIds(Collection ids, Class entityClass); - - /** - * Batch delete records by ids within the given set. - *

- * This operation requires Server version 6.0+. - * - * @param ids The ids of the records to be deleted. Must not be {@literal null}. - * @param setName Set name to use. - * @throws AerospikeException.BatchRecordArray if batch delete results contain errors or null records - */ - void deleteByIds(Collection ids, String setName); - /** * Perform a single batch delete for records from different sets. *

* This operation requires Server version 6.0+. * - * @param groupedKeys Keys grouped by document type. Must not be {@literal null}. + * @param groupedKeys Keys grouped by document type. Must not be {@literal null}, groupedKeys.getEntitiesKeys() must + * not be {@literal null}. * @throws AerospikeException.BatchRecordArray if batch delete results contain errors * @throws org.springframework.dao.DataAccessException if batch operation failed (see * {@link DefaultAerospikeExceptionTranslator} for details) @@ -443,20 +421,20 @@ public interface AerospikeOperations { void deleteAll(String setName); /** - * Find an existing record matching the document's class and id, add map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, add map values to the corresponding bins of the + * record and return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the record to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}. * @param values The Map of bin names and values to add. Must not be {@literal null}. * @return Modified record mapped to the document's class. */ T add(T document, Map values); /** - * Find an existing record matching the document's id and the given set name, add map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, add map values to the corresponding + * bins of the record and return the modified record mapped to the document's class. * - * @param document The document to get id from and to map the record to. Must not be - * {@literal null}. + * @param document The document to get id from and to map the record to. Must not be {@literal null}. * @param setName Set name to use. * @param values The Map of bin names and values to add. Must not be {@literal null}. * @return Modified record mapped to the document's class. @@ -464,10 +442,10 @@ public interface AerospikeOperations { T add(T document, String setName, Map values); /** - * Find an existing record matching the document's class and id, add specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, add specified value to the record's bin and return + * the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the record to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}. * @param binName Bin name to use add operation on. Must not be {@literal null}. * @param value The value to add. * @return Modified record mapped to the document's class. @@ -475,10 +453,10 @@ public interface AerospikeOperations { T add(T document, String binName, long value); /** - * Find an existing record matching the document's id and the given set name, add specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, add specified value to the record's + * bin and return the modified record mapped to the document's class. * - * @param document The document to get id from and to map the record to. Must not be - * {@literal null}. + * @param document The document to get id from and to map the record to. Must not be {@literal null}. * @param setName Set name to use. * @param binName Bin name to use add operation on. Must not be {@literal null}. * @param value The value to add. @@ -487,17 +465,18 @@ public interface AerospikeOperations { T add(T document, String setName, String binName, long value); /** - * Find an existing record matching the document's class and id, append map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, append map values to the corresponding bins of the + * record and return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the record to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}. * @param values The Map of bin names and values to append. Must not be {@literal null}. * @return Modified record mapped to the document's class. */ T append(T document, Map values); /** - * Find an existing record matching the document's id and the given set name, append map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, append map values to the corresponding + * bins of the record and return the modified record mapped to the document's class. * * @param document The document to get id from and to map the record to. Must not be {@literal null}. * @param setName Set name to use. @@ -507,10 +486,10 @@ public interface AerospikeOperations { T append(T document, String setName, Map values); /** - * Find an existing record matching the document's class and id, append specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, append specified value to the record's bin and + * return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the record to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}. * @param binName Bin name to use append operation on. * @param value The value to append. * @return Modified record mapped to the document's class. @@ -518,7 +497,8 @@ public interface AerospikeOperations { T append(T document, String binName, String value); /** - * Find an existing record matching the document's id and the given set name, append specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, append specified value to the record's + * bin and return the modified record mapped to the document's class. * * @param document The document to get id from and to map the record to. Must not be {@literal null}. * @param setName Set name to use. @@ -529,17 +509,18 @@ public interface AerospikeOperations { T append(T document, String setName, String binName, String value); /** - * Find an existing record matching the document's class and id, prepend map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, prepend map values to the corresponding bins of the + * record and return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the record to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}. * @param values The Map of bin names and values to prepend. Must not be {@literal null}. * @return Modified record mapped to the document's class. */ T prepend(T document, Map values); /** - * Find an existing record matching the document's id and the given set name, prepend map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, prepend map values to the + * corresponding bins of the record and return the modified record mapped to the document's class. * * @param document The document to get id from and to map the record to. Must not be {@literal null}. * @param setName Set name to use. @@ -549,10 +530,10 @@ public interface AerospikeOperations { T prepend(T document, String setName, Map values); /** - * Find an existing record matching the document's class and id, prepend specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, prepend specified value to the record's bin and + * return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the record to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}. * @param binName Bin name to use prepend operation on. * @param value The value to prepend. * @return Modified record mapped to the document's class. @@ -560,7 +541,8 @@ public interface AerospikeOperations { T prepend(T document, String binName, String value); /** - * Find an existing record matching the document's id and the given set name, prepend specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, prepend specified value to the + * record's bin and return the modified record mapped to the document's class. * * @param document The document to get id from and to map the record to. Must not be {@literal null}. * @param setName Set name to use. @@ -584,8 +566,7 @@ public interface AerospikeOperations { * The matching record will be mapped to the given entityClass. * * @param id The id of the record to find. Must not be {@literal null}. - * @param entityClass The class to extract set name from and to map the record to. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from and to map the record to. Must not be {@literal null}. * @return The document mapped to entityClass's type or null if nothing is found */ T findById(Object id, Class entityClass); @@ -596,8 +577,8 @@ public interface AerospikeOperations { * The matching record will be mapped to the given entityClass. * * @param id The id of the record to find. Must not be {@literal null}. - * @param entityClass The class to map the record to and to get entity properties from (such as expiration). Must not - * be {@literal null}. + * @param entityClass The class to map the record to and to get entity properties from (such as expiration). Must + * not be {@literal null}. * @param setName Set name to find the document from. * @return The record mapped to entityClass's type or null if document does not exist */ @@ -611,8 +592,7 @@ public interface AerospikeOperations { * @param id The id of the record to find. Must not be {@literal null}. * @param entityClass The class to extract set name from. Must not be {@literal null}. * @param targetClass The class to map the record to. Must not be {@literal null}. - * @return The record mapped to targetClass's type or null if document doesn't - * exist. + * @return The record mapped to targetClass's type or null if document doesn't exist. */ S findById(Object id, Class entityClass, Class targetClass); @@ -625,22 +605,18 @@ public interface AerospikeOperations { * @param entityClass The class to get entity properties from (such as expiration). Must not be {@literal null}. * @param targetClass The class to map the record to. Must not be {@literal null}. * @param setName Set name to find the document from. - * @return The record mapped to targetClass's type or null if document doesn't - * exist. + * @return The record mapped to targetClass's type or null if document doesn't exist. */ S findById(Object id, Class entityClass, Class targetClass, String setName); /** - * Find records by ids using a single batch read operation, set name will be determined by the - * given entityClass. + * Find records by ids using a single batch read operation, set name will be determined by the given entityClass. *

* The records will be mapped to the given entityClass. * * @param ids The ids of the documents to find. Must not be {@literal null}. - * @param entityClass The class to extract set name from and to map the records to. Must not be - * {@literal null}. - * @return The matching records mapped to entityClass's type, if no document - * exists, an empty list is returned. + * @param entityClass The class to extract set name from and to map the records to. Must not be {@literal null}. + * @return The matching records mapped to entityClass's type, if no document exists, an empty list is returned. */ List findByIds(Iterable ids, Class entityClass); @@ -657,8 +633,7 @@ public interface AerospikeOperations { List findByIds(Iterable ids, Class entityClass, String setName); /** - * Find records by ids using a single batch read operation, set name will be determined by the - * given entityClass. + * Find records by ids using a single batch read operation, set name will be determined by the given entityClass. *

* The records will be mapped to the given targetClass. * @@ -685,10 +660,11 @@ public interface AerospikeOperations { /** * Execute a single batch request to find several records, possibly from different sets. *

- * Aerospike provides functionality to get records from different sets in 1 batch request. This method receives - * keys grouped by document type as a parameter and returns Aerospike records mapped to documents grouped by type. + * Aerospike provides functionality to get records from different sets in 1 batch request. This method receives keys + * grouped by document type as a parameter and returns Aerospike records mapped to documents grouped by type. * - * @param groupedKeys Must not be {@literal null}. + * @param groupedKeys Keys grouped by document type. Must not be {@literal} null, groupedKeys.getEntitiesKeys() must + * not be {@literal null}. * @return grouped documents. */ GroupedEntities findByIds(GroupedKeys groupedKeys); @@ -731,8 +707,8 @@ Object findByIdUsingQuery(Object id, Class entityClass, Class targe * @param entityClass The class to extract set name from. Must not be {@literal null}. * @param targetClass The class to map the record to. * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). - * @return The matching records mapped to targetClass's type if provided - * (otherwise to entityClass's type), or an empty list if no documents found. + * @return The matching records mapped to targetClass's type if provided (otherwise to entityClass's type), or an + * empty list if no documents found. */ List findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, @Nullable Query query); @@ -748,8 +724,8 @@ List findByIdsUsingQuery(Collection ids, Class entityClass, Clas * @param targetClass The class to map the record to. * @param setName Set name to use. * @param query The {@link Query} to filter results. Optional argument (null if no filtering required). - * @return The matching records mapped to targetClass's type if provided - * (otherwise to entityClass's type), or an empty list if no documents found. + * @return The matching records mapped to targetClass's type if provided (otherwise to entityClass's type), or an + * empty list if no documents found. */ List findByIdsUsingQuery(Collection ids, Class entityClass, Class targetClass, String setName, @Nullable Query query); @@ -758,8 +734,7 @@ List findByIdsUsingQuery(Collection ids, Class entityClass, Clas * Find records in the given entityClass's set using a query and map them to the given class type. * * @param query The {@link Query} to filter results. Must not be {@literal null}. - * @param entityClass The class to extract set name from and to map the records to. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from and to map the records to. Must not be {@literal null}. * @return A Stream of matching records mapped to entityClass type. */ Stream find(Query query, Class entityClass); @@ -787,8 +762,7 @@ List findByIdsUsingQuery(Collection ids, Class entityClass, Clas /** * Find all records in the given entityClass's set and map them to the given class type. * - * @param entityClass The class to extract set name from and to map the records to. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from and to map the records to. Must not be {@literal null}. * @return A Stream of matching records mapped to entityClass type. */ Stream findAll(Class entityClass); @@ -854,8 +828,7 @@ List findByIdsUsingQuery(Collection ids, Class entityClass, Clas * @param offset The offset to start the range from. * @param limit The limit of the range. * @param sort The sort to affect the order of the returned Stream of documents. - * @param entityClass The class to extract set name from and to map the records to. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from and to map the records to. Must not be {@literal null}. * @return A Stream of matching records mapped to entityClass type. */ Stream findInRange(long offset, long limit, Sort sort, Class entityClass); @@ -887,8 +860,8 @@ List findByIdsUsingQuery(Collection ids, Class entityClass, Clas Stream findInRange(long offset, long limit, Sort sort, Class targetClass, String setName); /** - * Find records in the given entityClass set using a query and map them to the given target class type. If the - * query has pagination and/or sorting, post-processing must be applied separately. + * Find records in the given entityClass set using a query and map them to the given target class type. If the query + * has pagination and/or sorting, post-processing must be applied separately. * * @param entityClass The class to extract set name from. Must not be {@literal null}. * @param targetClass The class to map the records to. diff --git a/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java b/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java index d46a6572f..ce5160480 100644 --- a/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java +++ b/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java @@ -44,9 +44,9 @@ import org.springframework.data.aerospike.query.QueryEngine; import org.springframework.data.aerospike.query.cache.IndexRefresher; import org.springframework.data.aerospike.repository.query.Query; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.aerospike.utility.Utils; import org.springframework.data.domain.Sort; -import org.springframework.data.keyvalue.core.IterableConverter; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.util.StreamUtils; import org.springframework.util.Assert; @@ -66,6 +66,9 @@ import java.util.stream.IntStream; import java.util.stream.Stream; +import static org.springframework.data.aerospike.core.BaseAerospikeTemplate.OperationType.INSERT_OPERATION; +import static org.springframework.data.aerospike.core.BaseAerospikeTemplate.OperationType.SAVE_OPERATION; +import static org.springframework.data.aerospike.core.BaseAerospikeTemplate.OperationType.UPDATE_OPERATION; import static org.springframework.data.aerospike.core.CoreUtils.getDistinctPredicate; import static org.springframework.data.aerospike.core.CoreUtils.operations; import static org.springframework.data.aerospike.core.CoreUtils.verifyUnsortedWithOffset; @@ -88,6 +91,7 @@ public class AerospikeTemplate extends BaseAerospikeTemplate implements Aerospik IndexesCacheRefresher { private static final Pattern INDEX_EXISTS_REGEX_PATTERN = Pattern.compile("^FAIL:(-?\\d+).*$"); + private final IAerospikeClient client; private final QueryEngine queryEngine; private final IndexRefresher indexRefresher; @@ -98,8 +102,10 @@ public AerospikeTemplate(IAerospikeClient client, AerospikeMappingContext mappingContext, AerospikeExceptionTranslator exceptionTranslator, QueryEngine queryEngine, - IndexRefresher indexRefresher) { - super(namespace, converter, mappingContext, exceptionTranslator, client.getWritePolicyDefault()); + IndexRefresher indexRefresher, + ServerVersionSupport serverVersionSupport) { + super(namespace, converter, mappingContext, exceptionTranslator, client.getWritePolicyDefault(), + serverVersionSupport); this.client = client; this.queryEngine = queryEngine; this.indexRefresher = indexRefresher; @@ -150,17 +156,46 @@ public void save(T document, String setName) { @Override public void saveAll(Iterable documents) { - Assert.notNull(documents, "Documents for saving must not be null!"); + validateForBatchWrite(documents, "Documents for saving"); + saveAll(documents, getSetName(documents.iterator().next())); } @Override public void saveAll(Iterable documents, String setName) { - Assert.notNull(documents, "Documents for saving must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + validateForBatchWrite(documents, "Documents for saving"); + + applyBufferedBatchWrite(documents, setName, SAVE_OPERATION); + } + + private void applyBufferedBatchWrite(Iterable documents, String setName, OperationType operationType) { + int batchSize = converter.getAerospikeDataSettings().getBatchWriteSize(); + List docsList = new ArrayList<>(); + + for (T doc : documents) { + if (batchWriteSizeMatch(batchSize, docsList.size())) { + batchWriteAllDocuments(docsList, setName, operationType); + docsList.clear(); + } + docsList.add(doc); + } + if (!docsList.isEmpty()) { + batchWriteAllDocuments(docsList, setName, operationType); + } + } + private void batchWriteAllDocuments(List documents, String setName, OperationType operationType) { List> batchWriteDataList = new ArrayList<>(); - documents.forEach(document -> batchWriteDataList.add(getBatchWriteForSave(document, setName))); + switch (operationType) { + case SAVE_OPERATION -> + documents.forEach(document -> batchWriteDataList.add(getBatchWriteForSave(document, setName))); + case INSERT_OPERATION -> + documents.forEach(document -> batchWriteDataList.add(getBatchWriteForInsert(document, setName))); + case UPDATE_OPERATION -> + documents.forEach(document -> batchWriteDataList.add(getBatchWriteForUpdate(document, setName))); + default -> throw new IllegalArgumentException("Unexpected operation name: " + operationType); + } List batchWriteRecords = batchWriteDataList.stream().map(BatchWriteData::batchRecord).toList(); try { @@ -170,13 +205,13 @@ public void saveAll(Iterable documents, String setName) { throw translateError(e); } - checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, "save"); + checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, operationType); } private void checkForErrorsAndUpdateVersion(List> batchWriteDataList, - List batchWriteRecords, String commandName) { + List batchWriteRecords, OperationType operationType) { boolean errorsFound = false; - for (AerospikeTemplate.BatchWriteData data : batchWriteDataList) { + for (BaseAerospikeTemplate.BatchWriteData data : batchWriteDataList) { if (!errorsFound && batchRecordFailed(data.batchRecord())) { errorsFound = true; } @@ -186,7 +221,7 @@ private void checkForErrorsAndUpdateVersion(List> batchWri } if (errorsFound) { - AerospikeException e = new AerospikeException("Errors during batch " + commandName); + AerospikeException e = new AerospikeException("Errors during batch " + operationType); throw new AerospikeException.BatchRecordArray(batchWriteRecords.toArray(BatchRecord[]::new), e); } } @@ -221,27 +256,17 @@ public void insert(T document, String setName) { @Override public void insertAll(Iterable documents) { - Assert.notNull(documents, "Documents must not be null!"); + validateForBatchWrite(documents, "Documents for insert"); + insertAll(documents, getSetName(documents.iterator().next())); } @Override public void insertAll(Iterable documents, String setName) { - Assert.notNull(documents, "Documents for inserting must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + validateForBatchWrite(documents, "Documents for insert"); - List> batchWriteDataList = new ArrayList<>(); - documents.forEach(document -> batchWriteDataList.add(getBatchWriteForInsert(document, setName))); - - List batchWriteRecords = batchWriteDataList.stream().map(BatchWriteData::batchRecord).toList(); - try { - // requires server ver. >= 6.0.0 - client.operate(null, batchWriteRecords); - } catch (AerospikeException e) { - throw translateError(e); - } - - checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, "insert"); + applyBufferedBatchWrite(documents, setName, INSERT_OPERATION); } @Override @@ -318,37 +343,27 @@ public void update(T document, String setName, Collection fields) { @Override public void updateAll(Iterable documents) { - Assert.notNull(documents, "Documents must not be null!"); + validateForBatchWrite(documents, "Documents for update"); + updateAll(documents, getSetName(documents.iterator().next())); } @Override public void updateAll(Iterable documents, String setName) { - Assert.notNull(documents, "Documents must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + validateForBatchWrite(documents, "Documents for update"); - List> batchWriteDataList = new ArrayList<>(); - documents.forEach(document -> batchWriteDataList.add(getBatchWriteForUpdate(document, setName))); - - List batchWriteRecords = batchWriteDataList.stream().map(BatchWriteData::batchRecord).toList(); - try { - // requires server ver. >= 6.0.0 - client.operate(null, batchWriteRecords); - } catch (AerospikeException e) { - throw translateError(e); - } - - checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, "update"); + applyBufferedBatchWrite(documents, setName, UPDATE_OPERATION); } - @Deprecated + @Deprecated(since = "4.6.0", forRemoval = true) @Override public void delete(Class entityClass) { Assert.notNull(entityClass, "Class must not be null!"); delete(getSetName(entityClass)); } - @Deprecated + @Deprecated(since = "4.6.0", forRemoval = true) @Override public boolean delete(Object id, Class entityClass) { Assert.notNull(entityClass, "Class must not be null!"); @@ -405,30 +420,35 @@ public boolean deleteById(Object id, String setName) { @Override public void deleteByIds(Iterable ids, Class entityClass) { - Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(entityClass, "Class must not be null!"); + validateForBatchWrite(ids, "IDs"); + deleteByIds(ids, getSetName(entityClass)); } @Override - public void deleteByIds(Iterable ids, String setName) { - Assert.notNull(ids, "List of ids must not be null!"); + public void + deleteByIds(Iterable ids, String setName) { Assert.notNull(setName, "Set name must not be null!"); - - deleteByIds(IterableConverter.toList(ids), setName); - } - - @Override - public void deleteByIds(Collection ids, Class entityClass) { - Assert.notNull(ids, "List of ids must not be null!"); - Assert.notNull(entityClass, "Class must not be null!"); - deleteByIds(ids, getSetName(entityClass)); + validateForBatchWrite(ids, "IDs"); + + int batchSize = converter.getAerospikeDataSettings().getBatchWriteSize(); + List idsList = new ArrayList<>(); + for (Object id : ids) { + if (batchWriteSizeMatch(batchSize, idsList.size())) { + deleteByIds(idsList, setName); + idsList.clear(); + } + idsList.add(id); + } + if (!idsList.isEmpty()) { + deleteByIds(idsList, setName); + } } - @Override - public void deleteByIds(Collection ids, String setName) { - Assert.notNull(ids, "List of ids must not be null!"); + private void deleteByIds(Collection ids, String setName) { Assert.notNull(setName, "Set name must not be null!"); + validateForBatchWrite(ids, "IDs"); if (ids.isEmpty()) { return; @@ -444,9 +464,11 @@ public void deleteByIds(Collection ids, String setName) { @Override public void deleteByIds(GroupedKeys groupedKeys) { - Assert.notNull(groupedKeys, "Grouped keys must not be null!"); - Assert.notNull(groupedKeys.getEntitiesKeys(), "Entities keys must not be null!"); - Assert.notEmpty(groupedKeys.getEntitiesKeys(), "Entities keys must not be empty!"); + validateGroupedKeys(groupedKeys); + + if (groupedKeys.getEntitiesKeys().isEmpty()) { + return; + } deleteGroupedEntitiesByGroupedKeys(groupedKeys); } @@ -482,6 +504,10 @@ private void deleteAndHandleErrors(IAerospikeClient client, Key[] keys) { throw translateError(e); } + if (results.records == null) { + throw new AerospikeException.BatchRecordArray(results.records, + new AerospikeException("Errors during batch delete")); + } for (int i = 0; i < results.records.length; i++) { BatchRecord record = results.records[i]; if (batchRecordFailed(record)) { @@ -787,14 +813,35 @@ public List findByIds(Iterable ids, Class entityClass, Class Assert.notNull(entityClass, "Entity class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return (List) findByIdsUsingQuery(IterableConverter.toList(ids), entityClass, targetClass, setName, null); + int batchSize = converter.getAerospikeDataSettings().getBatchWriteSize(); + List idsList = new ArrayList<>(); + List result = new ArrayList<>(); + + for (Object id : ids) { + if (batchWriteSizeMatch(batchSize, idsList.size())) { + result = Stream.concat( + result.stream(), + ((List) findByIdsUsingQuery(idsList, entityClass, targetClass, setName, null)).stream() + ).toList(); + idsList.clear(); + } + idsList.add(id); + } + if (!idsList.isEmpty()) { + result = Stream.concat( + result.stream(), + ((List) findByIdsUsingQuery(idsList, entityClass, targetClass, setName, null)).stream() + ).toList(); + } + + return result; } @Override public GroupedEntities findByIds(GroupedKeys groupedKeys) { - Assert.notNull(groupedKeys, "Grouped keys must not be null!"); + validateGroupedKeys(groupedKeys); - if (groupedKeys.getEntitiesKeys() == null || groupedKeys.getEntitiesKeys().isEmpty()) { + if (groupedKeys.getEntitiesKeys().isEmpty()) { return GroupedEntities.builder().build(); } diff --git a/src/main/java/org/springframework/data/aerospike/core/BaseAerospikeTemplate.java b/src/main/java/org/springframework/data/aerospike/core/BaseAerospikeTemplate.java index 5dedddf98..50b2a1610 100644 --- a/src/main/java/org/springframework/data/aerospike/core/BaseAerospikeTemplate.java +++ b/src/main/java/org/springframework/data/aerospike/core/BaseAerospikeTemplate.java @@ -46,6 +46,7 @@ import org.springframework.data.aerospike.mapping.BasicAerospikePersistentEntity; import org.springframework.data.aerospike.mapping.Field; import org.springframework.data.aerospike.repository.query.Query; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.domain.Sort; import org.springframework.data.keyvalue.core.IterableConverter; import org.springframework.data.mapping.PersistentPropertyAccessor; @@ -79,12 +80,14 @@ abstract class BaseAerospikeTemplate { protected final AerospikeExceptionTranslator exceptionTranslator; protected final WritePolicy writePolicyDefault; protected final BatchWritePolicy batchWritePolicyDefault; + protected final ServerVersionSupport serverVersionSupport; BaseAerospikeTemplate(String namespace, MappingAerospikeConverter converter, AerospikeMappingContext mappingContext, AerospikeExceptionTranslator exceptionTranslator, - WritePolicy writePolicyDefault) { + WritePolicy writePolicyDefault, + ServerVersionSupport serverVersionSupport) { Assert.notNull(writePolicyDefault, "Write policy must not be null!"); Assert.notNull(namespace, "Namespace cannot be null"); Assert.hasLength(namespace, "Namespace cannot be empty"); @@ -95,6 +98,7 @@ abstract class BaseAerospikeTemplate { this.mappingContext = mappingContext; this.writePolicyDefault = writePolicyDefault; this.batchWritePolicyDefault = getFromWritePolicy(writePolicyDefault); + this.serverVersionSupport = serverVersionSupport; loggerSetup(); } @@ -370,10 +374,6 @@ private S convertIfNecessary(Object source, Class type) { : converter.getConversionService().convert(source, type); } - protected record BatchWriteData(T document, BatchRecord batchRecord, boolean hasVersionProperty) { - - } - protected Operation[] getPutAndGetHeaderOperations(AerospikeWriteData data, boolean firstlyDeleteBins) { Bin[] bins = data.getBinsAsArray(); @@ -455,7 +455,47 @@ public BatchWriteData getBatchWriteForUpdate(T document, String setName) entity.hasVersionProperty()); } + protected void validateGroupedKeys(GroupedKeys groupedKeys) { + Assert.notNull(groupedKeys, "Grouped keys must not be null!"); + validateForBatchWrite(groupedKeys.getEntitiesKeys(), "Entities keys"); + } + + protected void validateForBatchWrite(Object object, String objectName) { + Assert.notNull(object, objectName + " must not be null!"); + Assert.isTrue(batchWriteSupported(), "Batch write operations are supported starting with " + + "server version " + TemplateUtils.SERVER_VERSION_6); + } + + protected boolean batchWriteSizeMatch(int batchSize, int currentSize) { + return batchSize > 0 && currentSize == batchSize; + } + protected boolean batchRecordFailed(BatchRecord batchRecord) { return batchRecord.resultCode != ResultCode.OK || batchRecord.record == null; } + + protected boolean batchWriteSupported() { + return serverVersionSupport.batchWrite(); + } + + protected enum OperationType { + SAVE_OPERATION("save"), + INSERT_OPERATION("insert"), + UPDATE_OPERATION("update"); + + private final String name; + + OperationType(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + protected record BatchWriteData(T document, BatchRecord batchRecord, boolean hasVersionProperty) { + + } } diff --git a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java index 91ea6ff84..4561162e7 100644 --- a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java +++ b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeOperations.java @@ -199,8 +199,8 @@ public interface ReactiveAerospikeOperations { Mono persist(T document, WritePolicy writePolicy); /** - * Reactively persist a document within the given set (overrides the default set associated with the document) - * using specified WritePolicy. + * Reactively persist a document within the given set (overrides the default set associated with the document) using + * specified WritePolicy. * * @param document The document to be persisted. Must not be {@literal null}. * @param writePolicy The Aerospike write policy for the inner Aerospike put operation. Must not be @@ -298,7 +298,7 @@ public interface ReactiveAerospikeOperations { * Reactively truncate/delete all records from the set determined by the given entityClass. * * @param entityClass The class to extract the Aerospike set name from. Must not be {@literal null}. - * @deprecated since 4.6.0, use deleteAll(Class entityClass) instead. + * @deprecated since 4.6.0, use {@link AerospikeOperations#deleteAll(Class)} instead. */ Mono delete(Class entityClass); @@ -308,7 +308,7 @@ public interface ReactiveAerospikeOperations { * @param id The id of a record to be deleted. Must not be {@literal null}. * @param entityClass The class to extract the Aerospike set name from. Must not be {@literal null}. * @return A Mono of whether the document existed on server before deletion. - * @deprecated since 4.6.0, use deleteById(Object id, Class entityClass) instead. + * @deprecated since 4.6.0, use {@link AerospikeOperations#deleteById(Object, Class)} instead. */ Mono delete(Object id, Class entityClass); @@ -348,7 +348,8 @@ public interface ReactiveAerospikeOperations { Mono deleteById(Object id, String setName); /** - * Reactively delete records using a single batch delete operation, set name will be determined by the given entity class. + * Reactively delete records using a single batch delete operation, set name will be determined by the given entity + * class. *

* This operation requires Server version 6.0+. * @@ -373,37 +374,12 @@ public interface ReactiveAerospikeOperations { Mono deleteByIds(Iterable ids, String setName); /** - * Reactively delete records using a single batch delete operation, set name will be - * determined by the given entityClass. + * Reactively delete records from different sets in a single request. *

* This operation requires Server version 6.0+. * - * @param ids The ids of the documents to find. Must not be {@literal null}. - * @param entityClass The class to extract the Aerospike set name from and to map the results to. Must not be - * {@literal null}. - * @return onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain - * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. - */ - Mono deleteByIds(Collection ids, Class entityClass); - - /** - * Reactively delete records within the given set using a single batch delete operation. - *

- * This operation requires Server version 6.0+. - * - * @param ids The ids of the documents to find. Must not be {@literal null}. - * @param setName Set name to use. - * @return onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain - * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. - */ - Mono deleteByIds(Collection ids, String setName); - - /** - Reactively delete records from different sets in a single request. - *

- * This operation requires Server version 6.0+. - * - * @param groupedKeys Keys grouped by document type. Must not be {@literal null}. + * @param groupedKeys Keys grouped by document type. Must not be {@literal null}, groupedKeys.getEntitiesKeys() must + * not be {@literal null}. * @return onError is signalled with {@link AerospikeException.BatchRecordArray} if batch delete results contain * errors, or with {@link org.springframework.dao.DataAccessException} if batch operation failed. */ @@ -424,20 +400,20 @@ public interface ReactiveAerospikeOperations { Mono deleteAll(String setName); /** - * Find an existing record matching the document's class and id, add map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, add map values to the corresponding bins of the + * record and return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the result to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the result to. Must not be {@literal null}. * @param values The Map of bin names and values to add. Must not be {@literal null}. * @return A Mono of the modified record mapped to the document's class. */ Mono add(T document, Map values); /** - * Find an existing record matching the document's id and the given set name, add map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, add map values to the corresponding + * bins of the record and return the modified record mapped to the document's class. * - * @param document The document to get id from and to map the result to. Must not be - * {@literal null}. + * @param document The document to get id from and to map the result to. Must not be {@literal null}. * @param setName Set name to use. * @param values The Map of bin names and values to add. Must not be {@literal null}. * @return A Mono of the modified record mapped to the document's class. @@ -445,10 +421,10 @@ public interface ReactiveAerospikeOperations { Mono add(T document, String setName, Map values); /** - * Find an existing record matching the document's class and id, add specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, add specified value to the record's bin and return + * the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the result to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the result to. Must not be {@literal null}. * @param binName Bin name to use add operation on. Must not be {@literal null}. * @param value The value to add. * @return A Mono of the modified record mapped to the document's class. @@ -456,10 +432,10 @@ public interface ReactiveAerospikeOperations { Mono add(T document, String binName, long value); /** - * Find an existing record matching the document's id and the given set name, add specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, add specified value to the record's + * bin and return the modified record mapped to the document's class. * - * @param document The document to get id from and to map the result to. Must not be - * {@literal null}. + * @param document The document to get id from and to map the result to. Must not be {@literal null}. * @param setName Set name to use. * @param binName Bin name to use add operation on. Must not be {@literal null}. * @param value The value to add. @@ -468,17 +444,18 @@ public interface ReactiveAerospikeOperations { Mono add(T document, String setName, String binName, long value); /** - * Find an existing record matching the document's class and id, append map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, append map values to the corresponding bins of the + * record and return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the result to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the result to. Must not be {@literal null}. * @param values The Map of bin names and values to append. Must not be {@literal null}. * @return A Mono of the modified record mapped to the document's class. */ Mono append(T document, Map values); /** - * Find an existing record matching the document's id and the given set name, append map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, append map values to the corresponding + * bins of the record and return the modified record mapped to the document's class. * * @param document The document to get id from and to map the result to. Must not be {@literal null}. * @param setName Set name to use. @@ -488,10 +465,10 @@ public interface ReactiveAerospikeOperations { Mono append(T document, String setName, Map values); /** - * Find an existing record matching the document's class and id, append specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, append specified value to the record's bin and + * return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the result to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the result to. Must not be {@literal null}. * @param binName Bin name to use append operation on. * @param value The value to append. * @return A Mono of the modified record mapped to the document's class. @@ -499,7 +476,8 @@ public interface ReactiveAerospikeOperations { Mono append(T document, String binName, String value); /** - * Find an existing record matching the document's id and the given set name, append specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, append specified value to the record's + * bin and return the modified record mapped to the document's class. * * @param document The document to get id from and to map the result to. Must not be {@literal null}. * @param setName Set name to use. @@ -510,17 +488,18 @@ public interface ReactiveAerospikeOperations { Mono append(T document, String setName, String binName, String value); /** - * Find an existing record matching the document's class and id, prepend map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, prepend map values to the corresponding bins of the + * record and return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the result to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the result to. Must not be {@literal null}. * @param values The Map of bin names and values to prepend. Must not be {@literal null}. * @return A Mono of the modified record mapped to the document's class. */ Mono prepend(T document, Map values); /** - * Find an existing record matching the document's id and the given set name, prepend map values to the corresponding bins of the record and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, prepend map values to the + * corresponding bins of the record and return the modified record mapped to the document's class. * * @param document The document to get id from and to map the result to. Must not be {@literal null}. * @param setName Set name to use. @@ -530,10 +509,10 @@ public interface ReactiveAerospikeOperations { Mono prepend(T document, String setName, Map values); /** - * Find an existing record matching the document's class and id, prepend specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's class and id, prepend specified value to the record's bin and + * return the modified record mapped to the document's class. * - * @param document The document to get set name and id from and to map the result to. Must not be - * {@literal null}. + * @param document The document to get set name and id from and to map the result to. Must not be {@literal null}. * @param binName Bin name to use prepend operation on. * @param value The value to prepend. * @return A Mono of the modified record mapped to the document's class. @@ -541,7 +520,8 @@ public interface ReactiveAerospikeOperations { Mono prepend(T document, String binName, String value); /** - * Find an existing record matching the document's id and the given set name, prepend specified value to the record's bin and return the modified record mapped to the document's class. + * Find an existing record matching the document's id and the given set name, prepend specified value to the + * record's bin and return the modified record mapped to the document's class. * * @param document The document to get id from and to map the result to. Must not be {@literal null}. * @param setName Set name to use. @@ -565,8 +545,7 @@ public interface ReactiveAerospikeOperations { * The matching record will be mapped to the given entityClass. * * @param id The id of the record to find. Must not be {@literal null}. - * @param entityClass The class to extract set name from and to map the document to. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from and to map the document to. Must not be {@literal null}. * @return A Mono of the matching record mapped to entityClass type. */ Mono findById(Object id, Class entityClass); @@ -611,21 +590,20 @@ public interface ReactiveAerospikeOperations { Mono findById(Object id, Class entityClass, Class targetClass, String setName); /** - * Reactively find records by ids using a single batch read operation, set name will be - * determined by the given entityClass. + * Reactively find records by ids using a single batch read operation, set name will be determined by the given + * entityClass. *

* The records will be mapped to the given entityClass. * * @param ids The ids of the documents to find. Must not be {@literal null}. - * @param entityClass The class to extract set name from and to map the documents to. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from and to map the documents to. Must not be {@literal null}. * @return A Flux of matching records mapped to entityClass type. */ Flux findByIds(Iterable ids, Class entityClass); /** - * Reactively find records by ids using a single batch read operation, set name will be - * determined by the given entityClass. + * Reactively find records by ids using a single batch read operation, set name will be determined by the given + * entityClass. *

* The records will be mapped to the given targetClass. * @@ -651,10 +629,11 @@ public interface ReactiveAerospikeOperations { /** * Reactively execute a single batch request to find several records, possibly from different sets. *

- * Aerospike provides functionality to get records from different sets in 1 batch request. This method receives - * keys grouped by document type as a parameter and returns Aerospike records mapped to documents grouped by type. + * Aerospike provides functionality to get records from different sets in 1 batch request. This method receives keys + * grouped by document type as a parameter and returns Aerospike records mapped to documents grouped by type. * - * @param groupedKeys Must not be {@literal null}. + * @param groupedKeys Keys grouped by document type. Must not be {@literal} null, groupedKeys.getEntitiesKeys() must + * not be {@literal null}. * @return Mono of grouped documents. */ Mono findByIds(GroupedKeys groupedKeys); @@ -725,8 +704,7 @@ Flux findByIdsUsingQuery(Collection ids, Class entityClass, Clas * Reactively find records in the given entityClass's set using a query and map them to the given class type. * * @param query The {@link Query} to filter results. Must not be {@literal null}. - * @param entityClass The class to extract set name from and to map the documents to. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from and to map the documents to. Must not be {@literal null}. * @return A Flux of matching records mapped to entityClass type. */ Flux find(Query query, Class entityClass); @@ -755,8 +733,7 @@ Flux findByIdsUsingQuery(Collection ids, Class entityClass, Clas /** * Reactively find all records in the given entityClass's set and map them to the given class type. * - * @param entityClass The class to extract set name from and to map the documents to. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from and to map the documents to. Must not be {@literal null}. * @return A Flux of matching records mapped to entityClass type. */ Flux findAll(Class entityClass); @@ -780,8 +757,8 @@ Flux findByIdsUsingQuery(Collection ids, Class entityClass, Clas Flux findAll(Class targetClass, String setName); /** - * Reactively find all records in the given entityClass's set using a provided sort and map them to the given - * class type. + * Reactively find all records in the given entityClass's set using a provided sort and map them to the given class + * type. * * @param sort The sort to affect the returned iterable documents order. * @param offset The offset to start the range from. @@ -792,8 +769,8 @@ Flux findByIdsUsingQuery(Collection ids, Class entityClass, Clas Flux findAll(Sort sort, long offset, long limit, Class entityClass); /** - * Reactively find all records in the given entityClass's set using a provided sort and map them to the given - * target class type. + * Reactively find all records in the given entityClass's set using a provided sort and map them to the given target + * class type. * * @param sort The sort to affect the returned iterable documents order. * @param offset The offset to start the range from. @@ -805,8 +782,8 @@ Flux findByIdsUsingQuery(Collection ids, Class entityClass, Clas Flux findAll(Sort sort, long offset, long limit, Class entityClass, Class targetClass); /** - * Reactively find all records in the given entityClass's set using a provided sort and map them to the given - * target class type. + * Reactively find all records in the given entityClass's set using a provided sort and map them to the given target + * class type. * * @param sort The sort to affect the returned iterable documents order. * @param offset The offset to start the range from. @@ -824,15 +801,14 @@ Flux findByIdsUsingQuery(Collection ids, Class entityClass, Clas * @param offset The offset to start the range from. * @param limit The limit of the range. * @param sort The sort to affect the order of the returned Stream of documents. - * @param entityClass The class to extract set name from and to map the documents to. Must not be - * {@literal null}. + * @param entityClass The class to extract set name from and to map the documents to. Must not be {@literal null}. * @return A Flux of matching records mapped to entityClass type. */ Flux findInRange(long offset, long limit, Sort sort, Class entityClass); /** - * Reactively find records in the given set using a range (offset, limit) and a sort and map them to the given - * class type. + * Reactively find records in the given set using a range (offset, limit) and a sort and map them to the given class + * type. * * @param offset The offset to start the range from. * @param limit The limit of the range. @@ -857,14 +833,13 @@ Flux findByIdsUsingQuery(Collection ids, Class entityClass, Clas Flux findInRange(long offset, long limit, Sort sort, Class entityClass, Class targetClass); /** - * Reactively find records in the given entityClass's set using a query and map them to the given target class - * type. If the query has pagination and/or sorting, post-processing must be applied separately. + * Reactively find records in the given entityClass's set using a query and map them to the given target class type. + * If the query has pagination and/or sorting, post-processing must be applied separately. * * @param entityClass The class to extract set name from. Must not be {@literal null}. * @param targetClass The class to map the record to. * @param query The {@link Query} to filter results. - * @return A Flux of all matching records mapped to - * targetClass type (regardless of pagination/sorting). + * @return A Flux of all matching records mapped to targetClass type (regardless of pagination/sorting). */ Flux findUsingQueryWithoutPostProcessing(Class entityClass, Class targetClass, Query query); @@ -922,8 +897,7 @@ Flux findByIdsUsingQuery(Collection ids, Class entityClass, Clas Mono count(String setName); /** - * Reactively return the amount of records in query results. Set name will be determined by the given - * entityClass. + * Reactively return the amount of records in query results. Set name will be determined by the given entityClass. * * @param query The query that provides the result set for count. * @param entityClass entityClass to extract set name from. Must not be {@literal null}. diff --git a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java index 328e27a02..4625e9e90 100644 --- a/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java +++ b/src/main/java/org/springframework/data/aerospike/core/ReactiveAerospikeTemplate.java @@ -40,6 +40,7 @@ import org.springframework.data.aerospike.query.ReactorQueryEngine; import org.springframework.data.aerospike.query.cache.ReactorIndexRefresher; import org.springframework.data.aerospike.repository.query.Query; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.aerospike.utility.Utils; import org.springframework.data.domain.Sort; import org.springframework.data.keyvalue.core.IterableConverter; @@ -60,9 +61,13 @@ import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import static com.aerospike.client.ResultCode.KEY_NOT_FOUND_ERROR; import static java.util.Objects.nonNull; +import static org.springframework.data.aerospike.core.BaseAerospikeTemplate.OperationType.INSERT_OPERATION; +import static org.springframework.data.aerospike.core.BaseAerospikeTemplate.OperationType.SAVE_OPERATION; +import static org.springframework.data.aerospike.core.BaseAerospikeTemplate.OperationType.UPDATE_OPERATION; import static org.springframework.data.aerospike.core.CoreUtils.getDistinctPredicate; import static org.springframework.data.aerospike.core.CoreUtils.operations; import static org.springframework.data.aerospike.core.TemplateUtils.excludeIdQualifier; @@ -82,6 +87,7 @@ public class ReactiveAerospikeTemplate extends BaseAerospikeTemplate implements IndexesCacheRefresher { private static final Pattern INDEX_EXISTS_REGEX_PATTERN = Pattern.compile("^FAIL:(-?\\d+).*$"); + private final IAerospikeReactorClient reactorClient; private final ReactorQueryEngine reactorQueryEngine; private final ReactorIndexRefresher reactorIndexRefresher; @@ -91,8 +97,10 @@ public ReactiveAerospikeTemplate(IAerospikeReactorClient reactorClient, MappingAerospikeConverter converter, AerospikeMappingContext mappingContext, AerospikeExceptionTranslator exceptionTranslator, - ReactorQueryEngine queryEngine, ReactorIndexRefresher reactorIndexRefresher) { - super(namespace, converter, mappingContext, exceptionTranslator, reactorClient.getWritePolicyDefault()); + ReactorQueryEngine queryEngine, ReactorIndexRefresher reactorIndexRefresher, + ServerVersionSupport serverVersionSupport) { + super(namespace, converter, mappingContext, exceptionTranslator, reactorClient.getWritePolicyDefault(), + serverVersionSupport); Assert.notNull(reactorClient, "Aerospike reactor client must not be null!"); this.reactorClient = reactorClient; this.reactorQueryEngine = queryEngine; @@ -134,38 +142,72 @@ public Mono save(T document, String setName) { @Override public Flux saveAll(Iterable documents) { - Assert.notNull(documents, "Documents for saving must not be null!"); + validateForBatchWrite(documents, "Documents for saving"); + return saveAll(documents, getSetName(documents.iterator().next())); } @Override public Flux saveAll(Iterable documents, String setName) { - Assert.notNull(documents, "Documents for saving must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + validateForBatchWrite(documents, "Documents for saving"); + + return applyBufferedBatchWrite(documents, setName, SAVE_OPERATION); + } + private Flux applyBufferedBatchWrite(Iterable documents, String setName, + OperationType operationType) { + int batchSize = converter.getAerospikeDataSettings().getBatchWriteSize(); + List docsList = new ArrayList<>(); + Flux result = Flux.empty(); + + for (T doc : documents) { + if (batchWriteSizeMatch(batchSize, docsList.size())) { + result = Flux.concat(result, batchWriteAllDocuments(docsList, setName, operationType)); + docsList.clear(); + } + docsList.add(doc); + } + if (!docsList.isEmpty()) { + result = Flux.concat(result, batchWriteAllDocuments(docsList, setName, operationType)); + } + + return result; + } + + private Flux batchWriteAllDocuments(List documents, String setName, OperationType operationType) { List> batchWriteDataList = new ArrayList<>(); - documents.forEach(document -> batchWriteDataList.add(getBatchWriteForSave(document, setName))); + switch (operationType) { + case SAVE_OPERATION -> + documents.forEach(document -> batchWriteDataList.add(getBatchWriteForSave(document, setName))); + case INSERT_OPERATION -> + documents.forEach(document -> batchWriteDataList.add(getBatchWriteForInsert(document, setName))); + case UPDATE_OPERATION -> + documents.forEach(document -> batchWriteDataList.add(getBatchWriteForUpdate(document, setName))); + default -> throw new IllegalArgumentException("Unexpected operation name: " + operationType); + } List batchWriteRecords = batchWriteDataList.stream().map(BatchWriteData::batchRecord).toList(); - return batchWriteAndCheckForErrors(batchWriteRecords, batchWriteDataList, "save"); + return batchWriteAndCheckForErrors(batchWriteRecords, batchWriteDataList, operationType); } private Flux batchWriteAndCheckForErrors(List batchWriteRecords, - List> batchWriteDataList, String commandName) { + List> batchWriteDataList, + OperationType operationType) { // requires server ver. >= 6.0.0 return reactorClient.operate(null, batchWriteRecords) .onErrorMap(this::translateError) - .flatMap(ignore -> checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, commandName)) + .flatMap(ignore -> checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, operationType)) .flux() .flatMapIterable(list -> list.stream().map(BatchWriteData::document).toList()); } private Mono>> checkForErrorsAndUpdateVersion(List> batchWriteDataList, List batchWriteRecords, - String commandName) { + OperationType operationType) { boolean errorsFound = false; - for (AerospikeTemplate.BatchWriteData data : batchWriteDataList) { + for (BaseAerospikeTemplate.BatchWriteData data : batchWriteDataList) { if (!errorsFound && batchRecordFailed(data.batchRecord())) { errorsFound = true; } @@ -175,7 +217,7 @@ private Mono>> checkForErrorsAndUpdateVersion(List Mono insert(T document, String setName) { @Override public Flux insertAll(Iterable documents) { + validateForBatchWrite(documents, "Documents for insert"); + return insertAll(documents, getSetName(documents.iterator().next())); } @Override public Flux insertAll(Iterable documents, String setName) { - Assert.notNull(documents, "Documents for insert must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + validateForBatchWrite(documents, "Documents for insert"); - List> batchWriteDataList = new ArrayList<>(); - documents.forEach(document -> batchWriteDataList.add(getBatchWriteForInsert(document, setName))); - - List batchWriteRecords = batchWriteDataList.stream().map(BatchWriteData::batchRecord).toList(); - - return batchWriteAndCheckForErrors(batchWriteRecords, batchWriteDataList, "insert"); + return applyBufferedBatchWrite(documents, setName, INSERT_OPERATION); } @Override @@ -308,23 +347,20 @@ public Mono update(T document, String setName, Collection fields) @Override public Flux updateAll(Iterable documents) { + validateForBatchWrite(documents, "Documents for update"); + return updateAll(documents, getSetName(documents.iterator().next())); } @Override public Flux updateAll(Iterable documents, String setName) { - Assert.notNull(documents, "Documents for saving must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + validateForBatchWrite(documents, "Documents for update"); - List> batchWriteDataList = new ArrayList<>(); - documents.forEach(document -> batchWriteDataList.add(getBatchWriteForUpdate(document, setName))); - - List batchWriteRecords = batchWriteDataList.stream().map(BatchWriteData::batchRecord).toList(); - - return batchWriteAndCheckForErrors(batchWriteRecords, batchWriteDataList, "update"); + return applyBufferedBatchWrite(documents, setName, UPDATE_OPERATION); } - @Deprecated + @Deprecated(since = "4.6.0", forRemoval = true) @Override public Mono delete(Class entityClass) { Assert.notNull(entityClass, "Class must not be null!"); @@ -338,7 +374,7 @@ public Mono delete(Class entityClass) { } } - @Deprecated + @Deprecated(since = "4.6.0", forRemoval = true) @Override public Mono delete(Object id, Class entityClass) { Assert.notNull(id, "Id must not be null!"); @@ -391,32 +427,37 @@ public Mono deleteById(Object id, String setName) { @Override public Mono deleteByIds(Iterable ids, Class entityClass) { - Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(entityClass, "Class must not be null!"); + validateForBatchWrite(ids, "IDs"); return deleteByIds(ids, getSetName(entityClass)); } @Override public Mono deleteByIds(Iterable ids, String setName) { - Assert.notNull(ids, "List of ids must not be null!"); Assert.notNull(setName, "Set name must not be null!"); + validateForBatchWrite(ids, "IDs"); + + int batchSize = converter.getAerospikeDataSettings().getBatchWriteSize(); + List idsList = new ArrayList<>(); + Flux result = Flux.empty(); + for (Object id : ids) { + if (batchWriteSizeMatch(batchSize, idsList.size())) { + result = deleteByIds(idsList, setName).concatWith(result); + idsList.clear(); + } + idsList.add(id); + } + if (!idsList.isEmpty()) { + result = deleteByIds(idsList, setName).concatWith(result); + } - return deleteByIds(IterableConverter.toList(ids), setName); - } - - @Override - public Mono deleteByIds(Collection ids, Class entityClass) { - Assert.notNull(ids, "List of ids must not be null!"); - Assert.notNull(entityClass, "Class must not be null!"); - - return deleteByIds(ids, getSetName(entityClass)); + return result.then(); } - @Override - public Mono deleteByIds(Collection ids, String setName) { - Assert.notNull(ids, "List of ids must not be null!"); + private Mono deleteByIds(Collection ids, String setName) { Assert.notNull(setName, "Set name must not be null!"); + validateForBatchWrite(ids, "IDs"); Key[] keys = ids.stream() .map(id -> getKey(id, setName)) @@ -427,6 +468,10 @@ public Mono deleteByIds(Collection ids, String setName) { private Mono batchDeleteAndCheckForErrors(IAerospikeReactorClient reactorClient, Key[] keys) { Function> checkForErrors = results -> { + if (results.records == null) { + return Mono.error(new AerospikeException.BatchRecordArray(results.records, + new AerospikeException("Errors during batch delete"))); + } for (BatchRecord record : results.records) { if (batchRecordFailed(record)) { return Mono.error(new AerospikeException.BatchRecordArray(results.records, @@ -444,9 +489,11 @@ private Mono batchDeleteAndCheckForErrors(IAerospikeReactorClient reactorC @Override public Mono deleteByIds(GroupedKeys groupedKeys) { - Assert.notNull(groupedKeys, "Grouped keys must not be null!"); - Assert.notNull(groupedKeys.getEntitiesKeys(), "Entities keys must not be null!"); - Assert.notEmpty(groupedKeys.getEntitiesKeys(), "Entities keys must not be empty!"); + validateGroupedKeys(groupedKeys); + + if (groupedKeys.getEntitiesKeys().isEmpty()) { + return Mono.empty(); + } return deleteEntitiesByGroupedKeys(groupedKeys); } @@ -685,18 +732,45 @@ public Flux findByIds(Iterable ids, Class targetClass, String setNa Assert.notNull(targetClass, "Class must not be null!"); Assert.notNull(setName, "Set name must not be null!"); - return Flux.fromIterable(ids) + int batchSize = converter.getAerospikeDataSettings().getBatchWriteSize(); + List idsList = new ArrayList<>(); + Flux result = Flux.empty(); + + for (Object id : ids) { + if (batchWriteSizeMatch(batchSize, idsList.size())) { + result = Flux.concat(result, findByIds(idsList, targetClass, setName)); + idsList.clear(); + } + idsList.add(id); + } + if (!idsList.isEmpty()) { + result = Flux.concat(result, findByIds(idsList, targetClass, setName)); + } + + return result; + } + + private Flux findByIds(Collection ids, Class targetClass, String setName) { + Key[] keys = IterableConverter.toList(ids).stream() .map(id -> getKey(id, setName)) - .flatMap(reactorClient::get) - .filter(keyRecord -> nonNull(keyRecord.record)) - .map(keyRecord -> mapToEntity(keyRecord.key, targetClass, keyRecord.record)); + .toArray(Key[]::new); + + return reactorClient.get(null, keys) + .flatMap(kr -> Mono.just(kr.asMap())) + .flatMapMany(keyRecordMap -> { + List entities = keyRecordMap.entrySet().stream() + .filter(entry -> entry.getValue() != null) + .map(entry -> mapToEntity(entry.getKey(), targetClass, entry.getValue())) + .collect(Collectors.toList()); + return Flux.fromIterable(entities); + }); } @Override public Mono findByIds(GroupedKeys groupedKeys) { - Assert.notNull(groupedKeys, "Grouped keys must not be null!"); + validateGroupedKeys(groupedKeys); - if (groupedKeys.getEntitiesKeys() == null || groupedKeys.getEntitiesKeys().isEmpty()) { + if (groupedKeys.getEntitiesKeys().isEmpty()) { return Mono.just(GroupedEntities.builder().build()); } diff --git a/src/main/java/org/springframework/data/aerospike/core/TemplateUtils.java b/src/main/java/org/springframework/data/aerospike/core/TemplateUtils.java index 64664eed2..ba0c033cb 100644 --- a/src/main/java/org/springframework/data/aerospike/core/TemplateUtils.java +++ b/src/main/java/org/springframework/data/aerospike/core/TemplateUtils.java @@ -18,6 +18,8 @@ @UtilityClass public class TemplateUtils { + final String SERVER_VERSION_6 = "6.0.0"; + public static List getIdValue(Qualifier qualifier) { if (qualifier.hasId()) { return idObjectToList(qualifier.getId()); diff --git a/src/main/java/org/springframework/data/aerospike/core/model/GroupedEntities.java b/src/main/java/org/springframework/data/aerospike/core/model/GroupedEntities.java index 3c23b31da..5e91f7229 100644 --- a/src/main/java/org/springframework/data/aerospike/core/model/GroupedEntities.java +++ b/src/main/java/org/springframework/data/aerospike/core/model/GroupedEntities.java @@ -18,6 +18,7 @@ import lombok.Builder; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,7 +29,7 @@ @Builder public class GroupedEntities { - private final Map, List> entitiesResults; + private final Map, Collection> entitiesResults; @SuppressWarnings("unchecked") public List getEntitiesByClass(Class entityClass) { @@ -41,7 +42,7 @@ public boolean containsEntities() { public static class GroupedEntitiesBuilder { - private Map, List> entitiesResults = new HashMap<>(); + private Map, Collection> entitiesResults = new HashMap<>(); @SuppressWarnings("unchecked") public GroupedEntities.GroupedEntitiesBuilder entity(Class key, T entity) { @@ -50,14 +51,13 @@ public GroupedEntities.GroupedEntitiesBuilder entity(Class key, T entity) return new ArrayList<>(singletonList(entity)); } - ((List) v).add(entity); + ((Collection) v).add(entity); return v; }); - return this; } - private GroupedEntities.GroupedEntitiesBuilder entitiesResults(Map, List> keysMap) { + public GroupedEntities.GroupedEntitiesBuilder entities(Map, Collection> keysMap) { this.entitiesResults = keysMap; return this; } diff --git a/src/main/java/org/springframework/data/aerospike/core/model/GroupedKeys.java b/src/main/java/org/springframework/data/aerospike/core/model/GroupedKeys.java index b67e3575d..abcd3ae3c 100644 --- a/src/main/java/org/springframework/data/aerospike/core/model/GroupedKeys.java +++ b/src/main/java/org/springframework/data/aerospike/core/model/GroupedKeys.java @@ -32,13 +32,12 @@ public static class GroupedKeysBuilder { private Map, Collection> entitiesKeys = new HashMap<>(); - public GroupedKeys.GroupedKeysBuilder entityKeys(Class key, Collection value) { + public GroupedKeys.GroupedKeysBuilder entityKeys(Class key, Collection value) { entitiesKeys.put(key, value); - return this; } - private GroupedKeys.GroupedKeysBuilder entitiesKeys(Map, Collection> keys) { + public GroupedKeys.GroupedKeysBuilder entitiesKeys(Map, Collection> keys) { this.entitiesKeys = keys; return this; } diff --git a/src/main/java/org/springframework/data/aerospike/query/cache/IndexRefresher.java b/src/main/java/org/springframework/data/aerospike/query/cache/IndexRefresher.java index 48285cf26..0919bd604 100644 --- a/src/main/java/org/springframework/data/aerospike/query/cache/IndexRefresher.java +++ b/src/main/java/org/springframework/data/aerospike/query/cache/IndexRefresher.java @@ -22,6 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.aerospike.query.model.IndexesInfo; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import java.util.Arrays; import java.util.concurrent.Executors; @@ -38,21 +39,24 @@ public class IndexRefresher { private final IAerospikeClient client; private final InfoPolicy infoPolicy; + private final ServerVersionSupport serverVersionSupport; private final InternalIndexOperations indexOperations; private final IndexesCacheUpdater indexesCacheUpdater; private final ScheduledExecutorService executorService; - public IndexRefresher(IAerospikeClient client, InfoPolicy infoPolicy, - InternalIndexOperations indexOperations, IndexesCacheUpdater indexesCacheUpdater) { + public IndexRefresher(IAerospikeClient client, InfoPolicy infoPolicy, InternalIndexOperations indexOperations, + IndexesCacheUpdater indexesCacheUpdater, ServerVersionSupport serverVersionSupport) { this.client = client; this.infoPolicy = infoPolicy; this.indexOperations = indexOperations; this.indexesCacheUpdater = indexesCacheUpdater; this.executorService = Executors.newSingleThreadScheduledExecutor(); + this.serverVersionSupport = serverVersionSupport; } public void scheduleRefreshIndexes(long intervalSeconds) { - executorService.scheduleWithFixedDelay(this::refreshIndexes, intervalSeconds, intervalSeconds, TimeUnit.SECONDS); + executorService.scheduleWithFixedDelay(this::refreshIndexes, intervalSeconds, intervalSeconds, + TimeUnit.SECONDS); } public void refreshIndexes() { @@ -64,7 +68,7 @@ public void refreshIndexes() { .map(node -> Info.request(infoPolicy, node, indexOperations.buildGetIndexesCommand())) .map(response -> { IndexesInfo indexesInfo = indexOperations.parseIndexesInfo(response); - indexOperations.enrichIndexesWithCardinality(client, indexesInfo.indexes); + indexOperations.enrichIndexesWithCardinality(client, indexesInfo.indexes, serverVersionSupport); return indexesInfo; }) .orElse(IndexesInfo.empty()); diff --git a/src/main/java/org/springframework/data/aerospike/query/cache/InternalIndexOperations.java b/src/main/java/org/springframework/data/aerospike/query/cache/InternalIndexOperations.java index 65ca2b1cb..c128bd312 100644 --- a/src/main/java/org/springframework/data/aerospike/query/cache/InternalIndexOperations.java +++ b/src/main/java/org/springframework/data/aerospike/query/cache/InternalIndexOperations.java @@ -21,7 +21,7 @@ import org.springframework.data.aerospike.query.model.Index; import org.springframework.data.aerospike.query.model.IndexKey; import org.springframework.data.aerospike.query.model.IndexesInfo; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import java.util.Arrays; import java.util.Collections; @@ -68,18 +68,20 @@ public String buildGetIndexesCommand() { return SINDEX_WITH_BASE64; } - public void enrichIndexesWithCardinality(IAerospikeClient client, Map indexes) { + public void enrichIndexesWithCardinality(IAerospikeClient client, Map indexes, + ServerVersionSupport serverVersionSupport) { log.debug("Enriching secondary indexes with cardinality"); indexes.values().forEach( - index -> index.setBinValuesRatio(getIndexBinValuesRatio(client, index.getNamespace(), index.getName())) + index -> index.setBinValuesRatio(getIndexBinValuesRatio(client, serverVersionSupport, index.getNamespace(), + index.getName())) ); } - public int getIndexBinValuesRatio(IAerospikeClient client, String namespace, String indexName) { - if (ServerVersionUtils.isSIndexCardinalitySupported(client)) { - + public int getIndexBinValuesRatio(IAerospikeClient client, ServerVersionSupport serverVersionSupport, + String namespace, String indexName) { + if (serverVersionSupport.sIndexCardinality()) { try { - String indexStatData = Info.request(null, client.getCluster().getRandomNode(), + String indexStatData = Info.request(client.getInfoPolicyDefault(), client.getCluster().getRandomNode(), String.format("sindex-stat:ns=%s;indexname=%s", namespace, indexName)); return Integer.parseInt( diff --git a/src/main/java/org/springframework/data/aerospike/query/cache/ReactorIndexRefresher.java b/src/main/java/org/springframework/data/aerospike/query/cache/ReactorIndexRefresher.java index 77a5581b6..8cc4389cd 100644 --- a/src/main/java/org/springframework/data/aerospike/query/cache/ReactorIndexRefresher.java +++ b/src/main/java/org/springframework/data/aerospike/query/cache/ReactorIndexRefresher.java @@ -20,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.aerospike.query.model.IndexesInfo; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import reactor.core.publisher.Mono; /** @@ -31,15 +32,18 @@ public class ReactorIndexRefresher { private final IAerospikeReactorClient client; private final InfoPolicy infoPolicy; + private final ServerVersionSupport serverVersionSupport; private final InternalIndexOperations indexOperations; private final IndexesCacheUpdater indexesCacheUpdater; public ReactorIndexRefresher(IAerospikeReactorClient client, InfoPolicy infoPolicy, - InternalIndexOperations indexOperations, IndexesCacheUpdater indexesCacheUpdater) { + InternalIndexOperations indexOperations, IndexesCacheUpdater indexesCacheUpdater, + ServerVersionSupport serverVersionSupport) { this.client = client; this.infoPolicy = infoPolicy; this.indexOperations = indexOperations; this.indexesCacheUpdater = indexesCacheUpdater; + this.serverVersionSupport = serverVersionSupport; } public Mono refreshIndexes() { @@ -47,7 +51,8 @@ public Mono refreshIndexes() { .doOnSubscribe(subscription -> log.trace("Loading indexes")) .doOnNext(indexInfo -> { IndexesInfo cache = indexOperations.parseIndexesInfo(indexInfo); - indexOperations.enrichIndexesWithCardinality(client.getAerospikeClient(), cache.indexes); + indexOperations.enrichIndexesWithCardinality(client.getAerospikeClient(), cache.indexes, + serverVersionSupport); this.indexesCacheUpdater.update(cache); log.debug("Loaded indexes: {}", cache.indexes); }).then(); diff --git a/src/main/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepository.java b/src/main/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepository.java index 0c57e4fe1..239fd3b31 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepository.java +++ b/src/main/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepository.java @@ -129,8 +129,11 @@ public void deleteById(ID id) { @Override public void deleteAll(Iterable entities) { Assert.notNull(entities, "The given entities for deleting must not be null!"); - List ids = new ArrayList<>(); - entities.forEach(entity -> ids.add(entityInformation.getId(entity))); + List ids = new ArrayList<>(); + entities.forEach(entity -> { + Assert.notNull(entity, "The given Iterable of entities must not contain null!"); + ids.add(entityInformation.getId(entity)); + }); operations.deleteByIds(ids, entityInformation.getJavaType()); } diff --git a/src/main/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepository.java b/src/main/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepository.java index 82e9d30c3..8798c4629 100644 --- a/src/main/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepository.java +++ b/src/main/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepository.java @@ -26,6 +26,9 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.util.ArrayList; +import java.util.List; + /** * Stub implementation of {@link ReactiveAerospikeRepository}. * @@ -129,9 +132,12 @@ public Mono deleteAllById(Iterable ids) { @Override public Mono deleteAll(Iterable entities) { Assert.notNull(entities, "The given Iterable of entities must not be null!"); - entities.forEach(entity -> - Assert.notNull(entity, "The given Iterable of entities must not contain null!")); - return Flux.fromIterable(entities).flatMap(this::delete).then(); + List ids = new ArrayList<>(); + entities.forEach(entity -> { + Assert.notNull(entity, "The given Iterable of entities must not contain null!"); + ids.add(entityInformation.getId(entity)); + }); + return operations.deleteByIds(ids, entityInformation.getJavaType()); } @Override diff --git a/src/main/java/org/springframework/data/aerospike/server/version/ServerVersionSupport.java b/src/main/java/org/springframework/data/aerospike/server/version/ServerVersionSupport.java new file mode 100644 index 000000000..1514c582a --- /dev/null +++ b/src/main/java/org/springframework/data/aerospike/server/version/ServerVersionSupport.java @@ -0,0 +1,74 @@ +package org.springframework.data.aerospike.server.version; + +import com.aerospike.client.IAerospikeClient; +import com.aerospike.client.Info; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.lang.module.ModuleDescriptor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class ServerVersionSupport { + + private static final ModuleDescriptor.Version SERVER_VERSION_6_0_0_0 = ModuleDescriptor.Version.parse("6.0.0.0"); + private static final ModuleDescriptor.Version SERVER_VERSION_6_1_0_0 = ModuleDescriptor.Version.parse("6.1.0.0"); + private static final ModuleDescriptor.Version SERVER_VERSION_6_1_0_1 = ModuleDescriptor.Version.parse("6.1.0.1"); + private static final ModuleDescriptor.Version SERVER_VERSION_6_3_0_0 = ModuleDescriptor.Version.parse("6.3.0.0"); + + private final IAerospikeClient client; + private final ScheduledExecutorService executorService; + @Getter + private volatile String serverVersion; + + public ServerVersionSupport(IAerospikeClient client) { + this.client = client; + this.serverVersion = findServerVersion(); + this.executorService = Executors.newSingleThreadScheduledExecutor(); + } + + public void scheduleServerVersionRefresh(long intervalSeconds) { + executorService.scheduleWithFixedDelay( + () -> serverVersion = findServerVersion(), + intervalSeconds, + intervalSeconds, + TimeUnit.SECONDS); + } + + private String findServerVersion() { + String versionString = Info.request(client.getInfoPolicyDefault(), + client.getCluster().getRandomNode(), "version"); + versionString = versionString.substring(versionString.lastIndexOf(' ') + 1); + log.debug("Found server version {}", versionString); + return versionString; + } + + /** + * Since Aerospike Server ver. 6.1.0.1 attempting to create a secondary index which already exists or to drop a + * non-existing secondary index returns success/OK instead of an error. + */ + public boolean isDropCreateBehaviorUpdated() { + return ModuleDescriptor.Version.parse(getServerVersion()) + .compareTo(SERVER_VERSION_6_1_0_1) >= 0; + } + + /** + * Since Aerospike Server ver. 6.3.0.0 find by POJO is supported. + */ + public boolean findByPojo() { + return ModuleDescriptor.Version.parse(getServerVersion()) + .compareTo(SERVER_VERSION_6_3_0_0) >= 0; + } + + public boolean batchWrite() { + return ModuleDescriptor.Version.parse(getServerVersion()) + .compareTo(SERVER_VERSION_6_0_0_0) >= 0; + } + + public boolean sIndexCardinality() { + return ModuleDescriptor.Version.parse(getServerVersion()) + .compareTo(SERVER_VERSION_6_1_0_0) >= 0; + } +} diff --git a/src/main/java/org/springframework/data/aerospike/utility/ServerVersionUtils.java b/src/main/java/org/springframework/data/aerospike/utility/ServerVersionUtils.java deleted file mode 100644 index 03ef72dfb..000000000 --- a/src/main/java/org/springframework/data/aerospike/utility/ServerVersionUtils.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.springframework.data.aerospike.utility; - -import com.aerospike.client.IAerospikeClient; -import com.aerospike.client.Info; -import lombok.experimental.UtilityClass; - -import java.lang.module.ModuleDescriptor; - -@UtilityClass -public class ServerVersionUtils { - - private static final ModuleDescriptor.Version SERVER_VERSION_6_0_0_0 = ModuleDescriptor.Version.parse("6.0.0.0"); - private static final ModuleDescriptor.Version SERVER_VERSION_6_1_0_0 = ModuleDescriptor.Version.parse("6.1.0.0"); - private static final ModuleDescriptor.Version SERVER_VERSION_6_1_0_1 = ModuleDescriptor.Version.parse("6.1.0.1"); - private static final ModuleDescriptor.Version SERVER_VERSION_6_3_0_0 = ModuleDescriptor.Version.parse("6.3.0.0"); - - public static String getServerVersion(IAerospikeClient client) { - String versionString = Info.request(client.getCluster().getRandomNode(), "version"); - return versionString.substring(versionString.lastIndexOf(' ') + 1); - } - - /** - * Since Aerospike Server ver. 6.1.0.1 attempting to create a secondary index which already exists or to drop a - * non-existing secondary index now returns success/OK instead of an error. - */ - public static boolean isDropCreateBehaviorUpdated(IAerospikeClient client) { - return ModuleDescriptor.Version.parse(ServerVersionUtils.getServerVersion(client)) - .compareTo(SERVER_VERSION_6_1_0_1) >= 0; - } - - /** - * Since Aerospike Server ver. 6.3.0.0 find by POJO is supported. - */ - public static boolean isFindByPojoSupported(IAerospikeClient client) { - return ModuleDescriptor.Version.parse(ServerVersionUtils.getServerVersion(client)) - .compareTo(SERVER_VERSION_6_3_0_0) >= 0; - } - - public static boolean isBatchWriteSupported(IAerospikeClient client) { - return ModuleDescriptor.Version.parse(ServerVersionUtils.getServerVersion(client)) - .compareTo(SERVER_VERSION_6_0_0_0) >= 0; - } - - public static boolean isSIndexCardinalitySupported(IAerospikeClient client) { - return ModuleDescriptor.Version.parse(ServerVersionUtils.getServerVersion(client)) - .compareTo(SERVER_VERSION_6_1_0_0) >= 0; - } -} diff --git a/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java b/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java index 98f02fa73..7d694847f 100644 --- a/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java +++ b/src/test/java/org/springframework/data/aerospike/BaseBlockingIntegrationTests.java @@ -9,6 +9,7 @@ import org.springframework.data.aerospike.query.QueryEngine; import org.springframework.data.aerospike.query.cache.IndexRefresher; import org.springframework.data.aerospike.query.cache.IndexesCache; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import java.util.Collection; @@ -29,6 +30,8 @@ public abstract class BaseBlockingIntegrationTests extends BaseIntegrationTests @Autowired protected QueryEngine queryEngine; @Autowired + protected ServerVersionSupport serverVersionSupport; + @Autowired protected IndexesCache indexesCache; @Autowired protected IndexRefresher indexRefresher; diff --git a/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java b/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java index 06355fa98..cf0419905 100644 --- a/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java +++ b/src/test/java/org/springframework/data/aerospike/BaseReactiveIntegrationTests.java @@ -7,11 +7,10 @@ import org.springframework.data.aerospike.config.ReactiveTestConfig; import org.springframework.data.aerospike.core.ReactiveAerospikeTemplate; import org.springframework.data.aerospike.query.cache.ReactorIndexRefresher; -import org.springframework.data.aerospike.sample.Person; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import reactor.core.publisher.Flux; import java.io.Serializable; -import java.util.Collection; @SpringBootTest( classes = {ReactiveTestConfig.class, CommonTestConfig.class}, @@ -28,6 +27,8 @@ public abstract class BaseReactiveIntegrationTests extends BaseIntegrationTests @Autowired protected IAerospikeReactorClient reactorClient; @Autowired + protected ServerVersionSupport serverVersionSupport; + @Autowired protected ReactorIndexRefresher reactorIndexRefresher; protected T findById(Serializable id, Class type) { @@ -38,11 +39,11 @@ protected T findById(Serializable id, Class type, String setName) { return reactiveTemplate.findById(id, type, setName).block(); } - protected void deleteAll(Collection persons) { - Flux.fromIterable(persons).flatMap(person -> reactiveTemplate.delete(person)).blockLast(); + protected void deleteAll(Iterable iterable) { + Flux.fromIterable(iterable).flatMap(item -> reactiveTemplate.delete(item)).blockLast(); } - protected void deleteAll(Collection persons, String setName) { - Flux.fromIterable(persons).flatMap(person -> reactiveTemplate.delete(person, setName)).blockLast(); + protected void deleteAll(Iterable iterable, String setName) { + Flux.fromIterable(iterable).flatMap(item -> reactiveTemplate.delete(item, setName)).blockLast(); } } diff --git a/src/test/java/org/springframework/data/aerospike/BlockingAerospikeTestOperations.java b/src/test/java/org/springframework/data/aerospike/BlockingAerospikeTestOperations.java index 345e34262..846149247 100644 --- a/src/test/java/org/springframework/data/aerospike/BlockingAerospikeTestOperations.java +++ b/src/test/java/org/springframework/data/aerospike/BlockingAerospikeTestOperations.java @@ -3,9 +3,10 @@ import com.aerospike.client.IAerospikeClient; import org.springframework.data.aerospike.core.AerospikeTemplate; import org.springframework.data.aerospike.query.cache.IndexInfoParser; -import org.springframework.data.aerospike.utility.AdditionalAerospikeTestOperations; import org.springframework.data.aerospike.sample.Customer; import org.springframework.data.aerospike.sample.Person; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; +import org.springframework.data.aerospike.utility.AdditionalAerospikeTestOperations; import org.testcontainers.containers.GenericContainer; import java.util.List; @@ -21,8 +22,9 @@ public class BlockingAerospikeTestOperations extends AdditionalAerospikeTestOper public BlockingAerospikeTestOperations(IndexInfoParser indexInfoParser, AerospikeTemplate template, IAerospikeClient client, - GenericContainer aerospikeContainer) { - super(indexInfoParser, client, template, aerospikeContainer); + GenericContainer aerospikeContainer, + ServerVersionSupport serverVersionSupport) { + super(indexInfoParser, client, serverVersionSupport, template, aerospikeContainer); this.template = template; } @@ -56,7 +58,7 @@ protected String getSetName(Class clazz) { return template.getSetName(clazz); } - public List generateCustomers(int count) { + public List saveGeneratedCustomers(int count) { return IntStream.range(0, count) .mapToObj(i -> Customer.builder().id(nextId()) .firstName("firstName" + i) @@ -66,7 +68,7 @@ public List generateCustomers(int count) { .collect(Collectors.toList()); } - public List generatePersons(int count) { + public List saveGeneratedPersons(int count) { return IntStream.range(0, count) .mapToObj(i -> Person.builder().id(nextId()) .firstName("firstName" + i) diff --git a/src/test/java/org/springframework/data/aerospike/PoliciesVerificationTests.java b/src/test/java/org/springframework/data/aerospike/PoliciesVerificationTests.java index 292c730e1..3419fbc81 100644 --- a/src/test/java/org/springframework/data/aerospike/PoliciesVerificationTests.java +++ b/src/test/java/org/springframework/data/aerospike/PoliciesVerificationTests.java @@ -1,9 +1,13 @@ package org.springframework.data.aerospike; import org.junit.jupiter.api.Test; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class PoliciesVerificationTests extends BaseBlockingIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/ReactiveBlockingAerospikeTestOperations.java b/src/test/java/org/springframework/data/aerospike/ReactiveBlockingAerospikeTestOperations.java index 87905a093..c623b2851 100644 --- a/src/test/java/org/springframework/data/aerospike/ReactiveBlockingAerospikeTestOperations.java +++ b/src/test/java/org/springframework/data/aerospike/ReactiveBlockingAerospikeTestOperations.java @@ -7,8 +7,8 @@ import org.springframework.data.aerospike.repository.ReactiveAerospikeRepository; import org.springframework.data.aerospike.sample.Customer; import org.springframework.data.aerospike.sample.Person; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.aerospike.utility.AdditionalAerospikeTestOperations; -import org.springframework.data.aerospike.utility.ServerVersionUtils; import org.testcontainers.containers.GenericContainer; import java.util.Collection; @@ -21,12 +21,15 @@ public class ReactiveBlockingAerospikeTestOperations extends AdditionalAerospikeTestOperations { private final ReactiveAerospikeTemplate template; + private final ServerVersionSupport serverVersionSupport; public ReactiveBlockingAerospikeTestOperations(IndexInfoParser indexInfoParser, IAerospikeClient client, GenericContainer aerospike, - ReactiveAerospikeTemplate reactiveAerospikeTemplate) { - super(indexInfoParser, client, reactiveAerospikeTemplate, aerospike); + ReactiveAerospikeTemplate reactiveAerospikeTemplate, + ServerVersionSupport serverVersionSupport) { + super(indexInfoParser, client, serverVersionSupport, reactiveAerospikeTemplate, aerospike); this.template = reactiveAerospikeTemplate; + this.serverVersionSupport = serverVersionSupport; } @Override @@ -59,29 +62,29 @@ protected String getSetName(Class clazz) { return template.getSetName(clazz); } - public List generateCustomers(int count) { + public List saveGeneratedCustomers(int count) { return IntStream.range(0, count) .mapToObj(i -> Customer.builder().id(nextId()) .firstName("firstName" + i) .lastName("lastName") .build()) - .peek(template::save) + .peek(document -> template.save(document).block()) .collect(Collectors.toList()); } - public List generatePersons(int count) { + public List saveGeneratedPersons(int count) { return IntStream.range(0, count) .mapToObj(i -> Person.builder().id(nextId()) .firstName("firstName" + i) .emailAddress("mail.com") .build()) - .peek(template::save) + .peek(document -> template.save(document).block()) .collect(Collectors.toList()); } public void deleteAll(ReactiveAerospikeRepository repository, Collection entities) { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(template.getAerospikeReactorClient().getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { try { repository.deleteAll(entities).block(); } catch (AerospikeException.BatchRecordArray ignored) { @@ -94,7 +97,7 @@ public void deleteAll(ReactiveAerospikeRepository repository, Collecti public void saveAll(ReactiveAerospikeRepository repository, Collection entities) { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(template.getAerospikeReactorClient().getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { repository.saveAll(entities).blockLast(); } else { entities.forEach(entity -> repository.save(entity).block()); diff --git a/src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheManagerIntegrationTests.java b/src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheManagerIntegrationTests.java index d556ab5fa..04e003c65 100644 --- a/src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheManagerIntegrationTests.java +++ b/src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheManagerIntegrationTests.java @@ -28,10 +28,14 @@ import org.springframework.data.aerospike.core.AerospikeOperations; import org.springframework.data.aerospike.utility.AwaitilityUtils; import org.springframework.data.annotation.Id; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.utility.AwaitilityUtils.awaitTenSecondsUntil; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeCacheManagerIntegrationTests extends BaseBlockingIntegrationTests { private static final String KEY = "foo"; diff --git a/src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheMangerTests.java b/src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheManagerTests.java similarity index 90% rename from src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheMangerTests.java rename to src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheManagerTests.java index 537105358..3c7e86cb7 100644 --- a/src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheMangerTests.java +++ b/src/test/java/org/springframework/data/aerospike/cache/AerospikeCacheManagerTests.java @@ -23,16 +23,20 @@ import org.springframework.cache.transaction.TransactionAwareCacheDecorator; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.convert.MappingAerospikeConverter; +import org.springframework.test.context.TestPropertySource; import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Venil Noronha */ -public class AerospikeCacheMangerTests extends BaseBlockingIntegrationTests { +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup +public class AerospikeCacheManagerTests extends BaseBlockingIntegrationTests { @Value("${embedded.aerospike.namespace}") public String namespace; diff --git a/src/test/java/org/springframework/data/aerospike/config/BlockingTestConfig.java b/src/test/java/org/springframework/data/aerospike/config/BlockingTestConfig.java index dec214f33..0dfd9ba4b 100644 --- a/src/test/java/org/springframework/data/aerospike/config/BlockingTestConfig.java +++ b/src/test/java/org/springframework/data/aerospike/config/BlockingTestConfig.java @@ -8,12 +8,13 @@ import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; import org.springframework.data.aerospike.BlockingAerospikeTestOperations; -import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.aerospike.core.AerospikeTemplate; import org.springframework.data.aerospike.query.cache.IndexInfoParser; import org.springframework.data.aerospike.repository.config.EnableAerospikeRepositories; import org.springframework.data.aerospike.sample.ContactRepository; import org.springframework.data.aerospike.sample.CustomerRepository; +import org.springframework.data.aerospike.sample.SampleClasses; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.aerospike.utility.AdditionalAerospikeTestOperations; import org.testcontainers.containers.GenericContainer; @@ -67,7 +68,7 @@ protected void configureDataSettings(AerospikeDataSettings.AerospikeDataSettings boolean indexesOnStartup = Boolean.parseBoolean(env.getProperty("createIndexesOnStartup")); builder.createIndexesOnStartup(indexesOnStartup); Optional indexRefreshFrequency = getIntegerProperty(env.getProperty(INDEX_CACHE_REFRESH_SECONDS)); - indexRefreshFrequency.ifPresent(builder::indexCacheRefreshFrequencySeconds); + indexRefreshFrequency.ifPresent(builder::indexCacheRefreshSeconds); builder.queryMaxRecords(5000L); } @@ -82,7 +83,9 @@ protected ClientPolicy getClientPolicy() { @Bean public AdditionalAerospikeTestOperations aerospikeOperations(AerospikeTemplate template, IAerospikeClient client, - GenericContainer aerospike) { - return new BlockingAerospikeTestOperations(new IndexInfoParser(), template, client, aerospike); + GenericContainer aerospike, + ServerVersionSupport serverVersionSupport) { + return new BlockingAerospikeTestOperations(new IndexInfoParser(), template, client, aerospike, + serverVersionSupport); } } diff --git a/src/test/java/org/springframework/data/aerospike/config/ReactiveTestConfig.java b/src/test/java/org/springframework/data/aerospike/config/ReactiveTestConfig.java index 11296d095..991d94f73 100644 --- a/src/test/java/org/springframework/data/aerospike/config/ReactiveTestConfig.java +++ b/src/test/java/org/springframework/data/aerospike/config/ReactiveTestConfig.java @@ -16,11 +16,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; import org.springframework.data.aerospike.ReactiveBlockingAerospikeTestOperations; -import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.aerospike.core.ReactiveAerospikeTemplate; import org.springframework.data.aerospike.query.cache.IndexInfoParser; import org.springframework.data.aerospike.repository.config.EnableReactiveAerospikeRepositories; import org.springframework.data.aerospike.sample.ReactiveCustomerRepository; +import org.springframework.data.aerospike.sample.SampleClasses; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.springframework.data.aerospike.utility.AdditionalAerospikeTestOperations; import org.testcontainers.containers.GenericContainer; @@ -95,14 +96,16 @@ protected void configureDataSettings(AerospikeDataSettings.AerospikeDataSettings boolean indexesOnStartup = Boolean.parseBoolean(env.getProperty("createIndexesOnStartup")); builder.createIndexesOnStartup(indexesOnStartup); Optional indexRefreshFrequency = getIntegerProperty(env.getProperty(INDEX_CACHE_REFRESH_SECONDS)); - indexRefreshFrequency.ifPresent(builder::indexCacheRefreshFrequencySeconds); + indexRefreshFrequency.ifPresent(builder::indexCacheRefreshSeconds); builder.queryMaxRecords(5000L); } @Bean public AdditionalAerospikeTestOperations aerospikeOperations(ReactiveAerospikeTemplate template, IAerospikeClient client, - GenericContainer aerospike) { - return new ReactiveBlockingAerospikeTestOperations(new IndexInfoParser(), client, aerospike, template); + GenericContainer aerospike, + ServerVersionSupport serverVersionSupport) { + return new ReactiveBlockingAerospikeTestOperations(new IndexInfoParser(), client, aerospike, template, + serverVersionSupport); } } diff --git a/src/test/java/org/springframework/data/aerospike/convert/AerospikeReadDataTest.java b/src/test/java/org/springframework/data/aerospike/convert/AerospikeReadDataTest.java index 7cfac8b36..53f8d5963 100644 --- a/src/test/java/org/springframework/data/aerospike/convert/AerospikeReadDataTest.java +++ b/src/test/java/org/springframework/data/aerospike/convert/AerospikeReadDataTest.java @@ -6,11 +6,15 @@ import com.aerospike.client.Key; import com.aerospike.client.Record; import org.junit.jupiter.api.Test; +import org.springframework.test.context.TestPropertySource; import java.util.Collections; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeReadDataTest { @Test diff --git a/src/test/java/org/springframework/data/aerospike/convert/BaseMappingAerospikeConverterTest.java b/src/test/java/org/springframework/data/aerospike/convert/BaseMappingAerospikeConverterTest.java index e2df2d5d0..6df7af938 100644 --- a/src/test/java/org/springframework/data/aerospike/convert/BaseMappingAerospikeConverterTest.java +++ b/src/test/java/org/springframework/data/aerospike/convert/BaseMappingAerospikeConverterTest.java @@ -5,9 +5,9 @@ import org.springframework.context.ApplicationContext; import org.springframework.core.convert.converter.Converter; import org.springframework.core.env.Environment; -import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.aerospike.config.AerospikeDataSettings; import org.springframework.data.aerospike.mapping.AerospikeMappingContext; +import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.convert.CustomConversions; import java.util.Collection; diff --git a/src/test/java/org/springframework/data/aerospike/core/AbstractFindByEntitiesTest.java b/src/test/java/org/springframework/data/aerospike/core/AbstractFindByEntitiesTest.java index 640311e8e..74d6f357f 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AbstractFindByEntitiesTest.java +++ b/src/test/java/org/springframework/data/aerospike/core/AbstractFindByEntitiesTest.java @@ -25,8 +25,8 @@ public interface AbstractFindByEntitiesTest { @Test default void shouldFindAllRequestedEntities() { - List persons = generatePersons(5); - List customers = generateCustomers(3); + List persons = saveGeneratedPersons(5); + List customers = saveGeneratedCustomers(3); GroupedKeys groupedKeys = getGroupedKeys(persons, customers); GroupedEntities byIds = findByIds(groupedKeys); @@ -38,8 +38,8 @@ default void shouldFindAllRequestedEntities() { @Test default void shouldReturnAnEmptyResultIfKeysWhereSetToWrongEntities() { - List persons = generatePersons(5); - List customers = generateCustomers(3); + List persons = saveGeneratedPersons(5); + List customers = saveGeneratedCustomers(3); Set requestedPersonsIds = persons.stream() .map(Person::getId) @@ -59,8 +59,8 @@ default void shouldReturnAnEmptyResultIfKeysWhereSetToWrongEntities() { @Test default void shouldFindSomeOfIdsOfRequestedEntities() { - List persons = generatePersons(2); - List customers = generateCustomers(3); + List persons = saveGeneratedPersons(2); + List customers = saveGeneratedCustomers(3); GroupedKeys requestMapWithRandomExtraIds = getGroupedEntitiesKeysWithRandomExtraIds(persons, customers); GroupedEntities results = findByIds(requestMapWithRandomExtraIds); @@ -72,7 +72,7 @@ default void shouldFindSomeOfIdsOfRequestedEntities() { @Test default void shouldFindResultsOfOneOfRequestedEntity() { - List persons = generatePersons(3); + List persons = saveGeneratedPersons(3); GroupedKeys groupedKeysWithRandomExtraIds = getGroupedEntitiesKeysWithRandomExtraIds(persons, emptyList()); GroupedEntities results = findByIds(groupedKeysWithRandomExtraIds); @@ -84,7 +84,7 @@ default void shouldFindResultsOfOneOfRequestedEntity() { @Test default void shouldFindForOneEntityIfAnotherContainsEmptyRequestList() { - List persons = generatePersons(3); + List persons = saveGeneratedPersons(3); GroupedKeys groupedKeys = getGroupedKeys(persons, emptyList()); GroupedEntities batchGroupedEntities = findByIds(groupedKeys); @@ -126,7 +126,7 @@ default void shouldReturnMapWithEmptyResultsIfNoEntitiesWhereFound() { @Test default void shouldThrowMappingExceptionOnNonAerospikeEntityClass() { - List persons = generatePersons(2); + List persons = saveGeneratedPersons(2); Set personIds = persons.stream() .map(Person::getId) .collect(Collectors.toSet()); @@ -188,7 +188,7 @@ default GroupedKeys getGroupedEntitiesKeysWithRandomExtraIds(Collection .build(); } - default List generateCustomers(int count) { + default List saveGeneratedCustomers(int count) { return IntStream.range(0, count) .mapToObj(i -> Customer.builder().id(nextId()) .firstName("firstName" + i) @@ -198,7 +198,7 @@ default List generateCustomers(int count) { .collect(Collectors.toList()); } - default List generatePersons(int count) { + default List saveGeneratedPersons(int count) { return IntStream.range(0, count) .mapToObj(i -> Person.builder().id(nextId()) .firstName("firstName" + i) diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeExpirationTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeExpirationTests.java index d27361ece..288e6c1f2 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeExpirationTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeExpirationTests.java @@ -29,6 +29,7 @@ import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithExpirationAnnotation; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithExpirationOneDay; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithUnixTimeExpiration; +import org.springframework.test.context.TestPropertySource; import java.time.Duration; @@ -36,12 +37,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.data.Offset.offset; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.sample.SampleClasses.DocumentWithExpirationAnnotationAndPersistenceConstructor; import static org.springframework.data.aerospike.utility.AerospikeExpirationPolicy.DO_NOT_UPDATE_EXPIRATION; import static org.springframework.data.aerospike.utility.AerospikeExpirationPolicy.NEVER_EXPIRE; import static org.springframework.data.aerospike.utility.AwaitilityUtils.awaitTenSecondsUntil; import static org.springframework.data.aerospike.utility.AwaitilityUtils.awaitTwoSecondsUntil; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeExpirationTests extends BaseBlockingIntegrationTests { @AfterEach diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateAddTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateAddTests.java index 771cb433b..75063da28 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateAddTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateAddTests.java @@ -18,12 +18,16 @@ import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateAddTests extends BaseBlockingIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateAppendTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateAppendTests.java index 3ecf134d6..ddb594b4e 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateAppendTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateAppendTests.java @@ -18,12 +18,16 @@ import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateAppendTests extends BaseBlockingIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCompositeKeyTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCompositeKeyTests.java index 92fc06250..1361ea735 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCompositeKeyTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCompositeKeyTests.java @@ -5,12 +5,16 @@ import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.sample.SampleClasses.CompositeKey; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithCompositeKey; +import org.springframework.test.context.TestPropertySource; import java.util.List; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateCompositeKeyTests extends BaseBlockingIntegrationTests { private DocumentWithCompositeKey document; diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCountTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCountTests.java index 00655cf14..a6dd7dd08 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCountTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateCountTests.java @@ -26,14 +26,18 @@ import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import java.time.Duration; import java.util.Objects; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateCountTests extends BaseBlockingIntegrationTests { @BeforeAll diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateDeleteTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateDeleteTests.java index 4a78eb165..6861559a5 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateDeleteTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateDeleteTests.java @@ -17,15 +17,16 @@ import com.aerospike.client.AerospikeException; import com.aerospike.client.policy.GenerationPolicy; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; -import org.springframework.data.aerospike.sample.SampleClasses.CustomCollectionClassToDelete; -import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithExpiration; -import org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; import org.springframework.data.aerospike.core.model.GroupedKeys; import org.springframework.data.aerospike.sample.Customer; import org.springframework.data.aerospike.sample.Person; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.data.aerospike.sample.SampleClasses.CustomCollectionClassToDelete; +import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithExpiration; +import org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; +import org.springframework.test.context.TestPropertySource; import java.util.Arrays; import java.util.Collection; @@ -37,9 +38,18 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.awaitility.Awaitility.await; import static org.awaitility.Durations.TEN_SECONDS; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateDeleteTests extends BaseBlockingIntegrationTests { + @BeforeEach + public void beforeEach() { + template.deleteAll(Person.class); + template.deleteAll(Customer.class); + } + @Test public void deleteByObject_ignoresDocumentVersionEvenIfDefaultGenerationPolicyIsSet() { GenerationPolicy initialGenerationPolicy = client.getWritePolicyDefault().generationPolicy; @@ -127,10 +137,10 @@ public void deleteById_returnsFalseIfValueIsAbsent() { @Test public void deleteByGroupedKeys() { - if (ServerVersionUtils.isBatchWriteSupported(client)) { - List persons = additionalAerospikeTestOperations.generatePersons(5); + if (serverVersionSupport.batchWrite()) { + List persons = additionalAerospikeTestOperations.saveGeneratedPersons(5); List personsIds = persons.stream().map(Person::getId).toList(); - List customers = additionalAerospikeTestOperations.generateCustomers(3); + List customers = additionalAerospikeTestOperations.saveGeneratedCustomers(3); List customersIds = customers.stream().map(Customer::getId).toList(); GroupedKeys groupedKeys = getGroupedKeys(persons, customers); @@ -207,7 +217,7 @@ public void deleteByType_NullTypeThrowsException() { @Test public void deleteAll_rejectsDuplicateIds() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { String id1 = nextId(); DocumentWithExpiration document1 = new DocumentWithExpiration(id1); DocumentWithExpiration document2 = new DocumentWithExpiration(id1); @@ -224,7 +234,7 @@ public void deleteAll_rejectsDuplicateIds() { @Test public void deleteAll_ShouldDeleteAllDocuments() { // batch delete operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { String id1 = nextId(); String id2 = nextId(); template.save(new DocumentWithExpiration(id1)); @@ -232,15 +242,24 @@ public void deleteAll_ShouldDeleteAllDocuments() { List ids = List.of(id1, id2); template.deleteByIds(ids, DocumentWithExpiration.class); - assertThat(template.findByIds(ids, DocumentWithExpiration.class)).isEmpty(); + + List persons = additionalAerospikeTestOperations.saveGeneratedPersons(101); + ids = persons.stream().map(Person::getId).toList(); + template.deleteByIds(ids, Person.class); + assertThat(template.findByIds(ids, Person.class)).isEmpty(); + + List persons2 = additionalAerospikeTestOperations.saveGeneratedPersons(1001); + ids = persons2.stream().map(Person::getId).toList(); + template.deleteByIds(ids, Person.class); + assertThat(template.findByIds(ids, Person.class)).isEmpty(); } } @Test public void deleteAll_ShouldDeleteAllDocumentsWithSetName() { // batch delete operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { String id1 = nextId(); String id2 = nextId(); template.save(new DocumentWithExpiration(id1), OVERRIDE_SET_NAME); diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateExecuteTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateExecuteTests.java index b5de868ee..fedd7c71c 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateExecuteTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateExecuteTests.java @@ -23,9 +23,13 @@ import org.junit.jupiter.api.Test; import org.springframework.dao.DuplicateKeyException; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateExecuteTests extends BaseBlockingIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateExistsTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateExistsTests.java index 5e773d3fc..a17e737c0 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateExistsTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateExistsTests.java @@ -18,9 +18,13 @@ import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateExistsTests extends BaseBlockingIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByIdProjectionTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByIdProjectionTests.java index d1e3cb275..e0796c0d9 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByIdProjectionTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByIdProjectionTests.java @@ -6,13 +6,17 @@ import org.springframework.data.aerospike.sample.PersonMissingAndRedundantFields; import org.springframework.data.aerospike.sample.PersonSomeFields; import org.springframework.data.aerospike.sample.PersonTouchOnRead; +import org.springframework.test.context.TestPropertySource; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateFindByIdProjectionTests extends BaseBlockingIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByIdTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByIdTests.java index 5fd524318..1900bb3d3 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByIdTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByIdTests.java @@ -28,6 +28,7 @@ import org.springframework.data.aerospike.sample.SampleClasses.MapWithDoubleId; import org.springframework.data.aerospike.sample.SampleClasses.MapWithIntegerId; import org.springframework.data.aerospike.sample.SampleClasses.VersionedClassWithAllArgsConstructor; +import org.springframework.test.context.TestPropertySource; import java.util.Arrays; import java.util.Collections; @@ -36,9 +37,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.sample.SampleClasses.DocumentWithTouchOnReadAndExpirationProperty; import static org.springframework.data.aerospike.sample.SampleClasses.EXPIRATION_ONE_MINUTE; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateFindByIdTests extends BaseBlockingIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryProjectionTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryProjectionTests.java index caddbf57b..31a8eb5ab 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryProjectionTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryProjectionTests.java @@ -11,8 +11,8 @@ import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.sample.PersonSomeFields; import org.springframework.data.aerospike.utility.QueryUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; import org.springframework.data.domain.Sort; +import org.springframework.test.context.TestPropertySource; import java.util.Arrays; import java.util.List; @@ -20,8 +20,11 @@ import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateFindByQueryProjectionTests extends BaseBlockingIntegrationTests { final Person jean = Person.builder() @@ -54,7 +57,7 @@ public void beforeAllSetUp() { deleteOneByOne(allPersons, OVERRIDE_SET_NAME); // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { template.insertAll(allPersons); template.insertAll(allPersons, OVERRIDE_SET_NAME); } else { @@ -82,6 +85,17 @@ public void beforeAllSetUp() { @BeforeEach public void setUp() { super.setUp(); + template.deleteAll(Person.class); + template.deleteAll(OVERRIDE_SET_NAME); + if (serverVersionSupport.batchWrite()) { + template.insertAll(allPersons); + template.insertAll(allPersons, OVERRIDE_SET_NAME); + } else { + allPersons.forEach(person -> { + template.insert(person); + template.insert(person, OVERRIDE_SET_NAME); + }); + } } @AfterAll diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java index 6d56f3e1f..bba6e7b76 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateFindByQueryTests.java @@ -26,17 +26,17 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; -import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.aerospike.query.FilterOperation; import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.query.CriteriaDefinition; import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.sample.Address; import org.springframework.data.aerospike.sample.Person; +import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.aerospike.utility.CollectionUtils; import org.springframework.data.aerospike.utility.QueryUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; import org.springframework.data.domain.Sort; +import org.springframework.test.context.TestPropertySource; import java.util.Arrays; import java.util.Collections; @@ -47,9 +47,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.domain.Sort.Order.asc; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateFindByQueryTests extends BaseBlockingIntegrationTests { final Person jean = Person.builder().id(nextId()).firstName("Jean").lastName("Matthews").age(21) @@ -85,7 +88,7 @@ public void beforeAllSetUp() { deleteOneByOne(allPersons); // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { template.insertAll(allPersons); template.insertAll(allPersons, OVERRIDE_SET_NAME); } else { @@ -296,7 +299,7 @@ public void findAll_findNothing() { assertThat(result).isEmpty(); // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { template.insertAll(allPersons); } else { allPersons.forEach(person -> template.insert(person)); diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateIndexTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateIndexTests.java index ab74e64ef..7b5b729e1 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateIndexTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateIndexTests.java @@ -11,7 +11,6 @@ import org.springframework.data.aerospike.mapping.Document; import org.springframework.data.aerospike.query.model.Index; import org.springframework.data.aerospike.utility.AsyncUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; import org.springframework.test.context.TestPropertySource; import java.util.List; @@ -21,9 +20,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.utility.AwaitilityUtils.awaitTenSecondsUntil; -@TestPropertySource(properties = {"createIndexesOnStartup = true"}) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = true"}) // this test class requires secondary indexes created on startup public class AerospikeTemplateIndexTests extends BaseBlockingIntegrationTests { @@ -104,7 +104,7 @@ public void createIndexWithSetName_createsIndex() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void createIndex_shouldNotThrowExceptionIfIndexAlreadyExists() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(client)) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { template.createIndex(IndexedDocument.class, INDEX_TEST_1, "stringField", IndexType.STRING); awaitTenSecondsUntil(() -> assertThat(template.indexExists(INDEX_TEST_1)).isTrue()); @@ -179,7 +179,7 @@ public void createIndex_createsIndexForDifferentTypes() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void deleteIndex_doesNotThrowExceptionIfIndexDoesNotExist() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(client)) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { assertThatCode(() -> template.deleteIndex(IndexedDocument.class, "not-existing-index")) .doesNotThrowAnyException(); } @@ -188,7 +188,7 @@ public void deleteIndex_doesNotThrowExceptionIfIndexDoesNotExist() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void deleteIndexWithSetName_doesNotThrowExceptionIfIndexDoesNotExist() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(client)) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { assertThatCode(() -> template.deleteIndex(OVERRIDE_SET_NAME, "not-existing-index")) .doesNotThrowAnyException(); } @@ -197,7 +197,7 @@ public void deleteIndexWithSetName_doesNotThrowExceptionIfIndexDoesNotExist() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void createIndex_createsIndexOnNestedList() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(client)) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { String setName = template.getSetName(IndexedDocument.class); template.createIndex(IndexedDocument.class, INDEX_TEST_1, "nestedList", IndexType.STRING, IndexCollectionType.LIST, CTX.listIndex(1)); @@ -217,7 +217,7 @@ public void createIndex_createsIndexOnNestedList() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void createIndex_createsIndexOnNestedListContextRank() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(client)) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { String setName = template.getSetName(IndexedDocument.class); template.createIndex(IndexedDocument.class, INDEX_TEST_1, "nestedList", IndexType.STRING, IndexCollectionType.LIST, CTX.listRank(-1)); @@ -237,7 +237,7 @@ public void createIndex_createsIndexOnNestedListContextRank() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void createIndex_createsIndexOnMapOfMapsContext() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(client)) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { String setName = template.getSetName(IndexedDocument.class); CTX[] ctx = new CTX[]{ diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateInsertTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateInsertTests.java index 1197af2b7..5c6b51754 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateInsertTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateInsertTests.java @@ -20,14 +20,15 @@ import com.aerospike.client.Record; import com.aerospike.client.policy.Policy; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.dao.DuplicateKeyException; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; +import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.sample.SampleClasses.CustomCollectionClass; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithByteArray; -import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.utility.AsyncUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.test.context.TestPropertySource; import java.util.Arrays; import java.util.Collections; @@ -40,10 +41,21 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateInsertTests extends BaseBlockingIntegrationTests { + @BeforeEach + public void beforeEach() { + template.deleteAll(Person.class); + template.deleteAll(CustomCollectionClass.class); + template.deleteAll(DocumentWithByteArray.class); + template.deleteAll(VersionedClass.class); + } + @Test public void insertsAndFindsWithCustomCollectionSet() { CustomCollectionClass initial = new CustomCollectionClass(id, "data0"); @@ -54,7 +66,6 @@ public void insertsAndFindsWithCustomCollectionSet() { assertThat(record.getString("data")).isEqualTo("data0"); CustomCollectionClass result = template.findById(id, CustomCollectionClass.class); assertThat(result).isEqualTo(initial); - template.delete(result); // cleanup } @Test @@ -77,7 +88,6 @@ public void insertsDocumentWithListMapDateStringLongValues() { Person actual = template.findById(id, Person.class); assertThat(actual).isEqualTo(customer); - template.delete(actual); // cleanup } @Test @@ -100,7 +110,6 @@ public void insertsDocumentWithListMapDateStringLongValuesAndSetName() { Person actual = template.findById(id, Person.class, OVERRIDE_SET_NAME); assertThat(actual).isEqualTo(customer); - template.delete(actual, OVERRIDE_SET_NAME); // cleanup } @Test @@ -110,7 +119,6 @@ public void insertsAndFindsDocumentWithByteArrayField() { DocumentWithByteArray result = template.findById(id, DocumentWithByteArray.class); assertThat(result).isEqualTo(document); - template.delete(result); // cleanup } @Test @@ -119,7 +127,6 @@ public void insertsDocumentWithNullFields() { template.insert(document); assertThat(document.getField()).isNull(); - template.delete(template.findById(id, VersionedClass.class)); // cleanup } @Test @@ -128,7 +135,6 @@ public void insertsDocumentWithZeroVersionIfThereIsNoDocumentWithSameKey() { template.insert(document); assertThat(document.getVersion()).isEqualTo(1); - template.delete(template.findById(id, VersionedClass.class)); // cleanup } @Test @@ -139,7 +145,6 @@ public void insertsDocumentWithVersionGreaterThanZeroIfThereIsNoDocumentWithSame template.insert(document); assertThat(document.getVersion()).isEqualTo(1); - template.delete(template.findById(id, VersionedClass.class)); // cleanup } @Test @@ -149,7 +154,6 @@ public void throwsExceptionForDuplicateId() { assertThatThrownBy(() -> template.insert(person)) .isInstanceOf(DuplicateKeyException.class); - template.delete(template.findById(id, Person.class)); // cleanup } @Test @@ -159,7 +163,6 @@ public void throwsExceptionForDuplicateIdForVersionedDocument() { template.insert(document); assertThatThrownBy(() -> template.insert(document)) .isInstanceOf(DuplicateKeyException.class); - template.delete(template.findById(id, VersionedClass.class)); // cleanup } @Test @@ -179,7 +182,6 @@ public void insertsOnlyFirstDocumentAndNextAttemptsShouldFailWithDuplicateKeyExc }); assertThat(duplicateKeyCounter.intValue()).isEqualTo(numberOfConcurrentSaves - 1); - template.delete(template.findById(id, VersionedClass.class)); // cleanup } @Test @@ -199,30 +201,36 @@ public void insertsOnlyFirstDocumentAndNextAttemptsShouldFailWithDuplicateKeyExc }); assertThat(duplicateKeyCounter.intValue()).isEqualTo(numberOfConcurrentSaves - 1); - template.delete(template.findById(id, Person.class)); // cleanup } @Test public void insertAll_insertsAllDocuments() { - List persons = IntStream.range(1, 10) - .mapToObj(age -> Person.builder().id(nextId()) - .firstName("Gregor") - .age(age).build()) - .collect(Collectors.toList()); - // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { + List persons = IntStream.range(1, 10) + .mapToObj(age -> Person.builder().id(nextId()) + .firstName("Gregor") + .age(age).build()) + .collect(Collectors.toList()); template.insertAll(persons); - } else { - persons.forEach(person -> template.insert(person)); - } - List result = template.findByIds(persons.stream().map(Person::getId) - .collect(Collectors.toList()), Person.class); - - assertThat(result).hasSameElementsAs(persons); - for (Person person : result) { - template.delete(person); // cleanup + List result = template.findByIds(persons.stream().map(Person::getId) + .collect(Collectors.toList()), Person.class); + assertThat(result).hasSameElementsAs(persons); + template.deleteAll(Person.class); // cleanup + + Iterable personsToInsert = IntStream.range(0, 101) + .mapToObj(age -> Person.builder().id(nextId()) + .firstName("Gregor") + .age(age).build()) + .collect(Collectors.toList()); + template.insertAll(personsToInsert); + + @SuppressWarnings("CastCanBeRemovedNarrowingVariableType") + List ids = ((List) personsToInsert).stream().map(Person::getId) + .collect(Collectors.toList()); + result = template.findByIds(ids, Person.class); + assertThat(result).hasSameElementsAs(personsToInsert); } } @@ -235,7 +243,7 @@ public void insertAllWithSetName_insertsAllDocuments() { .collect(Collectors.toList()); // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { template.insertAll(persons, OVERRIDE_SET_NAME); } else { persons.forEach(person -> template.insert(person, OVERRIDE_SET_NAME)); @@ -245,29 +253,25 @@ public void insertAllWithSetName_insertsAllDocuments() { .collect(Collectors.toList()), Person.class, OVERRIDE_SET_NAME); assertThat(result).hasSameElementsAs(persons); - for (Person person : result) { - template.delete(person, OVERRIDE_SET_NAME); // cleanup - } } @Test public void insertAll_rejectsDuplicateIds() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { VersionedClass first = new VersionedClass(id, "foo"); assertThatThrownBy(() -> template.insertAll(List.of(first, first))) .isInstanceOf(AerospikeException.BatchRecordArray.class) .hasMessageContaining("Errors during batch insert"); Assertions.assertEquals(1, (long) first.getVersion()); - template.delete(first); // cleanup } } @Test public void shouldInsertAllVersionedDocuments() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { VersionedClass first = new VersionedClass(id, "foo"); VersionedClass second = new VersionedClass(nextId(), "foo", 1L); VersionedClass third = new VersionedClass(nextId(), "foo", 2L); diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplatePersistTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplatePersistTests.java index 4f87cecc4..ffe18aea0 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplatePersistTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplatePersistTests.java @@ -20,11 +20,15 @@ import org.junit.jupiter.api.Test; import org.springframework.dao.DataRetrievalFailureException; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.sample.SampleClasses.CustomCollectionClass; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplatePersistTests extends BaseBlockingIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplatePrependTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplatePrependTests.java index dc3890e3e..c1212b0bf 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplatePrependTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplatePrependTests.java @@ -18,12 +18,16 @@ import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplatePrependTests extends BaseBlockingIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateQueryAggregationTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateQueryAggregationTests.java index eb06d3dc6..fa2c8405e 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateQueryAggregationTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateQueryAggregationTests.java @@ -13,14 +13,18 @@ import org.junit.jupiter.api.TestInstance; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import java.util.ArrayList; import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateQueryAggregationTests extends BaseBlockingIntegrationTests { Person firstPerson, secondPerson, thirdPerson; diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java index 9b16c64a8..bdfa5278f 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateSaveTests.java @@ -26,10 +26,10 @@ import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; -import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.aerospike.sample.Person; +import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.aerospike.utility.AsyncUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.test.context.TestPropertySource; import java.math.BigInteger; import java.util.List; @@ -38,12 +38,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.sample.SampleClasses.CustomCollectionClass; import static org.springframework.data.aerospike.sample.SampleClasses.DocumentWithByteArray; import static org.springframework.data.aerospike.sample.SampleClasses.DocumentWithTouchOnRead; import static org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateSaveTests extends BaseBlockingIntegrationTests { @AfterAll @@ -337,7 +340,7 @@ public void shouldSaveAllAndSetVersion() { second.setVersion(second.getVersion()); // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { template.saveAll(List.of(first, second)); } else { List.of(first, second).forEach(document -> template.save(document)); @@ -355,7 +358,7 @@ public void shouldSaveAllAndSetVersionWithSetName() { VersionedClass first = new VersionedClass(id, "foo"); VersionedClass second = new VersionedClass(nextId(), "foo"); // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { template.saveAll(List.of(first, second), OVERRIDE_SET_NAME); } else { List.of(first, second).forEach(person -> template.save(person, OVERRIDE_SET_NAME)); @@ -371,7 +374,7 @@ public void shouldSaveAllAndSetVersionWithSetName() { @Test public void shouldSaveAllVersionedDocumentsAndSetVersionAndThrowExceptionIfAlreadyExist() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { VersionedClass first = new VersionedClass(id, "foo"); VersionedClass second = new VersionedClass(nextId(), "foo"); @@ -393,7 +396,7 @@ public void shouldSaveAllVersionedDocumentsAndSetVersionAndThrowExceptionIfAlrea @Test public void shouldSaveAllNotVersionedDocumentsIfAlreadyExist() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { Person john = new Person("id1", "John"); Person jack = new Person("id2", "Jack"); template.save(jack); // saving non-versioned document to create a new DB record diff --git a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateUpdateTests.java b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateUpdateTests.java index 2b0b1c76e..6cceab813 100644 --- a/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateUpdateTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/AerospikeTemplateUpdateTests.java @@ -26,7 +26,7 @@ import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.utility.AsyncUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.test.context.TestPropertySource; import java.util.ArrayList; import java.util.HashMap; @@ -36,8 +36,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeTemplateUpdateTests extends BaseBlockingIntegrationTests { @Test @@ -402,7 +405,7 @@ public void TestAddToMapSpecifyingMapFieldOnly() { @Test public void updateAllShouldThrowExceptionOnUpdateForNonExistingKey() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { VersionedClass first = new VersionedClass("newId1", "foo"); // This class has a version field (class // field annotated with @Version). The constructor does not receive the version, so it stays equal to zero VersionedClass second = new VersionedClass("newId2", "bar"); // @@ -431,7 +434,7 @@ public void updateAllShouldThrowExceptionOnUpdateForNonExistingKey() { @Test public void updateAllIfDocumentsNotChanged() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { int age1 = 140335200; int age2 = 177652800; Person person1 = new Person(id, "Wolfgang M", age1); @@ -451,7 +454,7 @@ public void updateAllIfDocumentsNotChanged() { @Test public void updateAllIfDocumentsChanged() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { int age1 = 140335200; int age2 = 177652800; Person person1 = new Person(id, "Wolfgang", age1); @@ -470,13 +473,21 @@ public void updateAllIfDocumentsChanged() { assertThat(result2.getFirstName()).isEqualTo("Johann B"); template.delete(result1); // cleanup template.delete(result2); // cleanup + + List persons = additionalAerospikeTestOperations.saveGeneratedPersons(101); + Iterable personsWithUpdate = persons.stream() + .peek(person -> person.setFirstName(person.getFirstName() + "_")).toList(); + template.updateAll(personsWithUpdate); + personsWithUpdate.forEach(person -> + assertThat(template.findById(person.getId(), Person.class).getFirstName() + .equals(person.getFirstName())).isTrue()); } } @Test public void updateAllIfDocumentsNotChangedWithSetName() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { int age1 = 140335200; int age2 = 177652800; Person person1 = new Person(id, "Wolfgang", age1); diff --git a/src/test/java/org/springframework/data/aerospike/core/DefaultAerospikeExceptionTranslatorTest.java b/src/test/java/org/springframework/data/aerospike/core/DefaultAerospikeExceptionTranslatorTest.java index cf6c7f77c..4f83e04c4 100644 --- a/src/test/java/org/springframework/data/aerospike/core/DefaultAerospikeExceptionTranslatorTest.java +++ b/src/test/java/org/springframework/data/aerospike/core/DefaultAerospikeExceptionTranslatorTest.java @@ -26,9 +26,13 @@ import org.springframework.dao.TransientDataAccessResourceException; import org.springframework.data.aerospike.exceptions.IndexAlreadyExistsException; import org.springframework.data.aerospike.exceptions.IndexNotFoundException; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class DefaultAerospikeExceptionTranslatorTest { private final DefaultAerospikeExceptionTranslator translator = new DefaultAerospikeExceptionTranslator(); diff --git a/src/test/java/org/springframework/data/aerospike/core/WritePolicyBuilderTest.java b/src/test/java/org/springframework/data/aerospike/core/WritePolicyBuilderTest.java index c296f6e80..b6276c2fc 100644 --- a/src/test/java/org/springframework/data/aerospike/core/WritePolicyBuilderTest.java +++ b/src/test/java/org/springframework/data/aerospike/core/WritePolicyBuilderTest.java @@ -4,10 +4,14 @@ import com.aerospike.client.policy.RecordExistsAction; import com.aerospike.client.policy.WritePolicy; import org.junit.jupiter.api.Test; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class WritePolicyBuilderTest { private static final GenerationPolicy GENERATION_POLICY = GenerationPolicy.EXPECT_GEN_EQUAL; diff --git a/src/test/java/org/springframework/data/aerospike/core/model/GroupedEntitiesTest.java b/src/test/java/org/springframework/data/aerospike/core/model/GroupedEntitiesTest.java index 7ece0939c..a4e21a768 100644 --- a/src/test/java/org/springframework/data/aerospike/core/model/GroupedEntitiesTest.java +++ b/src/test/java/org/springframework/data/aerospike/core/model/GroupedEntitiesTest.java @@ -3,14 +3,25 @@ import org.junit.Test; import org.springframework.data.aerospike.sample.Customer; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; + +import java.util.Collection; +import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class GroupedEntitiesTest { + private static final Map, Collection> entitiesMap = Map.of( + Person.class, List.of(Person.builder().id("22").build()), + Customer.class, List.of(Customer.builder().id("33").build()) + ); private static final GroupedEntities TEST_GROUPED_ENTITIES = GroupedEntities.builder() - .entity(Person.class, Person.builder().id("22").build()) - .entity(Customer.class, Customer.builder().id("33").build()) + .entities(entitiesMap) .build(); @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/model/GroupedKeysTest.java b/src/test/java/org/springframework/data/aerospike/core/model/GroupedKeysTest.java index 5c2cd6189..594d6f2df 100644 --- a/src/test/java/org/springframework/data/aerospike/core/model/GroupedKeysTest.java +++ b/src/test/java/org/springframework/data/aerospike/core/model/GroupedKeysTest.java @@ -2,6 +2,7 @@ import org.junit.Test; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import java.util.Collection; import java.util.HashMap; @@ -10,7 +11,10 @@ import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class GroupedKeysTest { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateCompositeKeyTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateCompositeKeyTests.java index 1260c082e..0b648afac 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateCompositeKeyTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateCompositeKeyTests.java @@ -5,13 +5,17 @@ import org.springframework.data.aerospike.BaseReactiveIntegrationTests; import org.springframework.data.aerospike.sample.SampleClasses.CompositeKey; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithCompositeKey; +import org.springframework.test.context.TestPropertySource; import reactor.test.StepVerifier; import java.util.List; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateCompositeKeyTests extends BaseReactiveIntegrationTests { private DocumentWithCompositeKey document; diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateDeleteRelatedTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateDeleteRelatedTests.java index 5a9b12613..765b671a6 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateDeleteRelatedTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateDeleteRelatedTests.java @@ -5,18 +5,21 @@ import com.aerospike.client.policy.WritePolicy; import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.BaseReactiveIntegrationTests; -import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.aerospike.core.ReactiveAerospikeTemplate; import org.springframework.data.aerospike.core.model.GroupedKeys; import org.springframework.data.aerospike.sample.Person; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.data.aerospike.sample.SampleClasses; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; +import java.util.Collection; import java.util.List; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; /** @@ -24,6 +27,8 @@ * * @author Yevhen Tsyba */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateDeleteRelatedTests extends BaseReactiveIntegrationTests { @Test @@ -145,32 +150,42 @@ public void deleteByObject_shouldReturnFalseIfValueIsAbsent() { @Test public void deleteAll_ShouldDeleteAllDocuments() { // batch delete operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { String id1 = nextId(); String id2 = nextId(); - reactiveTemplate.save(new SampleClasses.DocumentWithExpiration(id1)); - reactiveTemplate.save(new SampleClasses.DocumentWithExpiration(id2)); + reactiveTemplate.save(new SampleClasses.VersionedClass(id1, "test1")).block(); + reactiveTemplate.save(new SampleClasses.VersionedClass(id2, "test2")).block(); List ids = List.of(id1, id2); - reactiveTemplate.deleteByIds(ids, SampleClasses.DocumentWithExpiration.class); + reactiveTemplate.deleteByIds(ids, SampleClasses.VersionedClass.class).block(); - List list = reactiveTemplate.findByIds(ids, - SampleClasses.DocumentWithExpiration.class).subscribeOn(Schedulers.parallel()).collectList().block(); + List list = reactiveTemplate.findByIds(ids, + SampleClasses.VersionedClass.class).subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(list).isEmpty(); + + List persons = additionalAerospikeTestOperations.saveGeneratedPersons(101); + ids = persons.stream().map(Person::getId).toList(); + reactiveTemplate.deleteByIds(ids, Person.class).block(); + assertThat(reactiveTemplate.findByIds(ids, Person.class).collectList().block()).hasSize(0); + + List persons2 = additionalAerospikeTestOperations.saveGeneratedPersons(1001); + ids = persons2.stream().map(Person::getId).toList(); + reactiveTemplate.deleteByIds(ids, Person.class).block(); + assertThat(reactiveTemplate.findByIds(ids, Person.class).collectList().block()).hasSize(0); } } @Test public void deleteAllWithSetName_ShouldDeleteAllDocuments() { // batch delete operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { String id1 = nextId(); String id2 = nextId(); - reactiveTemplate.save(new SampleClasses.DocumentWithExpiration(id1), OVERRIDE_SET_NAME); - reactiveTemplate.save(new SampleClasses.DocumentWithExpiration(id2), OVERRIDE_SET_NAME); + reactiveTemplate.save(new SampleClasses.DocumentWithExpiration(id1), OVERRIDE_SET_NAME).block(); + reactiveTemplate.save(new SampleClasses.DocumentWithExpiration(id2), OVERRIDE_SET_NAME).block(); List ids = List.of(id1, id2); - reactiveTemplate.deleteByIds(ids, OVERRIDE_SET_NAME); + reactiveTemplate.deleteByIds(ids, OVERRIDE_SET_NAME).block(); List list = reactiveTemplate.findByIds(ids, SampleClasses.DocumentWithExpiration.class, OVERRIDE_SET_NAME) @@ -182,35 +197,55 @@ public void deleteAllWithSetName_ShouldDeleteAllDocuments() { @Test public void deleteAllFromDifferentSets_ShouldDeleteAllDocuments() { // batch delete operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { - SampleClasses.DocumentWithExpiration entity1 = new SampleClasses.DocumentWithExpiration(id); - SampleClasses.VersionedClass entity2 = new SampleClasses.VersionedClass(nextId(), "test"); - reactiveTemplate.save(entity1); - reactiveTemplate.save(entity2); - - reactiveTemplate.deleteByIds(GroupedKeys.builder() - .entityKeys(SampleClasses.DocumentWithExpiration.class, List.of(entity1.getId())).build()); - reactiveTemplate.deleteByIds(GroupedKeys.builder() - .entityKeys(SampleClasses.VersionedClass.class, List.of(entity2.getId())).build()); - - List list1 = reactiveTemplate.findByIds(List.of(entity1.getId()), - SampleClasses.DocumentWithExpiration.class).subscribeOn(Schedulers.parallel()).collectList().block(); + if (serverVersionSupport.batchWrite()) { + SampleClasses.DocumentWithExpiration entity1_1 = new SampleClasses.DocumentWithExpiration(id); + SampleClasses.DocumentWithExpiration entity1_2 = new SampleClasses.DocumentWithExpiration(nextId()); + SampleClasses.VersionedClass entity2_1 = new SampleClasses.VersionedClass(nextId(), "test1"); + SampleClasses.VersionedClass entity2_2 = new SampleClasses.VersionedClass(nextId(), "test2"); + Person entity3_1 = Person.builder().id(nextId()).firstName("Name1").build(); + Person entity3_2 = Person.builder().id(nextId()).firstName("Name2").build(); + reactiveTemplate.save(entity1_1).block(); + reactiveTemplate.save(entity1_2).block(); + reactiveTemplate.save(entity2_1).block(); + reactiveTemplate.save(entity2_2).block(); + reactiveTemplate.save(entity3_1).block(); + reactiveTemplate.save(entity3_2).block(); + + Map, Collection> entitiesKeys = Map.of( + SampleClasses.DocumentWithExpiration.class, List.of(entity1_1.getId(), entity1_2.getId()), + SampleClasses.VersionedClass.class, List.of(entity2_1.getId(), entity2_2.getId()), + Person.class, List.of(entity3_1.getId(), entity3_2.getId()) + ); + GroupedKeys groupedKeys = GroupedKeys.builder().entitiesKeys(entitiesKeys).build(); + reactiveTemplate.deleteByIds(groupedKeys).block(); + + List list1 = reactiveTemplate.findByIds( + List.of(entity1_1.getId(), entity1_2.getId()), + SampleClasses.DocumentWithExpiration.class + ).subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(list1).isEmpty(); - List list2 = reactiveTemplate.findByIds(List.of(entity2.getId()), - SampleClasses.VersionedClass.class).subscribeOn(Schedulers.parallel()).collectList().block(); + List list2 = reactiveTemplate.findByIds( + List.of(entity2_1.getId(), entity2_2.getId()), + SampleClasses.VersionedClass.class + ).subscribeOn(Schedulers.parallel()).collectList().block(); assertThat(list2).isEmpty(); + List list3 = reactiveTemplate.findByIds( + List.of(entity3_1.getId(), entity3_2.getId()), + Person.class + ).subscribeOn(Schedulers.parallel()).collectList().block(); + assertThat(list3).isEmpty(); } } @Test public void deleteAll_rejectsDuplicateIds() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { String id1 = nextId(); SampleClasses.DocumentWithExpiration document1 = new SampleClasses.DocumentWithExpiration(id1); SampleClasses.DocumentWithExpiration document2 = new SampleClasses.DocumentWithExpiration(id1); - reactiveTemplate.save(document1); - reactiveTemplate.save(document2); + reactiveTemplate.save(document1).block(); + reactiveTemplate.save(document2).block(); List ids = List.of(id1, id1); StepVerifier.create(reactiveTemplate.deleteByIds(ids, SampleClasses.DocumentWithExpiration.class)) diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByEntitiesTest.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByEntitiesTest.java index e46715760..48b650540 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByEntitiesTest.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByEntitiesTest.java @@ -4,8 +4,13 @@ import org.springframework.data.aerospike.core.AbstractFindByEntitiesTest; import org.springframework.data.aerospike.core.model.GroupedEntities; import org.springframework.data.aerospike.core.model.GroupedKeys; +import org.springframework.test.context.TestPropertySource; import reactor.core.scheduler.Schedulers; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; + +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateFindByEntitiesTest extends BaseReactiveIntegrationTests implements AbstractFindByEntitiesTest { diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByIdProjectionTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByIdProjectionTests.java index dd483805a..7b306f6c6 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByIdProjectionTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByIdProjectionTests.java @@ -6,6 +6,7 @@ import org.springframework.data.aerospike.sample.PersonMissingAndRedundantFields; import org.springframework.data.aerospike.sample.PersonSomeFields; import org.springframework.data.aerospike.sample.PersonTouchOnRead; +import org.springframework.test.context.TestPropertySource; import reactor.core.scheduler.Schedulers; import java.util.Arrays; @@ -13,7 +14,10 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateFindByIdProjectionTests extends BaseReactiveIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByIdTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByIdTests.java index 8a4c6357d..cd54c1244 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByIdTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByIdTests.java @@ -7,6 +7,7 @@ import org.springframework.data.aerospike.sample.SampleClasses; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithTouchOnRead; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithTouchOnReadAndExpirationProperty; +import org.springframework.test.context.TestPropertySource; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; @@ -16,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.sample.SampleClasses.EXPIRATION_ONE_MINUTE; /** @@ -23,6 +25,8 @@ * * @author Igor Ermolenko */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateFindByIdTests extends BaseReactiveIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryProjectionTest.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryProjectionTest.java index c4c6ffebe..f6858952f 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryProjectionTest.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryProjectionTest.java @@ -13,6 +13,7 @@ import org.springframework.data.aerospike.sample.PersonSomeFields; import org.springframework.data.aerospike.utility.QueryUtils; import org.springframework.data.domain.Sort; +import org.springframework.test.context.TestPropertySource; import reactor.core.scheduler.Schedulers; import java.util.List; @@ -21,12 +22,17 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateFindByQueryProjectionTest extends BaseReactiveIntegrationTests { @BeforeAll public void beforeAllSetUp() { + reactiveTemplate.deleteAll(Person.class).block(); + reactiveTemplate.deleteAll(OVERRIDE_SET_NAME).block(); additionalAerospikeTestOperations.createIndex( Person.class, "person_age_index", "age", IndexType.NUMERIC); additionalAerospikeTestOperations.createIndex( @@ -45,6 +51,8 @@ public void beforeAllSetUp() { @BeforeEach public void setUp() { super.setUp(); + reactiveTemplate.deleteAll(Person.class).block(); + reactiveTemplate.deleteAll(OVERRIDE_SET_NAME).block(); } @AfterAll @@ -54,7 +62,9 @@ public void afterAll() { additionalAerospikeTestOperations.dropIndex(Person.class, "person_first_name_index"); additionalAerospikeTestOperations.dropIndex(OVERRIDE_SET_NAME, "person_age_index" + OVERRIDE_SET_NAME); additionalAerospikeTestOperations.dropIndex(OVERRIDE_SET_NAME, "person_last_name_index" + OVERRIDE_SET_NAME); - additionalAerospikeTestOperations.dropIndex(OVERRIDE_SET_NAME, "person_first_name_index"+ OVERRIDE_SET_NAME); + additionalAerospikeTestOperations.dropIndex(OVERRIDE_SET_NAME, "person_first_name_index" + OVERRIDE_SET_NAME); + reactiveTemplate.deleteAll(Person.class).block(); + reactiveTemplate.deleteAll(OVERRIDE_SET_NAME).block(); } @Test @@ -125,7 +135,7 @@ public void findByFilterEqualProjection() { .collect(Collectors.toList()); reactiveTemplate.insertAll(allUsers).blockLast(); - Query query = QueryUtils.createQueryForMethodWithArgs("findPersonByFirstName", "Dave"); + Query query = QueryUtils.createQueryForMethodWithArgs("findByFirstName", "Dave"); List actual = reactiveTemplate.find(query, Person.class, PersonSomeFields.class) .subscribeOn(Schedulers.parallel()) @@ -145,7 +155,7 @@ public void findByFilterRangeProjection() { .collect(Collectors.toList()); reactiveTemplate.insertAll(allUsers).blockLast(); - Query query = QueryUtils.createQueryForMethodWithArgs("findCustomerByAgeBetween", 25, 30); + Query query = QueryUtils.createQueryForMethodWithArgs("findCustomerByAgeBetween", 25, 31); List actual = reactiveTemplate.find(query, Person.class, PersonSomeFields.class) .subscribeOn(Schedulers.parallel()) @@ -166,7 +176,7 @@ public void findByFilterRangeProjectionWithSetName() { .collect(Collectors.toList()); reactiveTemplate.insertAll(allUsers, OVERRIDE_SET_NAME).blockLast(); - Query query = QueryUtils.createQueryForMethodWithArgs("findCustomerByAgeBetween", 25, 30); + Query query = QueryUtils.createQueryForMethodWithArgs("findCustomerByAgeBetween", 25, 31); List actual = reactiveTemplate.find(query, PersonSomeFields.class, OVERRIDE_SET_NAME) .subscribeOn(Schedulers.parallel()) diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryTests.java index 2040c04b5..4436ee11e 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateFindByQueryTests.java @@ -14,6 +14,7 @@ import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.utility.QueryUtils; import org.springframework.data.domain.Sort; +import org.springframework.test.context.TestPropertySource; import reactor.core.scheduler.Schedulers; import java.util.ArrayList; @@ -26,9 +27,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.domain.Sort.Order.asc; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateFindByQueryTests extends BaseReactiveIntegrationTests { @BeforeAll diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateIndexTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateIndexTests.java index 3e32cf9fe..1e6b9e067 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateIndexTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateIndexTests.java @@ -12,7 +12,7 @@ import org.springframework.data.aerospike.exceptions.IndexNotFoundException; import org.springframework.data.aerospike.mapping.Document; import org.springframework.data.aerospike.query.model.Index; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Mono; import java.util.Objects; @@ -22,8 +22,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.utility.AwaitilityUtils.awaitTenSecondsUntil; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateIndexTests extends BaseReactiveIntegrationTests { private static final String INDEX_TEST_1 = "index-test-77777"; @@ -39,7 +42,7 @@ public void setUp() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void createIndex_shouldNotThrowExceptionIfIndexAlreadyExists() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { reactiveTemplate.createIndex(IndexedDocument.class, INDEX_TEST_1, "stringField", IndexType.STRING).block(); assertThatCode(() -> reactiveTemplate.createIndex(IndexedDocument.class, INDEX_TEST_1, "stringField", @@ -52,7 +55,7 @@ public void createIndex_shouldNotThrowExceptionIfIndexAlreadyExists() { // for Aerospike Server ver. < 6.1.0.1 @Test public void createIndex_throwsExceptionIfIndexAlreadyExists() { - if (!ServerVersionUtils.isDropCreateBehaviorUpdated(reactorClient.getAerospikeClient())) { + if (!serverVersionSupport.isDropCreateBehaviorUpdated()) { reactiveTemplate.createIndex(IndexedDocument.class, INDEX_TEST_1, "stringField", IndexType.STRING).block(); assertThatThrownBy(() -> reactiveTemplate.createIndex(IndexedDocument.class, INDEX_TEST_1, "stringField", @@ -139,7 +142,7 @@ public void createIndex_createsIndexForDifferentTypes() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void createIndex_createsIndexOnNestedList() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { String setName = reactiveTemplate.getSetName(AerospikeTemplateIndexTests.IndexedDocument.class); reactiveTemplate.createIndex( AerospikeTemplateIndexTests.IndexedDocument.class, INDEX_TEST_1, "nestedList", @@ -160,7 +163,7 @@ public void createIndex_createsIndexOnNestedList() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void createIndex_createsIndexOnMapOfMapsContext() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { String setName = reactiveTemplate.getSetName(AerospikeTemplateIndexTests.IndexedDocument.class); CTX[] ctx = new CTX[]{ @@ -191,7 +194,7 @@ public void createIndex_createsIndexOnMapOfMapsContext() { // for Aerospike Server ver. >= 6.1.0.1 @Test public void deleteIndex_doesNotThrowExceptionIfIndexDoesNotExist() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { assertThatCode(() -> reactiveTemplate.deleteIndex(IndexedDocument.class, "not-existing-index") .block()) .doesNotThrowAnyException(); @@ -201,7 +204,7 @@ public void deleteIndex_doesNotThrowExceptionIfIndexDoesNotExist() { // for Aerospike Server ver. < 6.1.0.1 @Test public void deleteIndex_throwsExceptionIfIndexDoesNotExist() { - if (!ServerVersionUtils.isDropCreateBehaviorUpdated(reactorClient.getAerospikeClient())) { + if (!serverVersionSupport.isDropCreateBehaviorUpdated()) { assertThatThrownBy(() -> reactiveTemplate.deleteIndex(IndexedDocument.class, "not-existing-index").block()) .isInstanceOf(IndexNotFoundException.class); } diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateInsertTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateInsertTests.java index 3f528647d..9280e63ff 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateInsertTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateInsertTests.java @@ -3,15 +3,19 @@ import com.aerospike.client.AerospikeException; import com.aerospike.client.Key; import com.aerospike.client.policy.Policy; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; import org.springframework.dao.DuplicateKeyException; import org.springframework.data.aerospike.BaseReactiveIntegrationTests; +import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.sample.SampleClasses.CustomCollectionClass; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithByteArray; import org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; -import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.utility.AsyncUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; @@ -21,11 +25,35 @@ import java.util.Date; import java.util.List; import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup +@TestInstance(Lifecycle.PER_CLASS) public class ReactiveAerospikeTemplateInsertTests extends BaseReactiveIntegrationTests { + @BeforeEach + public void beforeEach() { + reactiveTemplate.deleteAll(Person.class).block(); + reactiveTemplate.deleteAll(OVERRIDE_SET_NAME); + reactiveTemplate.deleteAll(VersionedClass.class).block(); + reactiveTemplate.deleteAll(DocumentWithByteArray.class).block(); + reactiveTemplate.deleteAll(CustomCollectionClass.class); + } + + @AfterAll + public void afterAll() { + reactiveTemplate.deleteAll(Person.class).block(); + reactiveTemplate.deleteAll(OVERRIDE_SET_NAME); + reactiveTemplate.deleteAll(VersionedClass.class).block(); + reactiveTemplate.deleteAll(DocumentWithByteArray.class).block(); + reactiveTemplate.deleteAll(CustomCollectionClass.class); + } + @Test public void insertsAndFindsWithCustomCollectionSet() { CustomCollectionClass initial = new CustomCollectionClass(id, "data0"); @@ -36,7 +64,6 @@ public void insertsAndFindsWithCustomCollectionSet() { .verifyComplete(); CustomCollectionClass result = findById(id, CustomCollectionClass.class); assertThat(findById(id, CustomCollectionClass.class)).isEqualTo(initial); - reactiveTemplate.delete(result).block(); } @Test @@ -62,7 +89,6 @@ public void insertsDocumentWithListMapDateStringLongValues() { Person actual = findById(id, Person.class); assertThat(actual).isEqualTo(customer); - reactiveTemplate.delete(actual).block(); // cleanup } @Test @@ -88,7 +114,6 @@ public void insertsDocumentWithListMapDateStringLongValuesAndSetName() { Person actual = findById(id, Person.class, OVERRIDE_SET_NAME); assertThat(actual).isEqualTo(customer); - reactiveTemplate.delete(actual, OVERRIDE_SET_NAME).block(); // cleanup } @Test @@ -99,7 +124,6 @@ public void insertsAndFindsDocumentWithByteArrayField() { DocumentWithByteArray result = findById(id, DocumentWithByteArray.class); assertThat(result).isEqualTo(document); - reactiveTemplate.delete(result).block(); // cleanup } @Test @@ -108,7 +132,6 @@ public void insertsDocumentWithNullFields() { reactiveTemplate.insert(document).subscribeOn(Schedulers.parallel()).block(); assertThat(document.getField()).isNull(); - reactiveTemplate.delete(findById(id, VersionedClass.class)).block(); // cleanup } @Test @@ -117,7 +140,6 @@ public void insertsDocumentWithZeroVersionIfThereIsNoDocumentWithSameKey() { reactiveTemplate.insert(document).subscribeOn(Schedulers.parallel()).block(); assertThat(document.getVersion()).isEqualTo(1); - reactiveTemplate.delete(findById(id, VersionedClass.class)).block(); // cleanup } @Test @@ -126,7 +148,6 @@ public void insertsDocumentWithVersionGreaterThanZeroIfThereIsNoDocumentWithSame reactiveTemplate.insert(document).subscribeOn(Schedulers.parallel()).block(); assertThat(document.getVersion()).isEqualTo(1); - reactiveTemplate.delete(findById(id, VersionedClass.class)).block(); // cleanup } @Test @@ -135,7 +156,6 @@ public void insertsDocumentWithVersionGreaterThanZeroIfThereIsNoDocumentWithSame reactiveTemplate.insert(document, OVERRIDE_SET_NAME).subscribeOn(Schedulers.parallel()).block(); assertThat(document.getVersion()).isEqualTo(1); - reactiveTemplate.delete(findById(id, VersionedClass.class, OVERRIDE_SET_NAME), OVERRIDE_SET_NAME).block(); // cleanup } @@ -147,7 +167,6 @@ public void throwsExceptionForDuplicateId() { StepVerifier.create(reactiveTemplate.insert(person).subscribeOn(Schedulers.parallel())) .expectError(DuplicateKeyException.class) .verify(); - reactiveTemplate.delete(findById(id, Person.class)).block(); // cleanup } @Test @@ -158,7 +177,6 @@ public void throwsExceptionForDuplicateIdAndSetName() { StepVerifier.create(reactiveTemplate.insert(person, OVERRIDE_SET_NAME).subscribeOn(Schedulers.parallel())) .expectError(DuplicateKeyException.class) .verify(); - reactiveTemplate.delete(findById(id, Person.class, OVERRIDE_SET_NAME), OVERRIDE_SET_NAME).block(); // cleanup } @Test @@ -169,7 +187,6 @@ public void throwsExceptionForDuplicateIdForVersionedDocument() { StepVerifier.create(reactiveTemplate.insert(document).subscribeOn(Schedulers.parallel())) .expectError(DuplicateKeyException.class) .verify(); - reactiveTemplate.delete(findById(id, VersionedClass.class)).block(); // cleanup } @Test @@ -191,7 +208,6 @@ public void insertsOnlyFirstDocumentAndNextAttemptsShouldFailWithDuplicateKeyExc }); assertThat(duplicateKeyCounter.intValue()).isEqualTo(numberOfConcurrentSaves - 1); - reactiveTemplate.delete(findById(id, VersionedClass.class)).block(); // cleanup } @Test @@ -213,12 +229,11 @@ public void insertsOnlyFirstDocumentAndNextAttemptsShouldFailWithDuplicateKeyExc }); assertThat(duplicateKeyCounter.intValue()).isEqualTo(numberOfConcurrentSaves - 1); - reactiveTemplate.delete(findById(id, Person.class)).block(); // cleanup } @Test public void insertAll_shouldInsertAllDocuments() { - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { Person customer1 = new Person(nextId(), "Dave"); Person customer2 = new Person(nextId(), "James"); reactiveTemplate.insertAll(List.of(customer1, customer2)).blockLast(); @@ -229,12 +244,24 @@ public void insertAll_shouldInsertAllDocuments() { assertThat(result2).isEqualTo(customer2); reactiveTemplate.delete(result1).block(); // cleanup reactiveTemplate.delete(result2).block(); // cleanup + + Iterable personsToInsert = IntStream.range(0, 101) + .mapToObj(age -> Person.builder().id(nextId()) + .firstName("Gregor") + .age(age).build()) + .collect(Collectors.toList()); + reactiveTemplate.insertAll(personsToInsert).blockLast(); + + @SuppressWarnings("CastCanBeRemovedNarrowingVariableType") + List ids = ((List) personsToInsert).stream().map(Person::getId).toList(); + List result = reactiveTemplate.findByIds(ids, Person.class).collectList().block(); + assertThat(result).hasSameElementsAs(personsToInsert); } } @Test public void insertAllWithSetName_shouldInsertAllDocuments() { - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { Person customer1 = new Person(nextId(), "Dave"); Person customer2 = new Person(nextId(), "James"); reactiveTemplate.insertAll(List.of(customer1, customer2), OVERRIDE_SET_NAME).blockLast(); @@ -243,34 +270,30 @@ public void insertAllWithSetName_shouldInsertAllDocuments() { Person result2 = findById(customer2.getId(), Person.class, OVERRIDE_SET_NAME); assertThat(result1).isEqualTo(customer1); assertThat(result2).isEqualTo(customer2); - reactiveTemplate.delete(result1, OVERRIDE_SET_NAME).block(); // cleanup - reactiveTemplate.delete(result2, OVERRIDE_SET_NAME).block(); // cleanup } } @Test public void insertAll_rejectsDuplicateId() { - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { Person person = new Person(id, "Amol"); person.setAge(28); StepVerifier.create(reactiveTemplate.insertAll(List.of(person, person))) .expectError(AerospikeException.BatchRecordArray.class) .verify(); - reactiveTemplate.delete(findById(id, Person.class)).block(); // cleanup } } @Test public void insertAllWithSetName_rejectsDuplicateId() { - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { Person person = new Person(id, "Amol"); person.setAge(28); StepVerifier.create(reactiveTemplate.insertAll(List.of(person, person), OVERRIDE_SET_NAME)) .expectError(AerospikeException.BatchRecordArray.class) .verify(); - reactiveTemplate.delete(findById(id, Person.class, OVERRIDE_SET_NAME), OVERRIDE_SET_NAME).block(); // cleanup } } } diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateMiscTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateMiscTests.java index c93f98fb8..b49966f3b 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateMiscTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateMiscTests.java @@ -10,14 +10,19 @@ import org.springframework.data.aerospike.core.ReactiveAerospikeTemplate; import org.springframework.data.aerospike.core.WritePolicyBuilder; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; + /** * Tests for different methods in {@link ReactiveAerospikeTemplate}. * * @author Igor Ermolenko */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateMiscTests extends BaseReactiveIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateModificationRelatedTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateModificationRelatedTests.java index 6c6269bf7..505ce5755 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateModificationRelatedTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateModificationRelatedTests.java @@ -4,6 +4,7 @@ import org.springframework.data.aerospike.BaseReactiveIntegrationTests; import org.springframework.data.aerospike.core.ReactiveAerospikeTemplate; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; @@ -11,11 +12,15 @@ import java.util.HashMap; import java.util.Map; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; + /** * Tests for save related methods in {@link ReactiveAerospikeTemplate}. * * @author Yevhen Tsyba */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateModificationRelatedTests extends BaseReactiveIntegrationTests { @Test diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateSaveRelatedTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateSaveRelatedTests.java index 42b3b9bee..b3d598d41 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateSaveRelatedTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateSaveRelatedTests.java @@ -7,12 +7,12 @@ import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.data.aerospike.BaseReactiveIntegrationTests; -import org.springframework.data.aerospike.sample.SampleClasses.CustomCollectionClass; -import org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; import org.springframework.data.aerospike.core.ReactiveAerospikeTemplate; import org.springframework.data.aerospike.sample.Person; +import org.springframework.data.aerospike.sample.SampleClasses.CustomCollectionClass; +import org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; import org.springframework.data.aerospike.utility.AsyncUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; @@ -23,12 +23,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * Tests for save related methods in {@link ReactiveAerospikeTemplate}. * * @author Igor Ermolenko */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateSaveRelatedTests extends BaseReactiveIntegrationTests { @Test @@ -256,7 +259,7 @@ public void save_rejectsNullObjectToBeSaved() { @Test public void saveAll_shouldSaveAllDocuments() { // batch delete operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { Person customer1 = new Person(nextId(), "Dave"); Person customer2 = new Person(nextId(), "James"); reactiveTemplate.saveAll(List.of(customer1, customer2)).blockLast(); @@ -273,7 +276,7 @@ public void saveAll_shouldSaveAllDocuments() { @Test public void saveAllWithSetName_shouldSaveAllDocuments() { // batch delete operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { Person customer1 = new Person(nextId(), "Dave"); Person customer2 = new Person(nextId(), "James"); reactiveTemplate.saveAll(List.of(customer1, customer2), OVERRIDE_SET_NAME).blockLast(); @@ -290,7 +293,7 @@ public void saveAllWithSetName_shouldSaveAllDocuments() { @Test public void saveAll_rejectsDuplicateId() { // batch delete operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { VersionedClass first = new VersionedClass(id, "foo"); StepVerifier.create(reactiveTemplate.saveAll(List.of(first, first))) @@ -303,7 +306,7 @@ public void saveAll_rejectsDuplicateId() { @Test public void saveAllWithSetName_rejectsDuplicateId() { // batch delete operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { VersionedClass first = new VersionedClass(id, "foo"); StepVerifier.create(reactiveTemplate.saveAll(List.of(first, first), OVERRIDE_SET_NAME)) diff --git a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateUpdateTests.java b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateUpdateTests.java index 0db151e87..cc63d75dd 100644 --- a/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateUpdateTests.java +++ b/src/test/java/org/springframework/data/aerospike/core/reactive/ReactiveAerospikeTemplateUpdateTests.java @@ -3,15 +3,16 @@ import com.aerospike.client.AerospikeException; import com.aerospike.client.Key; import com.aerospike.client.policy.Policy; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.dao.RecoverableDataAccessException; import org.springframework.data.aerospike.BaseReactiveIntegrationTests; -import org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; import org.springframework.data.aerospike.sample.Person; +import org.springframework.data.aerospike.sample.SampleClasses.VersionedClass; import org.springframework.data.aerospike.utility.AsyncUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -23,10 +24,19 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static reactor.test.StepVerifier.create; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeTemplateUpdateTests extends BaseReactiveIntegrationTests { + @BeforeEach + public void beforeEach() { + reactiveTemplate.deleteAll(Person.class).block(); + reactiveTemplate.deleteAll(VersionedClass.class).block(); + } + @Test public void shouldThrowExceptionOnUpdateForNonExistingKey() { // RecordExistsAction.UPDATE_ONLY @@ -43,7 +53,6 @@ public void updatesEvenIfDocumentNotChanged() { Person result = findById(id, Person.class); assertThat(result.getAge()).isEqualTo(11); - reactiveTemplate.delete(result).block(); // cleanup } @Test @@ -57,7 +66,6 @@ public void updatesMultipleFields() { assertThat(doc.getFirstName()).isEqualTo("Andrew"); assertThat(doc.getAge()).isEqualTo(32); }); - reactiveTemplate.delete(result).block(); // cleanup } @Test @@ -75,7 +83,6 @@ public void updateSpecificFields() { assertThat(doc.getAge()).isEqualTo(41); assertThat(doc.getWaist()).isEqualTo(20); }); - reactiveTemplate.delete(result).block(); // cleanup } @Test @@ -90,7 +97,6 @@ public void shouldFailUpdateNonExistingSpecificField() { assertThatThrownBy(() -> reactiveTemplate.update(Person.builder().id(id).age(41).build(), fields).block()) .isInstanceOf(RecoverableDataAccessException.class) .hasMessageContaining("field doesn't exists"); - reactiveTemplate.delete(findById(id, Person.class)).block(); // cleanup } @Test @@ -112,7 +118,6 @@ public void updateSpecificFieldsWithFieldAnnotatedProperty() { assertThat(doc.getWaist()).isEqualTo(20); assertThat(doc.getEmailAddress()).isEqualTo("andrew2@gmail.com"); }); - reactiveTemplate.delete(result).block(); // cleanup } @Test @@ -134,7 +139,6 @@ public void updateSpecificFieldsWithFieldAnnotatedPropertyActualValue() { assertThat(doc.getWaist()).isEqualTo(20); assertThat(doc.getEmailAddress()).isEqualTo("andrew2@gmail.com"); }); - reactiveTemplate.delete(result).block(); // cleanup } @@ -163,7 +167,6 @@ public void updatesFieldValueAndDocumentVersion() { assertThat(doc.getField()).isEqualTo("foobar2"); assertThat(doc.getVersion()).isEqualTo(3); }); - reactiveTemplate.delete(document).block(); // cleanup } @Test @@ -187,7 +190,6 @@ public void updateSpecificFieldsWithDocumentVersion() { assertThat(doc.getField()).isEqualTo("foobar2"); assertThat(doc.getVersion()).isEqualTo(3); }); - reactiveTemplate.delete(findById(id, VersionedClass.class)).block(); // cleanup } @Test @@ -201,8 +203,6 @@ public void updatesFieldToNull() { assertThat(doc.getField()).isNull(); assertThat(doc.getVersion()).isEqualTo(2); }); - - reactiveTemplate.delete(findById(id, VersionedClass.class)).block(); // cleanup } @Test @@ -217,7 +217,6 @@ public void setsVersionEqualToNumberOfModifications() { .verifyComplete(); VersionedClass actual = findById(id, VersionedClass.class); assertThat(actual.getVersion()).isEqualTo(3); - reactiveTemplate.delete(actual).block(); // cleanup } @Test @@ -232,7 +231,6 @@ public void setsVersionEqualToNumberOfModificationsWithSetName() { .verifyComplete(); VersionedClass actual = findById(id, VersionedClass.class, OVERRIDE_SET_NAME); assertThat(actual.getVersion()).isEqualTo(3); - reactiveTemplate.delete(actual, OVERRIDE_SET_NAME).block(); // cleanup } @Test @@ -256,7 +254,6 @@ public void onlyFirstUpdateSucceedsAndNextAttemptsShouldFailWithOptimisticLockin }); assertThat(optimisticLock.intValue()).isEqualTo(numberOfConcurrentSaves - 1); - reactiveTemplate.delete(findById(id, VersionedClass.class)).block(); // cleanup } @Test @@ -275,7 +272,6 @@ public void allConcurrentUpdatesSucceedForNonVersionedDocument() { Person actual = findById(id, Person.class); assertThat(actual.getFirstName()).startsWith("value-"); - reactiveTemplate.delete(actual).block(); // cleanup } @Test @@ -308,7 +304,6 @@ public void TestAddToListSpecifyingListFieldOnly() { Person personWithList2 = findById(id, Person.class); assertThat(personWithList2).isEqualTo(personWithList); assertThat(personWithList2.getStrings()).hasSize(4); - reactiveTemplate.delete(findById(id, Person.class)).block(); // cleanup } @Test @@ -341,7 +336,6 @@ public void TestAddToMapSpecifyingMapFieldOnly() { assertThat(personWithList2).isEqualTo(personWithList); assertThat(personWithList2.getStringMap()).hasSize(4); assertThat(personWithList2.getStringMap().get("key4")).isEqualTo("Added something new"); - reactiveTemplate.delete(findById(id, Person.class)).block(); // cleanup } @Test @@ -374,13 +368,44 @@ public void TestAddToMapSpecifyingMapFieldOnlyWithSetName() { assertThat(personWithList2).isEqualTo(personWithList); assertThat(personWithList2.getStringMap()).hasSize(4); assertThat(personWithList2.getStringMap().get("key4")).isEqualTo("Added something new"); - reactiveTemplate.delete(findById(id, Person.class, OVERRIDE_SET_NAME), OVERRIDE_SET_NAME).block(); // cleanup + } + + @Test + public void updateAllIfDocumentsChanged() { + // batch write operations are supported starting with Server version 6.0+ + if (serverVersionSupport.batchWrite()) { + int age1 = 140335200; + int age2 = 177652800; + Person person1 = new Person(id, "Wolfgang", age1); + Person person2 = new Person(nextId(), "Johann", age2); + reactiveTemplate.insertAll(List.of(person1, person2)).blockLast(); + + person1.setFirstName("Wolfgang M"); + person2.setFirstName("Johann B"); + reactiveTemplate.updateAll(List.of(person1, person2)).blockLast(); + + Person result1 = reactiveTemplate.findById(person1.getId(), Person.class).block(); + Person result2 = reactiveTemplate.findById(person2.getId(), Person.class).block(); + assertThat(result1.getAge()).isEqualTo(age1); + assertThat(result1.getFirstName()).isEqualTo("Wolfgang M"); + assertThat(result2.getAge()).isEqualTo(age2); + assertThat(result2.getFirstName()).isEqualTo("Johann B"); + + List persons = additionalAerospikeTestOperations.saveGeneratedPersons(101); + Iterable personsWithUpdate = persons.stream() + .peek(person -> person.setFirstName(person.getFirstName() + "_")).toList(); + + reactiveTemplate.updateAll(personsWithUpdate).blockLast(); + personsWithUpdate.forEach(person -> + assertThat(reactiveTemplate.findById(person.getId(), Person.class).block().getFirstName() + .equals(person.getFirstName())).isTrue()); + } } @Test public void updateAllShouldThrowExceptionOnUpdateForNonExistingKey() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { Person person1 = new Person(id, "svenfirstName", 11); Person person2 = new Person(nextId(), "svenfirstName", 11); Person person3 = new Person(nextId(), "svenfirstName", 11); @@ -398,7 +423,7 @@ public void updateAllShouldThrowExceptionOnUpdateForNonExistingKey() { @Test public void updateAllIfDocumentsNotChanged() { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(reactorClient.getAerospikeClient())) { + if (serverVersionSupport.batchWrite()) { int age1 = 140335200; int age2 = 177652800; Person person1 = new Person(id, "Wolfgang", age1); @@ -410,8 +435,6 @@ public void updateAllIfDocumentsNotChanged() { Person result2 = reactiveTemplate.findById(person2.getId(), Person.class).block(); assertThat(result1.getAge()).isEqualTo(age1); assertThat(result2.getAge()).isEqualTo(age2); - reactiveTemplate.delete(result1).block(); // cleanup - reactiveTemplate.delete(result2).block(); // cleanup } } } diff --git a/src/test/java/org/springframework/data/aerospike/index/AerospikePersistenceEntityIndexCreatorTest.java b/src/test/java/org/springframework/data/aerospike/index/AerospikePersistenceEntityIndexCreatorTest.java index 3c4669b8a..babd06e03 100644 --- a/src/test/java/org/springframework/data/aerospike/index/AerospikePersistenceEntityIndexCreatorTest.java +++ b/src/test/java/org/springframework/data/aerospike/index/AerospikePersistenceEntityIndexCreatorTest.java @@ -7,6 +7,7 @@ import org.springframework.data.aerospike.exceptions.IndexAlreadyExistsException; import org.springframework.data.aerospike.sample.AutoIndexedDocument; import org.springframework.data.aerospike.utility.MockObjectProvider; +import org.springframework.test.context.TestPropertySource; import java.util.Collections; import java.util.Set; @@ -15,7 +16,10 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup class AerospikePersistenceEntityIndexCreatorTest { final boolean createIndexesOnStartup = true; diff --git a/src/test/java/org/springframework/data/aerospike/index/IndexNotScheduledCacheRefreshTest.java b/src/test/java/org/springframework/data/aerospike/index/IndexNotScheduledCacheRefreshTest.java index c4c5ab286..e036b2f3b 100644 --- a/src/test/java/org/springframework/data/aerospike/index/IndexNotScheduledCacheRefreshTest.java +++ b/src/test/java/org/springframework/data/aerospike/index/IndexNotScheduledCacheRefreshTest.java @@ -16,7 +16,7 @@ @Slf4j @ContextConfiguration -@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0"}) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) public class IndexNotScheduledCacheRefreshTest extends BaseBlockingIntegrationTests { String setName = "scheduled"; diff --git a/src/test/java/org/springframework/data/aerospike/index/IndexScheduledCacheRefreshTest.java b/src/test/java/org/springframework/data/aerospike/index/IndexScheduledCacheRefreshTest.java index 81a041007..b270fb5fd 100644 --- a/src/test/java/org/springframework/data/aerospike/index/IndexScheduledCacheRefreshTest.java +++ b/src/test/java/org/springframework/data/aerospike/index/IndexScheduledCacheRefreshTest.java @@ -16,7 +16,7 @@ @Slf4j @ContextConfiguration -@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 4"}) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 4", "createIndexesOnStartup = false"}) public class IndexScheduledCacheRefreshTest extends BaseBlockingIntegrationTests { String setName = "scheduled"; diff --git a/src/test/java/org/springframework/data/aerospike/index/IndexedAnnotationTests.java b/src/test/java/org/springframework/data/aerospike/index/IndexedAnnotationTests.java index aacdc9be3..22494e09d 100644 --- a/src/test/java/org/springframework/data/aerospike/index/IndexedAnnotationTests.java +++ b/src/test/java/org/springframework/data/aerospike/index/IndexedAnnotationTests.java @@ -15,8 +15,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; -@TestPropertySource(properties = {"createIndexesOnStartup = true"}) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = true"}) // this test class requires secondary indexes created on startup public class IndexedAnnotationTests extends BaseBlockingIntegrationTests { diff --git a/src/test/java/org/springframework/data/aerospike/index/ReactiveAerospikePersistenceEntityIndexCreatorTest.java b/src/test/java/org/springframework/data/aerospike/index/ReactiveAerospikePersistenceEntityIndexCreatorTest.java index cd705fe1e..812cab345 100644 --- a/src/test/java/org/springframework/data/aerospike/index/ReactiveAerospikePersistenceEntityIndexCreatorTest.java +++ b/src/test/java/org/springframework/data/aerospike/index/ReactiveAerospikePersistenceEntityIndexCreatorTest.java @@ -7,6 +7,7 @@ import org.springframework.data.aerospike.exceptions.IndexAlreadyExistsException; import org.springframework.data.aerospike.sample.AutoIndexedDocument; import org.springframework.data.aerospike.utility.MockObjectProvider; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Mono; import java.util.Collections; @@ -15,7 +16,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup class ReactiveAerospikePersistenceEntityIndexCreatorTest { final boolean createIndexesOnStartup = true; diff --git a/src/test/java/org/springframework/data/aerospike/mapping/AerospikeMappingContextTest.java b/src/test/java/org/springframework/data/aerospike/mapping/AerospikeMappingContextTest.java index 612b6db69..8f6dcd18e 100644 --- a/src/test/java/org/springframework/data/aerospike/mapping/AerospikeMappingContextTest.java +++ b/src/test/java/org/springframework/data/aerospike/mapping/AerospikeMappingContextTest.java @@ -18,14 +18,18 @@ import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Peter Milne * @author Jean Mercier */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikeMappingContextTest { @Test diff --git a/src/test/java/org/springframework/data/aerospike/mapping/AerospikePersistentEntityTest.java b/src/test/java/org/springframework/data/aerospike/mapping/AerospikePersistentEntityTest.java index f9f8f007d..8ef682f6c 100644 --- a/src/test/java/org/springframework/data/aerospike/mapping/AerospikePersistentEntityTest.java +++ b/src/test/java/org/springframework/data/aerospike/mapping/AerospikePersistentEntityTest.java @@ -18,14 +18,18 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.aerospike.BaseBlockingIntegrationTests; +import org.springframework.test.context.TestPropertySource; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.springframework.data.aerospike.sample.SampleClasses.*; import static org.springframework.data.aerospike.mapping.BasicAerospikePersistentEntity.DEFAULT_EXPIRATION; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +import static org.springframework.data.aerospike.sample.SampleClasses.*; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class AerospikePersistentEntityTest extends BaseBlockingIntegrationTests { @Autowired diff --git a/src/test/java/org/springframework/data/aerospike/mapping/BasicAerospikePersistentEntityTest.java b/src/test/java/org/springframework/data/aerospike/mapping/BasicAerospikePersistentEntityTest.java index 7e27cff50..4a1c73876 100644 --- a/src/test/java/org/springframework/data/aerospike/mapping/BasicAerospikePersistentEntityTest.java +++ b/src/test/java/org/springframework/data/aerospike/mapping/BasicAerospikePersistentEntityTest.java @@ -20,10 +20,14 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithExpressionInCollection; import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithoutCollection; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup @ExtendWith(MockitoExtension.class) public class BasicAerospikePersistentEntityTest { diff --git a/src/test/java/org/springframework/data/aerospike/mapping/CachingAerospikePersistentPropertyTest.java b/src/test/java/org/springframework/data/aerospike/mapping/CachingAerospikePersistentPropertyTest.java index 0a01cf6c2..f2a171284 100644 --- a/src/test/java/org/springframework/data/aerospike/mapping/CachingAerospikePersistentPropertyTest.java +++ b/src/test/java/org/springframework/data/aerospike/mapping/CachingAerospikePersistentPropertyTest.java @@ -19,14 +19,18 @@ import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationContext; import org.springframework.data.aerospike.sample.Person; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Peter Milne * @author Jean Mercier */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class CachingAerospikePersistentPropertyTest { AerospikeMappingContext context; diff --git a/src/test/java/org/springframework/data/aerospike/query/FilterOperationRegexpBuilderTest.java b/src/test/java/org/springframework/data/aerospike/query/FilterOperationRegexpBuilderTest.java index 718964a01..b3033961f 100644 --- a/src/test/java/org/springframework/data/aerospike/query/FilterOperationRegexpBuilderTest.java +++ b/src/test/java/org/springframework/data/aerospike/query/FilterOperationRegexpBuilderTest.java @@ -18,13 +18,17 @@ import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.utility.FilterOperationRegexpBuilder; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /* - * Tests to ensure that Qualifiers are built successfully for non indexed bins. + * Tests to ensure that Qualifiers are built successfully for non-indexed bins. */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class FilterOperationRegexpBuilderTest { @Test diff --git a/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java b/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java index 09e4e041f..6f91d6eb3 100644 --- a/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/IndexedQualifierTests.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test; import org.springframework.data.aerospike.repository.query.Query; import org.springframework.data.aerospike.utility.CollectionUtils; -import org.springframework.data.aerospike.utility.ServerVersionUtils; import java.util.Map; import java.util.stream.Collectors; @@ -198,7 +197,7 @@ void selectOnIndexedStringEQQualifier() { @Test void selectWithGeoWithin() { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(client)) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { withIndex(namespace, INDEXED_GEO_SET, "geo_index", GEO_BIN_NAME, IndexType.GEO2DSPHERE, () -> { double lon = -122.0; double lat = 37.5; diff --git a/src/test/java/org/springframework/data/aerospike/query/cache/IndexTests.java b/src/test/java/org/springframework/data/aerospike/query/cache/IndexTests.java index c0143e33f..519639d24 100644 --- a/src/test/java/org/springframework/data/aerospike/query/cache/IndexTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/cache/IndexTests.java @@ -7,6 +7,7 @@ import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.query.model.Index; import org.springframework.data.aerospike.query.model.IndexKey; +import org.springframework.test.context.TestPropertySource; import java.util.List; import java.util.Optional; @@ -18,8 +19,11 @@ import static com.aerospike.client.query.IndexType.NUMERIC; import static com.aerospike.client.query.IndexType.STRING; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.query.model.Index.builder; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class IndexTests extends BaseBlockingIntegrationTests { private static final String SET = "index-test"; diff --git a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedPersonRepositoryQueryTests.java b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedPersonRepositoryQueryTests.java index 36229f629..a63c96eed 100644 --- a/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedPersonRepositoryQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/query/reactive/ReactiveIndexedPersonRepositoryQueryTests.java @@ -23,6 +23,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; +import org.springframework.test.context.TestPropertySource; import reactor.core.scheduler.Schedulers; import java.util.Arrays; @@ -31,10 +32,13 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.data.aerospike.AsCollections.of; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeMapCriteria.VALUE; import static org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeMetadata.SINCE_UPDATE_TIME; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveIndexedPersonRepositoryQueryTests extends BaseReactiveIntegrationTests { static final IndexedPerson alain = IndexedPerson.builder().id(nextId()).firstName("Alain").lastName("Sebastian") diff --git a/src/test/java/org/springframework/data/aerospike/repository/CustomerRepositoriesIntegrationTests.java b/src/test/java/org/springframework/data/aerospike/repository/CustomerRepositoriesIntegrationTests.java index bba9cb1aa..fc9aa3015 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/CustomerRepositoriesIntegrationTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/CustomerRepositoriesIntegrationTests.java @@ -20,16 +20,20 @@ import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.sample.Customer; import org.springframework.data.aerospike.sample.CustomerRepository; +import org.springframework.test.context.TestPropertySource; import java.util.Arrays; import java.util.List; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Oliver Gierke */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class CustomerRepositoriesIntegrationTests extends BaseBlockingIntegrationTests { @Autowired diff --git a/src/test/java/org/springframework/data/aerospike/repository/IdTypesRepositoryQueryTests.java b/src/test/java/org/springframework/data/aerospike/repository/IdTypesRepositoryQueryTests.java index 2e7030c28..cca456c52 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/IdTypesRepositoryQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/IdTypesRepositoryQueryTests.java @@ -8,14 +8,30 @@ import org.springframework.data.aerospike.BaseBlockingIntegrationTests; import org.springframework.data.aerospike.query.Qualifier; import org.springframework.data.aerospike.repository.query.Query; -import org.springframework.data.aerospike.sample.*; -import org.springframework.data.aerospike.sample.SampleClasses.*; +import org.springframework.data.aerospike.sample.DocumentByteArrayIdRepository; +import org.springframework.data.aerospike.sample.DocumentByteIdRepository; +import org.springframework.data.aerospike.sample.DocumentCharacterIdRepository; +import org.springframework.data.aerospike.sample.DocumentIntegerIdRepository; +import org.springframework.data.aerospike.sample.DocumentLongIdRepository; +import org.springframework.data.aerospike.sample.DocumentShortIdRepository; +import org.springframework.data.aerospike.sample.DocumentStringIdRepository; +import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithByteArrayId; +import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithByteId; +import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithCharacterId; +import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithIntegerId; +import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithLongId; +import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithShortId; +import org.springframework.data.aerospike.sample.SampleClasses.DocumentWithStringId; +import org.springframework.test.context.TestPropertySource; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class IdTypesRepositoryQueryTests extends BaseBlockingIntegrationTests { @Autowired diff --git a/src/test/java/org/springframework/data/aerospike/repository/IndexedPersonRepositoryQueryTests.java b/src/test/java/org/springframework/data/aerospike/repository/IndexedPersonRepositoryQueryTests.java index ab15bbed8..11c5003e1 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/IndexedPersonRepositoryQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/IndexedPersonRepositoryQueryTests.java @@ -19,6 +19,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; +import org.springframework.test.context.TestPropertySource; import java.util.ArrayList; import java.util.List; @@ -34,9 +35,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.data.aerospike.AsCollections.of; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeMapCriteria.VALUE; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class IndexedPersonRepositoryQueryTests extends BaseBlockingIntegrationTests { @Autowired diff --git a/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java b/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java index c93f90f78..01db5a9cb 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/PersonRepositoryQueryTests.java @@ -14,13 +14,13 @@ import org.springframework.data.aerospike.sample.Person; import org.springframework.data.aerospike.sample.PersonRepository; import org.springframework.data.aerospike.sample.PersonSomeFields; -import org.springframework.data.aerospike.utility.ServerVersionUtils; import org.springframework.data.aerospike.utility.TestUtils; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; +import org.springframework.test.context.TestPropertySource; import java.math.BigDecimal; import java.math.BigInteger; @@ -41,12 +41,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.springframework.data.aerospike.AsCollections.of; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeMapCriteria.KEY; import static org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeMapCriteria.VALUE; import static org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeMapCriteria.VALUE_CONTAINING; import static org.springframework.data.aerospike.repository.query.CriteriaDefinition.AerospikeMetadata.SINCE_UPDATE_TIME; @TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class PersonRepositoryQueryTests extends BaseBlockingIntegrationTests { static final Person dave = Person.builder().id(nextId()).firstName("Dave").lastName("Matthews").age(42) @@ -544,7 +547,7 @@ void findByMapOfListsKeyValueEquals() { @Test void findByAddressesMapKeyValueEquals() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Address address1 = new Address("Foo Street 1", 1, "C0123", "Bar"); Address address2 = new Address("Foo Street 2", 1, "C0123", "Bar"); Address address3 = new Address("Foo Street 2", 1, "C0124", "Bar"); @@ -757,7 +760,7 @@ void findByMapOfListsKeyValueNotEqual() { @Test void findByAddressesMapKeyValueNotEqual() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Address address1 = new Address("Foo Street 1", 1, "C0123", "Bar"); Address address2 = new Address("Foo Street 2", 1, "C0123", "Bar"); Address address3 = new Address("Foo Street 2", 1, "C0124", "Bar"); @@ -886,7 +889,7 @@ void findByMapKeyValueLessThanOrEqual() { @Test void findByMapKeyValueGreaterThanList() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Map> mapOfLists1 = Map.of("0", List.of(100), "1", List.of(200), "2", List.of(300), "3", List.of(400)); Map> mapOfLists2 = Map.of("0", List.of(101), "1", List.of(201), "2", List.of(301), @@ -921,7 +924,7 @@ void findByMapKeyValueGreaterThanList() { @Test void findByMapKeyValueLessThanAddress() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Map mapOfAddresses1 = Map.of("a", new Address("Foo Street 1", 1, "C0123", "Bar")); Map mapOfAddresses2 = Map.of("b", new Address("Foo Street 2", 1, "C0123", "Bar")); Map mapOfAddresses3 = Map.of("c", new Address("Foo Street 2", 1, "C0124", "Bar")); @@ -979,7 +982,7 @@ void findByIntMapKeyValueBetween() { @Test void findByIntMapBetween() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { assertThat(carter.getIntMap()).isEqualTo(Map.of("key1", 0, "key2", 1)); Map map1 = Map.of("key1", -1, "key2", 0); @@ -993,7 +996,7 @@ void findByIntMapBetween() { @Test void findByMapOfListsBetween() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Map> mapOfLists1 = Map.of("0", List.of(100), "1", List.of(200)); Map> mapOfLists2 = Map.of("2", List.of(301), "3", List.of(401)); Map> mapOfLists3 = Map.of("1", List.of(102), "2", List.of(202)); @@ -1208,7 +1211,7 @@ void deletePersonById() { @Test void deletePersonsByIds() { - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { // batch delete requires server ver. >= 6.0.0 repository.deleteAllById(List.of(dave.getId(), carter.getId())); @@ -1221,7 +1224,7 @@ void deletePersonsByIds() { @Test void deleteAllPersonsFromList() { - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { // batch delete requires server ver. >= 6.0.0 repository.deleteAll(List.of(dave, carter)); @@ -2000,7 +2003,7 @@ void findPersonInRangeCorrectly() { it = repository.findByFirstNameBetween("Dave", "David"); assertThat(it).hasSize(1).contains(dave); - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { assertThat(dave.getAddress()).isEqualTo(new Address("Foo Street 1", 1, "C0123", "Bar")); Address address1 = new Address("Foo Street 1", 0, "C0123", "Bar"); Address address2 = new Address("Foo Street 2", 2, "C0124", "Bar"); @@ -2048,7 +2051,7 @@ void findPersonsByFriendsInAgeRangeCorrectly() { @Test void findPersonsByStringsList() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { List listToCompareWith = List.of("str0", "str1", "str2"); assertThat(dave.getStrings()).isEqualTo(listToCompareWith); @@ -2063,7 +2066,7 @@ void findPersonsByStringsList() { @Test void findPersonsByStringsListNotEqual() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { List listToCompareWith = List.of("str0", "str1", "str2"); assertThat(dave.getStrings()).isEqualTo(listToCompareWith); assertThat(donny.getStrings()).isNotEmpty(); @@ -2076,7 +2079,7 @@ void findPersonsByStringsListNotEqual() { @Test void findPersonsByStringsListLessThan() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { List davesStrings = dave.getStrings(); List listToCompareWith = List.of("str1", "str2", "str3"); List listWithFewerElements = List.of("str1", "str2"); @@ -2096,7 +2099,7 @@ void findPersonsByStringsListLessThan() { @Test void findPersonsByStringsListGreaterThanOrEqual() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Set setToCompareWith = Set.of(0, 1, 2, 3, 4); dave.setIntSet(setToCompareWith); repository.save(dave); @@ -2109,7 +2112,7 @@ void findPersonsByStringsListGreaterThanOrEqual() { @Test void findPersonsByStringMap() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Map mapToCompareWith = Map.of("key1", "val1", "key2", "val2"); assertThat(boyd.getStringMap()).isEqualTo(mapToCompareWith); @@ -2124,7 +2127,7 @@ void findPersonsByStringMap() { @Test void findPersonsByAddress() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Address address = new Address("Foo Street 1", 1, "C0123", "Bar"); dave.setAddress(address); repository.save(dave); @@ -2136,7 +2139,7 @@ void findPersonsByAddress() { @Test void findPersonsByAddressNotEqual() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Address address = new Address("Foo Street 1", 1, "C0123", "Bar"); assertThat(dave.getAddress()).isEqualTo(address); assertThat(carter.getAddress()).isNotNull(); @@ -2151,7 +2154,7 @@ void findPersonsByAddressNotEqual() { @Test void findPersonsByIntMapNotEqual() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Map mapToCompareWith = Map.of("key1", 0, "key2", 1); assertThat(carter.getIntMap()).isEqualTo(mapToCompareWith); assertThat(boyd.getIntMap()).isNullOrEmpty(); @@ -2169,7 +2172,7 @@ void findPersonsByIntMapNotEqual() { @Test void findPersonsByAddressLessThan() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Address address = new Address("Foo Street 2", 2, "C0124", "C0123"); assertThat(dave.getAddress()).isNotEqualTo(address); assertThat(carter.getAddress()).isEqualTo(address); @@ -2181,7 +2184,7 @@ void findPersonsByAddressLessThan() { @Test void findPersonsByStringMapGreaterThan() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { assertThat(boyd.getStringMap()).isNotEmpty(); assertThat(donny.getStringMap()).isNotEmpty(); @@ -2193,7 +2196,7 @@ void findPersonsByStringMapGreaterThan() { @Test void findPersonsByFriend() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { alicia.setAddress(new Address("Foo Street 1", 1, "C0123", "Bar")); repository.save(alicia); oliver.setFriend(alicia); @@ -2209,7 +2212,7 @@ void findPersonsByFriend() { @Test void findPersonsByFriendAddress() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Address address = new Address("Foo Street 1", 1, "C0123", "Bar"); dave.setAddress(address); repository.save(dave); @@ -2348,7 +2351,7 @@ void findPersonsByFriendFriendFriendFriendFriendFriendFriendFriendBestFriendAddr @Test // find by deeply nested POJO void findPersonsByFriendFriendFriendFriendFriendFriendFriendFriendBestFriendBestFriendAddress() { - if (ServerVersionUtils.isFindByPojoSupported(client)) { + if (serverVersionSupport.findByPojo()) { Address address = new Address("Foo Street 1", 1, "C0123", "Bar"); dave.setAddress(address); repository.save(dave); diff --git a/src/test/java/org/springframework/data/aerospike/repository/RepositoriesIntegrationTests.java b/src/test/java/org/springframework/data/aerospike/repository/RepositoriesIntegrationTests.java index a499ca643..6d5c5670b 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/RepositoriesIntegrationTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/RepositoriesIntegrationTests.java @@ -21,11 +21,15 @@ import org.springframework.data.aerospike.sample.CompositeObject; import org.springframework.data.aerospike.sample.CompositeObjectRepository; import org.springframework.data.aerospike.sample.SimpleObject; +import org.springframework.test.context.TestPropertySource; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class RepositoriesIntegrationTests extends BaseBlockingIntegrationTests { @Autowired diff --git a/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryDeleteRelatedTests.java b/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryDeleteRelatedTests.java index df2a4e152..5ad6a6818 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryDeleteRelatedTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryDeleteRelatedTests.java @@ -1,5 +1,6 @@ package org.springframework.data.aerospike.repository.reactive; +import com.aerospike.client.AerospikeException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.reactivestreams.Publisher; @@ -7,6 +8,7 @@ import org.springframework.data.aerospike.BaseReactiveIntegrationTests; import org.springframework.data.aerospike.sample.Customer; import org.springframework.data.aerospike.sample.ReactiveCustomerRepository; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Flux; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; @@ -15,10 +17,13 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Igor Ermolenko */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeRepositoryDeleteRelatedTests extends BaseReactiveIntegrationTests { @Autowired @@ -52,7 +57,7 @@ public void deleteById_ShouldSkipNonexistent() { @Test @SuppressWarnings("ConstantConditions") - public void deleteById_ShouldRejectsNullObject() { + public void deleteById_ShouldRejectNullObject() { assertThatThrownBy(() -> customerRepo.deleteById((String) null).block()) .isInstanceOf(IllegalArgumentException.class); } @@ -76,7 +81,7 @@ public void deleteByIdPublisher_ShouldSkipNonexistent() { @Test @SuppressWarnings("ConstantConditions") - public void deleteByIdPublisher_ShouldRejectsNullObject() { + public void deleteByIdPublisher_ShouldRejectNullObject() { //noinspection unchecked,rawtypes assertThatThrownBy(() -> customerRepo.deleteById((Publisher) null).block()) .isInstanceOf(IllegalArgumentException.class); @@ -100,7 +105,7 @@ public void delete_ShouldSkipNonexistent() { @Test @SuppressWarnings("ConstantConditions") - public void delete_ShouldRejectsNullObject() { + public void delete_ShouldRejectNullObject() { assertThatThrownBy(() -> customerRepo.delete(null).block()) .isInstanceOf(IllegalArgumentException.class); @@ -115,19 +120,18 @@ public void deleteAllIterable_ShouldDeleteExistent() { } @Test - public void deleteAllIterable_ShouldSkipNonexistent() { + public void deleteAllIterable_ShouldSkipNonexistentAndThrowException() { Customer nonExistentCustomer = Customer.builder().id(nextId()).firstName("Bart").lastName("Simpson").age(15) .build(); - customerRepo.deleteAll(asList(customer1, nonExistentCustomer, customer2)).subscribeOn(Schedulers.parallel()) - .block(); - + assertThatThrownBy(() -> customerRepo.deleteAll(asList(customer1, nonExistentCustomer, customer2)).block()) + .isInstanceOf(AerospikeException.BatchRecordArray.class); StepVerifier.create(customerRepo.findById(customer1.getId())).expectNextCount(0).verifyComplete(); StepVerifier.create(customerRepo.findById(customer2.getId())).expectNextCount(0).verifyComplete(); } @Test - public void deleteAllIterable_ShouldRejectsNullObject() { + public void deleteAllIterable_ShouldRejectNullObject() { List entities = asList(customer1, null, customer2); assertThatThrownBy(() -> customerRepo.deleteAll(entities).subscribeOn(Schedulers.parallel()).block()) @@ -143,7 +147,7 @@ public void deleteAllPublisher_ShouldDeleteExistent() { } @Test - public void deleteAllPublisher_ShouldSkipNonexistent() { + public void deleteAllPublisher_ShouldNotSkipNonexistent() { Customer nonExistentCustomer = Customer.builder().id(nextId()).firstName("Bart").lastName("Simpson").age(15) .build(); diff --git a/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryExistRelatedTests.java b/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryExistRelatedTests.java index 759ca1f3f..2fa399ca4 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryExistRelatedTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryExistRelatedTests.java @@ -6,13 +6,18 @@ import org.springframework.data.aerospike.BaseReactiveIntegrationTests; import org.springframework.data.aerospike.sample.Customer; import org.springframework.data.aerospike.sample.ReactiveCustomerRepository; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Flux; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; + /** * @author Igor Ermolenko */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeRepositoryExistRelatedTests extends BaseReactiveIntegrationTests { @Autowired diff --git a/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryFindRelatedTests.java b/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryFindRelatedTests.java index e02ed49f2..99a099f5f 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryFindRelatedTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositoryFindRelatedTests.java @@ -12,6 +12,7 @@ import org.springframework.data.aerospike.sample.CustomerSomeFields; import org.springframework.data.aerospike.sample.ReactiveCustomerRepository; import org.springframework.data.domain.Sort; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Flux; import reactor.core.scheduler.Schedulers; @@ -19,11 +20,14 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.domain.Sort.Order.asc; /** * @author Igor Ermolenko */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class ReactiveAerospikeRepositoryFindRelatedTests extends BaseReactiveIntegrationTests { diff --git a/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositorySaveRelatedTests.java b/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositorySaveRelatedTests.java index 74271d460..1b3cdf5df 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositorySaveRelatedTests.java +++ b/src/test/java/org/springframework/data/aerospike/repository/reactive/ReactiveAerospikeRepositorySaveRelatedTests.java @@ -9,6 +9,7 @@ import org.springframework.data.aerospike.sample.ReactiveCompositeObjectRepository; import org.springframework.data.aerospike.sample.ReactiveCustomerRepository; import org.springframework.data.aerospike.sample.SimpleObject; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Flux; import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; @@ -17,10 +18,13 @@ import java.util.Arrays; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Igor Ermolenko */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup public class ReactiveAerospikeRepositorySaveRelatedTests extends BaseReactiveIntegrationTests { @Autowired diff --git a/src/test/java/org/springframework/data/aerospike/repository/support/AerospikeRepositoryFactoryTest.java b/src/test/java/org/springframework/data/aerospike/repository/support/AerospikeRepositoryFactoryTest.java index 839acb0f6..9c5243fde 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/support/AerospikeRepositoryFactoryTest.java +++ b/src/test/java/org/springframework/data/aerospike/repository/support/AerospikeRepositoryFactoryTest.java @@ -32,17 +32,21 @@ import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.PersistentEntityInformation; +import org.springframework.test.context.TestPropertySource; import java.io.Serializable; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Peter Milne * @author Jean Mercier */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup @MockitoSettings(strictness = Strictness.LENIENT) @ExtendWith(MockitoExtension.class) public class AerospikeRepositoryFactoryTest { diff --git a/src/test/java/org/springframework/data/aerospike/repository/support/ReactiveAerospikeRepositoryFactoryTest.java b/src/test/java/org/springframework/data/aerospike/repository/support/ReactiveAerospikeRepositoryFactoryTest.java index 203f6e2ee..7fbf02efe 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/support/ReactiveAerospikeRepositoryFactoryTest.java +++ b/src/test/java/org/springframework/data/aerospike/repository/support/ReactiveAerospikeRepositoryFactoryTest.java @@ -32,16 +32,20 @@ import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.PersistentEntityInformation; +import org.springframework.test.context.TestPropertySource; import java.io.Serializable; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Igor Ermolenko */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup @MockitoSettings(strictness = Strictness.LENIENT) @ExtendWith(MockitoExtension.class) public class ReactiveAerospikeRepositoryFactoryTest { diff --git a/src/test/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepositoryTest.java b/src/test/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepositoryTest.java index 1e8f57039..4a985d99c 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepositoryTest.java +++ b/src/test/java/org/springframework/data/aerospike/repository/support/SimpleAerospikeRepositoryTest.java @@ -34,7 +34,9 @@ import org.springframework.data.domain.Sort; import org.springframework.data.keyvalue.core.IterableConverter; import org.springframework.data.repository.core.EntityInformation; +import org.springframework.test.context.TestPropertySource; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -44,14 +46,17 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Peter Milne * @author Jean Mercier */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup @MockitoSettings(strictness = Strictness.LENIENT) @ExtendWith(MockitoExtension.class) public class SimpleAerospikeRepositoryTest { @@ -103,7 +108,7 @@ public void saveIterableOfS() { List result = aerospikeRepository.saveAll(testPersons); assertThat(result).isEqualTo(testPersons); - verify(operations, times(testPersons.size())).save(any()); + verify(operations).saveAll(testPersons); } @Test @@ -183,10 +188,18 @@ public void deleteAllById() { } @Test - public void deleteIterableOfQExtendsT() { - // batch write operations are supported starting with Server version 6.0+ - additionalAerospikeTestOperations.deleteAll(aerospikeRepository, testPersons); - verify(operations, times(testPersons.size())).delete(any(Person.class)); + public void deleteAllIterable() throws NoSuchFieldException, IllegalAccessException { + Field field = aerospikeRepository.getClass().getDeclaredField("entityInformation"); + field.setAccessible(true); + EntityInformation entityInformation = mock(EntityInformation.class); + field.set(aerospikeRepository, entityInformation); + when(entityInformation.getId(any(Person.class))).thenReturn(testPerson.getId()); + when(entityInformation.getJavaType()).thenReturn(Person.class); + + aerospikeRepository.deleteAll(List.of(testPerson)); + List ids = List.of(testPerson.getId()); + + verify(operations).deleteByIds(ids, Person.class); } @Test diff --git a/src/test/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepositoryTest.java b/src/test/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepositoryTest.java index a51f8c1d4..67c41808e 100644 --- a/src/test/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepositoryTest.java +++ b/src/test/java/org/springframework/data/aerospike/repository/support/SimpleReactiveAerospikeRepositoryTest.java @@ -27,9 +27,11 @@ import org.springframework.data.aerospike.core.ReactiveAerospikeOperations; import org.springframework.data.aerospike.sample.Customer; import org.springframework.data.repository.core.EntityInformation; +import org.springframework.test.context.TestPropertySource; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.lang.reflect.Field; import java.util.List; import java.util.Map; @@ -39,13 +41,17 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; /** * @author Igor Ermolenko */ +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup @ExtendWith(MockitoExtension.class) public class SimpleReactiveAerospikeRepositoryTest { @@ -81,12 +87,12 @@ public void save() { @Test public void saveAllIterable() { - when(operations.save(any(Customer.class))).then(invocation -> Mono.just(invocation.getArgument(0))); + when(operations.saveAll(any())).then(invocation -> Flux.fromIterable(invocation.getArgument(0))); List result = repository.saveAll(testCustomers).collectList().block(); assertThat(result).hasSameElementsAs(testCustomers); - verify(operations, times(testCustomers.size())).save(any(Customer.class)); + verify(operations).saveAll(testCustomers); } @Test @@ -202,11 +208,34 @@ public void testDelete() { } @Test - public void testDeleteAllIterable() { - when(operations.delete(any(Customer.class))).thenReturn(Mono.just(true)); + public void testDeleteAllById() throws NoSuchFieldException, IllegalAccessException { + Field field = repository.getClass().getDeclaredField("entityInformation"); + field.setAccessible(true); + EntityInformation entityInformation = mock(EntityInformation.class); + field.set(repository, entityInformation); + when(entityInformation.getJavaType()).thenReturn(Customer.class); + + List customersIds = testCustomers.stream() + .map(Customer::getId) + .collect(toList()); + repository.deleteAllById(customersIds); + + verify(operations).deleteByIds(customersIds, Customer.class); + } - reactiveBlockingAerospikeTestOperations.deleteAll(repository, testCustomers); - verify(operations, times(testCustomers.size())).delete(any(Customer.class)); + @Test + public void testDeleteAllIterable() throws NoSuchFieldException, IllegalAccessException { + Field field = repository.getClass().getDeclaredField("entityInformation"); + field.setAccessible(true); + EntityInformation entityInformation = mock(EntityInformation.class); + field.set(repository, entityInformation); + when(entityInformation.getJavaType()).thenReturn(Customer.class); + when(entityInformation.getId(any(Customer.class))).thenReturn(testCustomer.getId()); + + repository.deleteAll(List.of(testCustomer)); + + List ids = List.of(testCustomer.getId()); + verify(operations).deleteByIds(ids, Customer.class); } @Test diff --git a/src/test/java/org/springframework/data/aerospike/utility/AdditionalAerospikeTestOperations.java b/src/test/java/org/springframework/data/aerospike/utility/AdditionalAerospikeTestOperations.java index 53d73fa42..cbe816998 100644 --- a/src/test/java/org/springframework/data/aerospike/utility/AdditionalAerospikeTestOperations.java +++ b/src/test/java/org/springframework/data/aerospike/utility/AdditionalAerospikeTestOperations.java @@ -24,6 +24,7 @@ import org.springframework.data.aerospike.repository.AerospikeRepository; import org.springframework.data.aerospike.sample.Customer; import org.springframework.data.aerospike.sample.Person; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import org.testcontainers.containers.Container; import org.testcontainers.containers.GenericContainer; @@ -45,6 +46,7 @@ public abstract class AdditionalAerospikeTestOperations { private final IndexInfoParser indexInfoParser; private final IAerospikeClient client; + private final ServerVersionSupport serverVersionSupport; private final IndexesCacheRefresher indexesRefresher; private final GenericContainer aerospike; @@ -140,7 +142,8 @@ public void createIndex(String namespace, String setName, String indexName, Stri public void createIndex(String namespace, String setName, String indexName, String binName, IndexType indexType, IndexCollectionType indexCollectionType, CTX... ctx) { IndexCollectionType collType = indexCollectionType == null ? DEFAULT : indexCollectionType; - IndexUtils.createIndex(client, namespace, setName, indexName, binName, indexType, collType, ctx); + IndexUtils.createIndex(client, serverVersionSupport, namespace, setName, indexName, binName, indexType, collType, + ctx); indexesRefresher.refreshIndexesCache(); } @@ -149,15 +152,15 @@ public void createIndexes(Collection indexesToBeCreated) { IndexCollectionType collType = index.getIndexCollectionType() == null ? DEFAULT : index.getIndexCollectionType(); - IndexUtils.createIndex(client, getNamespace(), index.getSet(), index.getName(), index.getBin(), - index.getIndexType(), collType, index.getCtx()); + IndexUtils.createIndex(client, serverVersionSupport, getNamespace(), index.getSet(), index.getName(), + index.getBin(), index.getIndexType(), collType, index.getCtx()); } ); indexesRefresher.refreshIndexesCache(); } public void dropIndex(String setName, String indexName) { - IndexUtils.dropIndex(client, getNamespace(), setName, indexName); + IndexUtils.dropIndex(client, serverVersionSupport, getNamespace(), setName, indexName); indexesRefresher.refreshIndexesCache(); } @@ -167,7 +170,7 @@ public void dropIndex(Class entityClass, String indexName) { public void dropIndexes(Collection indexesToBeDropped) { indexesToBeDropped.forEach(index -> { - IndexUtils.dropIndex(client, getNamespace(), index.getSet(), index.getName()); + IndexUtils.dropIndex(client, serverVersionSupport, getNamespace(), index.getSet(), index.getName()); } ); indexesRefresher.refreshIndexesCache(); @@ -175,7 +178,7 @@ public void dropIndexes(Collection indexesToBeDropped) { public void deleteAll(AerospikeRepository repository, Collection entities) { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { try { repository.deleteAll(entities); } catch (AerospikeException.BatchRecordArray ignored) { @@ -188,7 +191,7 @@ public void deleteAll(AerospikeRepository repository, Collection en public void saveAll(AerospikeRepository repository, Collection entities) { // batch write operations are supported starting with Server version 6.0+ - if (ServerVersionUtils.isBatchWriteSupported(client)) { + if (serverVersionSupport.batchWrite()) { repository.saveAll(entities); } else { entities.forEach(repository::save); @@ -259,7 +262,7 @@ public static class ScanJob { } - public abstract List generateCustomers(int count); + public abstract List saveGeneratedCustomers(int count); - public abstract List generatePersons(int count); + public abstract List saveGeneratedPersons(int count); } diff --git a/src/test/java/org/springframework/data/aerospike/utility/IndexUtils.java b/src/test/java/org/springframework/data/aerospike/utility/IndexUtils.java index c09ccaabb..5ce58cba3 100644 --- a/src/test/java/org/springframework/data/aerospike/utility/IndexUtils.java +++ b/src/test/java/org/springframework/data/aerospike/utility/IndexUtils.java @@ -11,6 +11,7 @@ import com.aerospike.client.task.IndexTask; import org.springframework.data.aerospike.query.cache.IndexInfoParser; import org.springframework.data.aerospike.query.model.Index; +import org.springframework.data.aerospike.server.version.ServerVersionSupport; import java.util.Arrays; import java.util.List; @@ -19,8 +20,9 @@ public class IndexUtils { - static void dropIndex(IAerospikeClient client, String namespace, String setName, String indexName) { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(client)) { + static void dropIndex(IAerospikeClient client, ServerVersionSupport serverVersionSupport, String namespace, + String setName, String indexName) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { waitTillComplete(() -> client.dropIndex(null, namespace, setName, indexName)); } else { // ignoring ResultCode.INDEX_NOTFOUND for Aerospike Server prior to ver. 6.1.0.1 @@ -28,10 +30,10 @@ static void dropIndex(IAerospikeClient client, String namespace, String setName, } } - static void createIndex(IAerospikeClient client, String namespace, String setName, String indexName, - String binName, IndexType indexType, IndexCollectionType collectionType, - CTX[] ctx) { - if (ServerVersionUtils.isDropCreateBehaviorUpdated(client)) { + static void createIndex(IAerospikeClient client, ServerVersionSupport serverVersionSupport, String namespace, + String setName, String indexName, String binName, IndexType indexType, + IndexCollectionType collectionType, CTX[] ctx) { + if (serverVersionSupport.isDropCreateBehaviorUpdated()) { waitTillComplete(() -> client.createIndex(null, namespace, setName, indexName, binName, indexType, collectionType, ctx)); } else { diff --git a/src/test/java/org/springframework/data/aerospike/utility/InfoResponseUtilsTest.java b/src/test/java/org/springframework/data/aerospike/utility/InfoResponseUtilsTests.java similarity index 91% rename from src/test/java/org/springframework/data/aerospike/utility/InfoResponseUtilsTest.java rename to src/test/java/org/springframework/data/aerospike/utility/InfoResponseUtilsTests.java index 47d271df8..3a5b17cc1 100644 --- a/src/test/java/org/springframework/data/aerospike/utility/InfoResponseUtilsTest.java +++ b/src/test/java/org/springframework/data/aerospike/utility/InfoResponseUtilsTests.java @@ -1,13 +1,17 @@ package org.springframework.data.aerospike.utility; import org.junit.jupiter.api.Test; +import org.springframework.test.context.TestPropertySource; import java.util.function.Function; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; -class InfoResponseUtilsTest { +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup +class InfoResponseUtilsTests { @Test void propertyInStartOfResponseSemicolonSeparated() { diff --git a/src/test/java/org/springframework/data/aerospike/utility/TimeUtilsTest.java b/src/test/java/org/springframework/data/aerospike/utility/TimeUtilsTests.java similarity index 71% rename from src/test/java/org/springframework/data/aerospike/utility/TimeUtilsTest.java rename to src/test/java/org/springframework/data/aerospike/utility/TimeUtilsTests.java index c2defa90f..46521ab78 100644 --- a/src/test/java/org/springframework/data/aerospike/utility/TimeUtilsTest.java +++ b/src/test/java/org/springframework/data/aerospike/utility/TimeUtilsTests.java @@ -3,11 +3,15 @@ import org.assertj.core.data.Offset; import org.joda.time.DateTime; import org.junit.jupiter.api.Test; +import org.springframework.test.context.TestPropertySource; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.data.aerospike.query.cache.IndexRefresher.INDEX_CACHE_REFRESH_SECONDS; import static org.springframework.data.aerospike.sample.SampleClasses.EXPIRATION_ONE_SECOND; -public class TimeUtilsTest { +@TestPropertySource(properties = {INDEX_CACHE_REFRESH_SECONDS + " = 0", "createIndexesOnStartup = false"}) +// this test class does not require secondary indexes created on startup +public class TimeUtilsTests { @Test public void shouldConvertOffsetInSecondsToUnixTime() {