Coder Social home page Coder Social logo

aerospike / spring-data-aerospike Goto Github PK

View Code? Open in Web Editor NEW

This project forked from spring-attic/spring-data-aerospike

42.0 21.0 28.0 4.26 MB

Spring Data Aerospike

Home Page: https://aerospike.github.io/spring-data-aerospike/

License: Apache License 2.0

Java 99.97% Lua 0.03%
spring-data spring-data-aerospike aerospike framework java spring spring-boot

spring-data-aerospike's Introduction

Spring Data Aerospike

maven ci javadoc

The Spring Data Aerospike project aims to provide a familiar and consistent Spring-based programming model for new data stores while retaining store-specific features and capabilities. It provides integration with the Aerospike database. Key functional areas of Spring Data Aerospike are a POJO centric model for interacting with Aerospike DB and easily writing a repository style data access layer.

Documentation

Examples

  1. Demo project example with a step-by-step tutorial can be found here

  2. Demo project with detailed guides is located here

Spring Data Aerospike Compatibility

Compatibility Table
Spring Data Aerospike Spring Boot Aerospike Client Aerospike Reactor Client Aerospike Server

4.7.x

3.2.x

7.2.x

7.1.x

5.2.x.x

4.6.x

3.2.x

7.2.x

7.1.x

5.2.x.x

4.5.x

3.1.x

7.1.x

7.0.x

5.2.x.x

4.4.x

3.1.x

7.0.x

7.0.x

5.2.x.x

4.3.x

3.1.x

6.1.x

6.1.x

5.2.x.x

4.2.x

3.0.x

6.1.x

6.1.x

5.2.x.x

4.1.x

3.0.x

6.1.x

6.1.x

5.2.x.x

3.5.x

2.7.x

6.1.x

6.1.x

5.2.x.x

3.4.x

2.6.x

5.1.x

5.1.x

5.2.x.x

3.3.x

2.5.x

5.1.x

5.1.x

5.2.x.x

3.2.x

2.5.x

5.1.x

5.0.x

5.2.x.x

3.0.x, 3.1.x

2.5.x

5.1.x

5.0.x

2.5.x

2.5.x

4.4.x

4.4.x

2.4.2.RELEASE

2.3.x

4.4.x

4.4.x

2.3.5.RELEASE

2.2.x

4.4.x

4.4.x

2.1.1.RELEASE

2.1.x, 2.0.x

4.4.x

3.2.x

1.2.1.RELEASE

1.5.x

4.1.x

Quick Start

1. Maven configuration

Add the spring-data-aerospike Maven dependency:

<dependency>
  <groupId>com.aerospike</groupId>
  <artifactId>spring-data-aerospike</artifactId>
  <version>${spring-data-aerospike.version}</version>
</dependency>

Notes:

2. AerospikeRepository

AerospikeRepository is the simplest way to interact with Aerospike using Spring Data Aerospike.

Create your own custom repository that extends AerospikeRepository which will provide out-of-the-box CRUD operations and query implementations, so you can easily save, find, delete and query single entities and collections of them.

Implementation will be determined by the method names automatically, no need to write any implementation.

For example, given a Person class with first and last name properties, a PersonRepository interface that can query for Person by last name and when the first name matches a like expression is shown below:

public interface PersonRepository extends AerospikeRepository<Person, Long> {

    List<Person> findByLastname(String lastname);

    List<Person> findByFirstnameLike(String firstname);
}

For non-blocking reactive API use ReactiveAerospikeRepository.

3. Configuration

In order to configure Spring Data Aerospike you will need to create a configuration class that extends AbstractAerospikeDataConfiguration and defines the relevant Spring Data Repositories via @EnableAerospikeRepositories annotation.

To set the connection details you can either override getHosts() and nameSpace() methods of the AbstractAerospikeDataConfiguration class or define spring-data-aerospike.connection.hosts and spring-data-aerospike.connection.namespace in application.properties file.

Note
You can further customize your configuration by changing other settings.

Here is a simple example of a configuration class that sets up a connection to a local Aerospike DB instance:

