Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POC for The Aerospike enterprise embedded server with mandatory durable delete #1650

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions embedded-aerospike-enterprise/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>testcontainers-spring-boot-parent</artifactId>
<groupId>com.playtika.testcontainers</groupId>
<version>3.1.0</version>
<relativePath>../testcontainers-spring-boot-parent</relativePath>
</parent>

<artifactId>embedded-aerospike-enterprise</artifactId>

<properties>
<aerospike-client.version>7.2.0</aerospike-client.version>
</properties>

<dependencies>
<dependency>
<groupId>com.aerospike</groupId>
<artifactId>aerospike-client</artifactId>
<version>${aerospike-client.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.playtika.testcontainers</groupId>
<artifactId>embedded-aerospike</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.playtika.testcontainer.enterpise.aerospike;

import com.aerospike.client.AerospikeClient;
import com.playtika.testcontainer.aerospike.AerospikeExpiredDocumentsCleaner;
import com.playtika.testcontainer.aerospike.AerospikeProperties;
import com.playtika.testcontainer.aerospike.EmbeddedAerospikeTestOperationsAutoConfiguration;
import com.playtika.testcontainer.aerospike.ExpiredDocumentsCleaner;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;

@AutoConfiguration(afterName = "org.springframework.boot.autoconfigure.aerospike.AerospikeAutoConfiguration",
before = EmbeddedAerospikeTestOperationsAutoConfiguration.class)
@ConditionalOnExpression("${embedded.containers.enabled:true}")
@ConditionalOnBean({AerospikeClient.class, AerospikeProperties.class})
@ConditionalOnProperty(value = "embedded.aerospike.enabled", matchIfMissing = true)
public class EnterpriseAerospikeTestOperationsAutoConfiguration {

@Bean
@ConditionalOnProperty(value = "embedded.aerospike.time-travel.enabled", havingValue = "true", matchIfMissing = true)
public ExpiredDocumentsCleaner expiredDocumentsCleaner(AerospikeClient client,
AerospikeProperties properties) {
return new AerospikeExpiredDocumentsCleaner(client, properties.getNamespace(), true);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.playtika.testcontainer.enterpise.aerospike;

import com.playtika.testcontainer.aerospike.EmbeddedAerospikeBootstrapConfiguration;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.LinkedHashMap;

@Slf4j
@AutoConfiguration(before = EmbeddedAerospikeBootstrapConfiguration.class)
@ConditionalOnExpression("${embedded.containers.enabled:true}")
@ConditionalOnProperty(value = "embedded.aerospike.enabled", matchIfMissing = true)
public class InjectEnterpriseAerospikeBootstrapConfiguration {

private static final String DOCKER_IMAGE = "aerospike/aerospike-server-enterprise:6.3.0.16_1";
private static final String AEROSPIKE_DOCKER_IMAGE_PROPERTY = "embedded.aerospike.dockerImage";

private ConfigurableEnvironment environment;

@Autowired
public void setEnvironment(ConfigurableEnvironment environment) {
this.environment = environment;
}

@PostConstruct
public void overrideAerospikeImage() {
log.info("Overriding aerospike Image");

String dockerImage = environment.getProperty(AEROSPIKE_DOCKER_IMAGE_PROPERTY);
if (isEnterpriseImage(dockerImage)) {
return;
}

LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put(AEROSPIKE_DOCKER_IMAGE_PROPERTY, DOCKER_IMAGE);
MapPropertySource propertySource = new MapPropertySource("modifiedAerospikeProperties", map);
environment.getPropertySources().addFirst(propertySource);
}

private boolean isEnterpriseImage(String dockerImage) {
//TODO add version check
return dockerImage != null && dockerImage.contains("enterprise");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.playtika.testcontainer.enterpise.aerospike;

import com.playtika.testcontainer.aerospike.AerospikeProperties;
import com.playtika.testcontainer.aerospike.EmbeddedAerospikeBootstrapConfiguration;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.testcontainers.containers.Container;
import org.testcontainers.containers.GenericContainer;

import java.io.IOException;

@Slf4j
@AutoConfiguration(after = EmbeddedAerospikeBootstrapConfiguration.class)
@ConditionalOnExpression("${embedded.containers.enabled:true}")
@ConditionalOnProperty(value = "embedded.aerospike.enabled", matchIfMissing = true)
public class SetupEnterpriseAerospikeBootstrapConfiguration {

private GenericContainer<?> aerospikeContainer;
private AerospikeProperties aerospikeProperties;

@Autowired
@Qualifier(AerospikeProperties.BEAN_NAME_AEROSPIKE)
public void setAerospikeContainer(GenericContainer<?> aerospikeContainer) {
this.aerospikeContainer = aerospikeContainer;
}

@Autowired
public void setAerospikeProperties(AerospikeProperties aerospikeProperties) {
this.aerospikeProperties = aerospikeProperties;
}

@PostConstruct
public void setupEnterpriseAerospike() throws IOException, InterruptedException {
log.info("Setting up disallow-expunge to true");
String namespace = aerospikeProperties.getNamespace();
Container.ExecResult result = aerospikeContainer.execInContainer("asadm", "-e",
String.format("enable; manage config namespace %s param disallow-expunge to true", namespace));
log.info("Set up disallow-expunge to true: {}", result.getStdout());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.playtika.testcontainer.enterpise.aerospike.InjectEnterpriseAerospikeBootstrapConfiguration,\
com.playtika.testcontainer.enterpise.aerospike.SetupEnterpriseAerospikeBootstrapConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.playtika.testcontainer.enterpise.aerospike.EnterpriseAerospikeTestOperationsAutoConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.playtika.testcontainer.enterpise.aerospike;

import com.aerospike.client.AerospikeClient;
import com.aerospike.client.policy.ClientPolicy;
import com.aerospike.client.policy.WritePolicy;
import com.playtika.testcontainer.aerospike.AerospikeTestOperations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@SpringBootTest(
classes = BaseEnterpriseAerospikeTest.TestConfiguration.class
)
public abstract class BaseEnterpriseAerospikeTest {

protected static final String SET = "values";

@Value("${embedded.aerospike.namespace}")
protected String namespace;

@Autowired
ConfigurableListableBeanFactory beanFactory;

@Autowired
AerospikeClient client;

@Autowired
WritePolicy writePolicyWithDurableDelete;

@Autowired
WritePolicy writePolicyWithoutDurableDelete;

@Autowired
AerospikeTestOperations aerospikeTestOperations;

@EnableAutoConfiguration
@Configuration
static class TestConfiguration {

@Primary
@Bean(destroyMethod = "close")
public AerospikeClient aerospikeClient(@Value("${embedded.aerospike.host}") String host,
@Value("${embedded.aerospike.port}") int port) {
ClientPolicy clientPolicy = new ClientPolicy();
clientPolicy.timeout = 10_000;//in millis
return new AerospikeClient(clientPolicy, host, port);
}

@Bean
public WritePolicy writePolicyWithDurableDelete() {
WritePolicy policy = new WritePolicy();
policy.durableDelete = true;
policy.totalTimeout = 200;//in millis
return policy;
}

@Bean
public WritePolicy writePolicyWithoutDurableDelete() {
WritePolicy policy = new WritePolicy();
policy.totalTimeout = 200;//in millis
return policy;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.playtika.testcontainer.enterpise.aerospike;

import com.aerospike.client.AerospikeException;
import com.aerospike.client.Bin;
import com.aerospike.client.Key;
import com.aerospike.client.Record;
import com.aerospike.client.ResultCode;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

public class EnterpriseAerospikeBootstrapConfigurationTest extends BaseEnterpriseAerospikeTest {

@Test
public void shouldDeleteWithDurableDeleteFlag() {
Key key = new Key(namespace, SET, "key1");
Bin bin = new Bin("mybin", "myvalue");
client.put(writePolicyWithDurableDelete, key, bin);

Record actualRecord = client.get(writePolicyWithDurableDelete, key);

assertThat(actualRecord.bins).hasSize(1);
assertThat(actualRecord.bins.get("mybin")).isEqualTo("myvalue");

client.delete(writePolicyWithDurableDelete, key);

actualRecord = client.get(writePolicyWithDurableDelete, key);
assertThat(actualRecord).isNull();
}

@Test
public void shouldNotDeleteWithoutDurableDeleteFlag() {
Key key = new Key(namespace, SET, "key2");
Bin bin = new Bin("mybin", "myvalue");
client.put(writePolicyWithDurableDelete, key, bin);

Record actualRecord = client.get(writePolicyWithoutDurableDelete, key);

assertThat(actualRecord.bins).hasSize(1);
assertThat(actualRecord.bins.get("mybin")).isEqualTo("myvalue");

assertThatExceptionOfType(AerospikeException.class)
.isThrownBy(() -> client.delete(writePolicyWithoutDurableDelete, key))
.extracting(AerospikeException::getResultCode)
.isEqualTo(ResultCode.FAIL_FORBIDDEN);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Language;
import com.aerospike.client.Value;
import com.aerospike.client.policy.WritePolicy;
import com.aerospike.client.query.Statement;
import com.aerospike.client.task.ExecuteTask;
import com.aerospike.client.task.RegisterTask;
Expand All @@ -22,16 +23,22 @@ public class AerospikeExpiredDocumentsCleaner implements ExpiredDocumentsCleaner

private final AerospikeClient client;
private final String namespace;
private final boolean durableDelete;

public AerospikeExpiredDocumentsCleaner(AerospikeClient client, String namespace) {
public AerospikeExpiredDocumentsCleaner(AerospikeClient client, String namespace, boolean durableDelete) {
Assert.notNull(client, "Aerospike client can not be null");
Assert.notNull(namespace, "Namespace can not be null");
this.client = client;
this.namespace = namespace;
this.durableDelete = durableDelete;

registerUdf();
}

public AerospikeExpiredDocumentsCleaner(AerospikeClient client, String namespace) {
this(client, namespace, false);
}

private void registerUdf() {
ClassLoader classLoader = AerospikeExpiredDocumentsCleaner.class.getClassLoader();
RegisterTask registerTask = client.register(null, classLoader, RESOURCE_PATH, SERVER_PATH, Language.LUA);
Expand All @@ -51,8 +58,11 @@ public void cleanExpiredDocumentsBefore(long expireTimeMillis) {

Statement statement = new Statement();
statement.setNamespace(namespace);

ExecuteTask executeTask = client.execute(null, statement, PACKAGE_NAME, FUNC_NAME, value);
WritePolicy writePolicy = new WritePolicy(client.getWritePolicyDefault());
if (durableDelete) {
writePolicy.durableDelete = true;
}
ExecuteTask executeTask = client.execute(writePolicy, statement, PACKAGE_NAME, FUNC_NAME, value);
executeTask.waitTillComplete(SLEEP_INTERVAL, TIMEOUT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@ConfigurationProperties("embedded.aerospike")
public class AerospikeProperties extends CommonContainerProperties {

static final String BEAN_NAME_AEROSPIKE = "aerospike";
public static final String BEAN_NAME_AEROSPIKE = "aerospike";
static final String BEAN_NAME_AEROSPIKE_BEAN_NAME = "aerospikePackageProperties";

boolean enabled = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public PackageInstaller aerospikePackageInstaller(@Qualifier(BEAN_NAME_AEROSPIKE
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "embedded.aerospike.time-travel.enabled", havingValue = "true", matchIfMissing = true)
public ExpiredDocumentsCleaner expiredDocumentsCleaner(AerospikeClient client,
AerospikeProperties properties) {
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
<module>embedded-zookeeper</module>
<module>embedded-couchbase</module>
<module>embedded-wiremock</module>
<module>embedded-aerospike-enterprise</module>
</modules>

<properties>
Expand Down