@Configuration
@EnableAerospikeRepositories(basePackageClasses = PersonRepository.class)
class ApplicationConfig extends AbstractAerospikeDataConfiguration {

    @Override
    protected Collection<Host> getHosts() {
        return Collections.singleton(new Host("localhost", 3000));
    }

    @Override
    protected String nameSpace() {
        return "test";
    }
}

Usage

Below is an example of a service that uses PersonRepository operations.

  • deleteAll and save are provided automatically by extending AerospikeRepository interface

  • findByFirstnameLike and findByLastname methods were defined in PersonRepository but there was no need to write the actual implementation, it is determined automatically from the method names

@Service
public class PersonService {

    private final PersonRepository personRepository;

    @Autowired
    public PersonService(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    public void example() {
        // Delete all existing persons
        personRepository.deleteAll();

        Person person = new Person();
        person.setFirstname("John");
        person.setLastname("Smith");
        // Save the new created person
        personRepository.save(person);

        // Get all persons whose first name starts with "Jo"
        List<Person> firstNameResults = personRepository.findByFirstnameLike("Jo*");
        // Get all persons whose last name is equal to "Smith"
        List<Person> lastNameResults = personRepository.findByLastname("Smith");
    }
}

AerospikeOperations

AerospikeOperations is the base interface for Aerospike database operations. It is implemented by AerospikeTemplate class.

As a lower-level alternative to AerospikeRepository, AerospikeOperations supports wider variety of operations and greater flexibility, but requires a bit more code writing and less out-of-the-box functionality.

Features supported by AerospikeOperations:

  • Basic support for mapping POJOs to and from Aerospike bins

  • Convenience CRUD (Create, Read, Update and Delete) methods for interacting with Aerospike

  • Rich query API

  • Access to the native Aerospike Java Client (reactive and non-reactive)

  • Translating exceptions into Spring’s technology-agnostic DAO exception hierarchy

For non-blocking reactive API use ReactiveAerospikeOperations.

Getting Help

Contributing to Spring Data Aerospike

Here are some ways you can get involved:

  • Help out on the StackOverflow spring-data-aerospike tag by responding to questions and joining the debate

  • Create a GitHub issue for a feature request or bug fixing, comment and vote on the ones that you are interested in

  • GitHub is for social coding: we encourage contributions through pull requests from forks of this repository. When contributing code, please reference a specific GitHub issue you are addressing

  • Watch for upcoming articles by subscribing to Aerospike Stand-Up

spring-data-aerospike's People

Contributors

agrgr avatar aloren avatar arrowplum avatar avi054 avatar briannichols avatar carosys avatar dependabot-preview[bot] avatar dependabot[bot] avatar echatman-ias avatar emax19 avatar helipilot50 avatar igor-ermolenko avatar ijusti avatar jnopnop avatar kptfh avatar mrozk avatar mustafa-zidan avatar mzhangsfly avatar odrotbohm avatar playtikagithub avatar rbotzer avatar reugn avatar roimenashe avatar samarthsinha avatar tdanylchuk avatar venilnoronha avatar wchu-citrusleaf avatar wolfchkov avatar yevtsy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spring-data-aerospike's Issues

AerospikeTemplate.update operation does not consider document version

See more details in #47.

TL;DR
Currently update operation in AerospikeTemplate does not consider document's version property and overwrites document even when document's version is not equal to server version.

Thoughts
If we change this behavior -- we can break existing users that expect such behavior.
If we do not change this behavior -- some users may potentially overwrite data that has not been seen.
Also we do not know whether such behavior in spring-data-aerospike for update operation was intended or not. Probably @helipilot50 could help with that as the first update implementation was done by him.

FYI @vasilievip @tdanylchuk

@Expiration annotation doesn't accept 0, -1, -2 values.

Based on Aerospike Policies(https://www.aerospike.com/docs/guide/policies.html) the Expiration or Time to Live can take the following values.
-2 - Do not change ttl when record is updated.
-1 - Never expire.
0 - Default to namespace configuration variable "default-ttl" on the server.
greater than 0 — Actual ttl in seconds.

I checked the code and you have an explicit validation about this in MappingAerospikeWriteConverter.

private int getExpiration(AerospikePersistentEntity<?> entity, ConvertingPropertyAccessor accessor) { AerospikePersistentProperty expirationProperty = entity.getExpirationProperty(); if (expirationProperty != null) { int expiration = getExpirationFromProperty(accessor, expirationProperty); Assert.isTrue(expiration > 0, "Expiration value must be greater than zero, but was: " + expiration); return expiration; } return entity.getExpiration(); }

when I use @Document expiration then throw AerospikeException(resultCode = 22)

hello.

when I use this code

@Document(expiration = 30000)
data class Test(
    @Id
    val id: String,
    val test: Long,
): Serializable {
    companion object {
        private const val serialVersionUID = 1L
    }
}

then occurred exception where AsyncWrite
(reactive aerospike client)

image

but I remove @document expiration option then error is gone.

@Document(expiration = 30000)
data class Test(
    @Id
    val id: String,
    val test: Long,
) : Serializable {
    companion object {
        private const val serialVersionUID = 1L
    }
} 

when I add expiration on @document then I meet exception.

how I add ttl? and pass error?

Thank you.

ps - dependencies

    implementation("com.aerospike:spring-data-aerospike:2.4.2.RELEASE")
    implementation("com.aerospike:aerospike-client:5.0.6")

Avoid extra evaluations when resolving "dynamic" parameters for entity

We use dynamic properties for "set name" and "expiration":

@Document(
        collection = "${aerospike.collection:${spring.application.name}-user}",
        expirationExpression = "${aerospike.expiration:-1}"
)
public class User {
    @Id
    private Long userId;
     .  .  .
}

During profiling of memory allocations with async-profiler tool we've found an issue inside the library.

image

As we can see from the diagram it tries to resolve "set name" and "expiration" from environment on each save operation.

Seems setName and expiration values can be cached once they were resolved.

save method doesn't return document version

When method save is invoked on repository parameterized with versioned document it's expected that returned document will contain not null versioned field.

Class SimpleAerospikeRepository contains following:

  @Override
public <S extends T> S save(S entity) {
	Assert.notNull(entity, "Cannot save NULL entity");

	operations.save(entity);
	return entity;                            <-- **returns not saved entity**  
}

Update/delete record fails while using EXPECT_GEN_GT/EXPECT_GEN_EQUAL write generation policies

Write Generation Policy
EXPECT_GEN_EQUAL — Update/delete record if expected generation is equal to server generation. Otherwise, fail.
EXPECT_GEN_GT — Update/delete record if expected generation greater than the server generation. Otherwise, fail. This is useful for restore after backup.

Documentation tells us that Write Generation Policy can be used to handle update/delete requests.

Relative PR with tests cases which shows the issues:
#46

Expectations:
Let's say I use
policy.writePolicyDefault.generationPolicy = GenerationPolicy.EXPECT_GEN_EQUAL;
and record with @Version property. I want to have the next cases:

  1. Create record -> update it with the same version -> check updates are applied
  2. Create record -> update it with different version -> update fails
  3. Create record -> delete it by id -> check record doesn't exist
  4. Create record -> delete it by object -> check record doesn't exist

Actual result:
All these requests are fail with generation-related error

org.springframework.dao.RecoverableDataAccessException: Error 3,1,30000,0,0,BB9030011AC4202 127.0.0.1 32879: Generation error; nested exception is com.aerospike.client.AerospikeException: Error 3,1,30000,0,0,BB9030011AC4202 127.0.0.1 32879: Generation error
	at org.springframework.data.aerospike.core.DefaultAerospikeExceptionTranslator.translateExceptionIfPossible(DefaultAerospikeExceptionTranslator.java:73)
	at org.springframework.data.aerospike.core.AerospikeTemplate.delete(AerospikeTemplate.java:227)
	at org.springframework.data.aerospike.core.AerospikeClientPolicyGenerationTest.shouldSuccessfullyDeleteRecordWithSameGeneration(AerospikeClientPolicyGenerationTest.java:104)

Caused by: com.aerospike.client.AerospikeException: Error 3,1,30000,0,0,BB9030011AC4202 127.0.0.1 32879: Generation error
	at com.aerospike.client.command.DeleteCommand.parseResult(DeleteCommand.java:77)
	at com.aerospike.client.command.SyncCommand.execute(SyncCommand.java:103)
	at com.aerospike.client.command.SyncCommand.execute(SyncCommand.java:50)
	at com.aerospike.client.AerospikeClient.delete(AerospikeClient.java:559)
	at org.springframework.data.aerospike.core.AerospikeTemplate.delete(AerospikeTemplate.java:225)```

release

Could you please provide release for the latest changes? Thanks

Storing a user key twice

We store @user_key by default in Aerospike when using spring-data-aerospike.
In some scenarios we store the user key twice, for example:

  1. "PK" bin sends via save() method when using AerospikeRepository (WritePolicyBuilder.sendkey = true in BaseAerospikeTemplate class).

  2. "@user_key" bin is added in MappingAerospikeWriteConverter class (in all spring-data-aerospike operations).

Attached image that shows an example of storing the user key twice:
issue screenshot

Expiration property can not be deserialized for immutable document

Document:

	@Value
	@Document(collection = "expiration-set")
	public static class DocumentWithExpirationAnnotationAndPersistenceConstructor {

		@Id
		private final String id;

		@Expiration
		private final Long expiration;

		@PersistenceConstructor
		public DocumentWithExpirationAnnotationAndPersistenceConstructor(String id, Long expiration) {
			this.id = id;
			this.expiration = expiration;
		}
	}

fails with: UnsupportedOperationException: No accessor to set property

Evolved schema is not persisted to aerospike database

Hi,

I've below entity class -

    package com.demo.aerospike.entity;

    import org.springframework.data.annotation.Id;
    import java.io.Serializable;
    import java.util.HashMap;

    public class User implements Serializable {

        @Id
        private String id;

        private HashMap<String, String> eUserIds = new HashMap();

        //getters/setters/toString/equals/hashCode
    }

Below is the repository class -

package com.demo.aerospike.repositories;

import com.demo.aerospike.entity.User;
import org.springframework.data.aerospike.repository.AerospikeRepository;

public interface UserRepository extends AerospikeRepository<User, String> {}

Below is spring application context -

package com.demo.aerospike.config;

import com.aerospike.client.AerospikeClient;
import com.aerospike.client.policy.ClientPolicy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.aerospike.core.AerospikeTemplate;
import org.springframework.data.aerospike.repository.config.EnableAerospikeRepositories;

@Configuration
@EnableAerospikeRepositories(basePackages = "com.demo.aerospike.repositories")
public class AerospikeConfig {
    public @Bean(destroyMethod = "close") AerospikeClient aerospikeClient() {
        ClientPolicy policy = new ClientPolicy();
        policy.failIfNotConnected = true;
        String localhost = "localhost";
        return new AerospikeClient(policy, localhost, 3000);
    }

    public @Bean AerospikeTemplate aerospikeTemplate() {
        return new AerospikeTemplate(aerospikeClient(), "test");
    }
}

Below is my main class -

package com.demo.aerospike;

import com.demo.aerospike.config.AerospikeConfig;
import com.demo.aerospike.entity.User;
import com.demo.aerospike.repositories.UserRepository;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.*;

public class MySpringDataAerospikeDemo {

    public static void main(String []args) {

        ApplicationContext ctx = new AnnotationConfigApplicationContext(AerospikeConfig.class);
        UserRepository repository = ctx.getBean(UserRepository.class);
        HashMap<String, String> daveMap = new HashMap<>();
        daveMap.put(UUID.randomUUID().toString(), UUID.randomUUID().toString());
        User dave = new User("Dave-01", daveMap);
        repository.save(dave);
        Optional<User> davenew = repository.findById(dave.getId());
        System.out.println("new dave is \n" + davenew);
    }
}

All the above is working fine.
The issue comes when I am adding new field say name to User entity.

    package com.demo.aerospike.entity;

    import org.springframework.data.annotation.Id;
    import java.io.Serializable;
    import java.util.HashMap;

    public class User implements Serializable {

        @Id
        private String id;

        private String name;
        
        private HashMap<String, String> eUserIds = new HashMap();

        //getters/setters/toString/equals/hashCode
    }

The new main program is -


public class MySpringDataAerospikeDemo {

    public static void main(String []args) {

        ApplicationContext ctx = new AnnotationConfigApplicationContext(AerospikeConfig.class);
        UserRepository repository = ctx.getBean(UserRepository.class);
        HashMap<String, String> daveMap = new HashMap<>();
        daveMap.put(UUID.randomUUID().toString(), UUID.randomUUID().toString());
        User dave = new User("Dave-01", daveMap);
        dave.setName("Dave");
        repository.save(dave);
        Optional<User> davenew = repository.findById(dave.getId());
        System.out.println("new dave is \n" + davenew);
    }
}

the name field is not added to schema. only fields which are mentioned in V1 version of User are persisted.

I've found workaround But it would be good if it can work by default.

The workaround is run the below program to create new column in the set

package com.demo.aerospike;

import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Bin;
import com.aerospike.client.Key;
import com.aerospike.client.Record;
import java.util.Map;

public class AerospikeJavaDemo {

    public static void main(String[] args) {
        AerospikeClient client = new AerospikeClient("localhost", 3000);
        Key k1 = new Key("test", "User", "Dave-01");
        Record record = client.get(null, k1);
        Map<String, Object> bins = record.bins;
        bins.put("name", "Dave");
        System.out.println("record is "+ bins);
        int binSize = bins.size();
        System.out.println("binSize = " + binSize);
        Bin[] binArray =  bins.entrySet().stream().map(x -> new Bin(x.getKey(), x.getValue())).toArray(Bin[]::new);
        client.put(null, k1,binArray);
    }
}

Is there a way I can achieve schema evolution without any workaround seamlessly?

Support For CDT

I’m trying to run an example with Maps in maps, but the library version didn’t include com.aerospike.client.cdt.CTX;

So I upgraded from 1.0.2 to
compile('com.aerospike:spring-data-aerospike:2.2.0.RELEASE')
But now I get
Caused by: java.lang.ClassNotFoundException: org.springframework.data.mapping.MappingException
at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_232]
at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[na:1.8.0_232]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352) ~[na:1.8.0_232]
at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[na:1.8.0_232]
... 69 common frames omitted

`byte[]` data type doesn't work

When I use byte[] field type of entity:
Document(collection = "bug-set") public static class BugCollectionClass { @Id private String id; private byte[] data; public BugCollectionClass(String id, byte[] data) { this.id = id; this.data = data; } }
then I catch an Exception during findById method of AerospikeTemplate class:
java.lang.ClassCastException: class [B cannot be cast to class java.util.Collection ([B and java.util.Collection are in module java.base of loader 'bootstrap') at org.springframework.data.aerospike.convert.MappingAerospikeReadConverter.readValue(MappingAerospikeReadConverter.java:148) at org.springframework.data.aerospike.convert.MappingAerospikeReadConverter.access$100(MappingAerospikeReadConverter.java:48) at org.springframework.data.aerospike.convert.MappingAerospikeReadConverter$RecordReadingPropertyValueProvider.getPropertyValue(MappingAerospikeReadConverter.java:246) at org.springframework.data.aerospike.convert.MappingAerospikeReadConverter$RecordReadingPropertyValueProvider.getPropertyValue(MappingAerospikeReadConverter.java:225) at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:71) at org.springframework.data.convert.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.extractInvocationArguments(ClassGeneratingEntityInstantiator.java:250) at org.springframework.data.convert.ClassGeneratingEntityInstantiator$EntityInstantiatorAdapter.createInstance(ClassGeneratingEntityInstantiator.java:223) at org.springframework.data.convert.ClassGeneratingEntityInstantiator.createInstance(ClassGeneratingEntityInstantiator.java:84) at org.springframework.data.aerospike.convert.MappingAerospikeReadConverter.getConvertingPropertyAccessor(MappingAerospikeReadConverter.java:206) at org.springframework.data.aerospike.convert.MappingAerospikeReadConverter.read(MappingAerospikeReadConverter.java:85) at org.springframework.data.aerospike.convert.MappingAerospikeConverter.read(MappingAerospikeConverter.java:78) at org.springframework.data.aerospike.core.AerospikeTemplate.mapToEntity(AerospikeTemplate.java:761) at org.springframework.data.aerospike.core.AerospikeTemplate.findById(AerospikeTemplate.java:337) at org.springframework.data.aerospike.repository.support.SimpleAerospikeRepository.findById(SimpleAerospikeRepository.java:48) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359) at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy105.findById(Unknown Source)

Throwing exceptions instead of returning Mono.error breaks the reactive chain.

You use org.springframework.util.Assert to validate different things. Ex:
Assert.isTrue(expiration > 0, "Expiration value must be greater than zero, but was: " + expiration);
In my reactive code I have many .onErrorResume that should catch exceptions. They work only if a Mono.error is triggered in the chain. Using the spring Assert will directly throw an exception and all my .onErrorResume are skipped and the exception is handled by spring default error handling. Which is not what we want if you rely heavily to control the flow using .onError.. operators.

To fix this i need to do something like this in my reactive repository:

default Mono<Document> saveWithErrorHandling(Document document){
	try {
		return save(document);
	}catch (Exception exception){
		return Mono.error(exception);
	}
}

You can do this try catch at the library level so we don't have to do it in our application. Or you can remove all the code that throws exceptions, and replace with Mono.error.

circular dependency in 2.4.1

After update spring-data-aerospike from 2.4.0 to 2.4.1 spring application context failed to load with circular dependency between

┌─────┐
| aerospikePersistenceEntityIndexCreator defined in com.playtika.services.contest.run.db.AerospikeConfiguration
↑ ↓
| aerospikeMappingContext defined in com.playtika.services.contest.run.db.AerospikeConfiguration
└─────┘
It seems that during creation of AerospikeMappingContext bean, scan of the root package for Aerospike configuration is happening.
During processing of the entities found, MappingContextEvent is published that leads to fail.
As a workaround Aerospike configuration bean could be moved to the separate package.

See: aerospike-community/spring-data-aerospike-demo#4

would you tell me why can't find AerospikeReactorClient bean?

spring container load fail and I see this error log => required a bean of type 'com.aerospike.client.reactor.AerospikeReactorClient' that could not be found.

image

but AbstractReactiveAerospikeDataConfiguration have @bean configuration for AerospikeReactorClient

image

I can't understand this situation.

can I ask why this is?

Unable to read saved map

Consider having class:

@Document(collection = "hive")
@Builder(toBuilder = true)
public class Doc {

    @Id
    private Id id;

    @Field
    private Map<String, Object> data = new HashMap<>();

    @Version
    private Long versionId;

    @Expiration
    private long expirationTs;
}

Consider having value Map<String, Object> saved into data.
Write operation will go as expected, but on read you will have:


java.lang.IllegalArgumentException: Entity must not be null!

	at org.springframework.util.Assert.notNull(Assert.java:198)
	at org.springframework.data.convert.EntityInstantiators.getInstantiatorFor(EntityInstantiators.java:88)
	at org.springframework.data.aerospike.convert.MappingAerospikeReadConverter.getConvertingPropertyAccessor(MappingAerospikeReadConverter.java:200)
	at org.springframework.data.aerospike.convert.MappingAerospikeReadConverter.convertCustomType(MappingAerospikeReadConverter.java:151)

Here is a test to reproduce:

	@Test
	public void shouldReadMapWithObjectValue1() throws Exception {
		Map<String, Object> bins = of(
				"mapWithNonSimpleValue", of("map" , of("key", "value")),
				"@_class", MapWithGenericValue.class.getName(),
				"@user_key", "10"
		);
		AerospikeReadData forRead = AerospikeReadData.forRead(new Key(NAMESPACE, SIMPLESET, 10L), record(bins));

		MapWithGenericValue<Object> actual = converter.read(MapWithGenericValue.class, forRead);

		Map<String, Object> value = of("map", of("key", "value"));
		MapWithGenericValue<Object> expected = new MapWithGenericValue(10L, value);
		assertThat(actual).isEqualTo(expected);
	}

Question on set name customization

I am reading data from a set whose name is in SnakeCase(i.e. product_data_cache) format. Since the set name is derived from entity class, it is little inconvenient to create a class that doesn't follow java naming convention. Is it possible to overwrite set name resolution preferably through annotation?

for example:

@Set("product_data_cache")
public class ProductDataCache{
}

support index collection type

currently having index for the bin with the same index type, but different collection type prevents loading indexes cache

Cache connected to namespace instead of set

Could anyone please explain to me why a cache connected to a namespace instead of a set?

In according to documentation "Namespaces are like tablespaces for traditional SQL databases."
Nobody split collection in RDBMS by tablespaces, everyone splits by tables.

Or is this a special restriction to have only two caches for two different types?

WARNING in logs: Registering converter as reading converter although it doesn't convert from a store-supported type

2020-07-23T08:07:35.2174109Z 08:07:35.210 [main] WARN o.s.data.convert.CustomConversions - Registering converter from class org.springframework.data.aerospike.convert.AerospikeReadData to class org.springframework.data.aerospike.SampleClasses$User as reading converter although it doesn't convert from a store-supported type! You might want to check your annotation setup at the converter implementation.

Upgrading the spring-cloud-starter to 3.X.X (3.0.1)

@Aloren

if we upgrade the cloud starter to 3.X.X we are getting the following exception related to Playtika's test containers:

10:24:27.923 [main] ERROR o.s.test.context.TestContextManager - Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@60b616c8] to prepare test instance [org.springframework.data.aerospike.repository.PersonRepositoryQueryTests@5112b7]
java.lang.IllegalStateException: Failed to load ApplicationContext
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118)
	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
	at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:43)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244)
	at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$6(ClassBasedTestDescriptor.java:350)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:355)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$7(ClassBasedTestDescriptor.java:350)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:349)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$4(ClassBasedTestDescriptor.java:270)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:269)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$2(ClassBasedTestDescriptor.java:259)
	at java.base/java.util.Optional.orElseGet(Optional.java:362)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$3(ClassBasedTestDescriptor.java:258)
	at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$before$0(ClassBasedTestDescriptor.java:184)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:183)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:78)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:136)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:188)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:154)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:128)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:428)
	at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162)
	at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:562)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:548)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'blockingTestConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'embedded.aerospike.namespace' in value "${embedded.aerospike.namespace}"
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:405)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1413)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:917)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:582)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:326)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
	... 64 common frames omitted
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'embedded.aerospike.namespace' in value "${embedded.aerospike.namespace}"
	at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:178)
	at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124)
	at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239)
	at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
	at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175)
	at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:936)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
	... 81 common frames omitted

We upgraded to 2.2.7.RELEASE for now, but we will need to upgrade to 3.X.X in the future.

Touch on read fails with IllegalStateException

Currently touch on read is available only to the documents that have expiration specified via @Document annotation, but not via @Expiration field.

	@Data
	@AllArgsConstructor
	@Document(collection = "expiration-set", touchOnRead = true)
	public static class DocumentWithTouchOnReadAndExpirationProperty {

		@Id
		private String id;
		@Expiration
		private long expiration;
	}

error with spring boot

erro info is :

Caused by: java.lang.ClassNotFoundException: org.springframework.data.querydsl.QueryDslUtils
at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_171]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_171]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[na:1.8.0_171]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_171]
... 97 common frames omitted

support aerospike expressions

https://www.aerospike.com/docs/guide/expressions/

Aerospike expressions were introduced in Aerospike server 5.x. They replace predexps. Predexps will be removed next year. We need to add support for Aerospike expressions in spring-data-aerospike.
An implementation for expressions should give users an ability to migrate from predxeps to expressions (meaning that we cannot just replace predexps usage with expressions in QueryEngine). Maybe we could make this feature togglable.

Query on Secondary Index of a Map field

Given the following,

public interface UserRepository extends CrudRepository<User, Integer> {
   List<User> findAllByAddress_City(String city);
}
@Data
@AllArgsConstructor
public Address {
   String state;
   @Indexed(type = STRING, collectionType = DEFAULT)
   String city;
}
@Document
@Data
@AllArgsConstructor
public User {
   @Id Integer id;
   String name;
   Address address;
}

When we execute

repository.findAllByAddress_City("Boston");

Expected: We get a list of all users that have "Boston" set as the city field of their address field.
Actual: No filter is added to the query.

Feature: Support for Enums in Repository Functions

Summary

I'd like to use Enums in Java code and have the values saved as Strings in Aerospike. When creating new repository functions the
AerospikeQueryCreator and Value classes should use the enum.toString() function to properly generate the query.

Example

Given

@Repository
public interface UserRepository extends ReactiveAerospikeRepository<User, UUID> {

  Flux<User> findAllByStatus(Status status);
}

and

@AllArgsConstructor
public enum Status{
  ACTIVE,
  INACTIVE
}

and

@Data
@Document(collection = "user")
@Builder
@AllArgsConstructor
public class User {
  @Id UUID id;

  @Indexed(type = IndexType.STRING)
  @Field
  Status status;
}

When calling the repository function repository.findAllByStatus(Status.PENDING) the generated query would be select * from test.user where status="ACTIVE"

count method fails with exception if set is not present

java.lang.StringIndexOutOfBoundsException: begin 0, end -1, length 0
    at java.lang.String.checkBoundsBeginEnd(String.java:3319) ~[?:?]
    at java.lang.String.substring(String.java:1874) ~[?:?]
    at org.springframework.data.aerospike.core.AerospikeTemplate.count(AerospikeTemplate.java:442) ~[spring-data-aerospike-2.4.0.RELEASE.jar:?]

list contains query is not working

hi,

How can I use list indexing with spring data?

I got sample data and a index like below:

+-----+-----------+-------------------------+-------------------------------------------+
| PK  | @user_key | myList                  | @_class                                   |
+-----+-----------+-------------------------+-------------------------------------------+
| "1" | "1"       | LIST('["a", "b", "c"]') | "com.dain.TestObj" |
| "2" | "2"       | LIST('["a", "d", "e"]') | "com.dain.TestObj" |
+-----+-----------+-------------------------+-------------------------------------------+
| ns     | bin          | indextype   | set                             | state | indexname                             | path         | type      |
+--------+--------------+-------------+---------------------------------+-------+---------------------------------------+--------------+-----------+
| "test" | "myList"     | "LIST"      | "test"                          | "RW"  | "list_index_test"                     | "myList"     | "STRING"  |
+--------+--------------+-------------+---------------------------------+-------+---------------------------------------+--------------+-----------+

It gets two rows when using a query with "in list" and nothing when without "in list".

aql> select * from test.test in list where myList = "a"
+-----+-----------+-------------------------+-------------------------------------------+
| PK  | @user_key | myList                  | @_class                                   |
+-----+-----------+-------------------------+-------------------------------------------+
| "1" | "1"       | LIST('["a", "b", "c"]') | "com.dain.TestObj" |
| "2" | "2"       | LIST('["a", "d", "e"]') | "com.dain.TestObj" |
+-----+-----------+-------------------------+-------------------------------------------+
2 rows in set (0.008 secs)

aql> select * from test.test where myList = "a"
0 rows in set (0.011 secs)

Error: (201) AEROSPIKE_ERR_INDEX_NOT_FOUND

AeropsikeRepository looks like working without "in list". It means that these three methods below return 0 item.

interface TestRepository : ReactiveAerospikeRepository<TestObj, String> {
    fun findAllByMyList(str: String): Flux<TestObj>
    fun findAllByMyListContaining(str: String): Flux<TestObj>
    fun findAllByMyListContains(str: String): Flux<TestObj>
}

Again, how can I use list indexing within spring data repository? or any way to specify query explicitly?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.